网络编程:掌握TCP Socket和UDP Socket
IP地址:
两台计算机通信,双方都必须有IP地址。
IPV4地址有32位,由四个8位二进制组成,因为不好记所以我们把二进制转化为十进制,比如192.168.0.20,这称为点分十进制。
IPV6有128位,由8个16位的无符号整数组成,每个整数用4个十六进制数表示,这些数之间用冒号(:)分开。
IP地址的分类:
IP地址 = 网络地址 +主机地址
网络地址决定网络数
主机地址决定了一个网络中可以存在的计算机最大数量
A类IP地址:前八位表示网络地址,取值范围1-126
B类IP地址:前十六位表示网络地址,取值范围128-191
C类IP地址:前三组表示网络地址,取值范围192-223
D类IP地址:不分网络地址和主机地址, 用于多播(Multicast)。多播通信是一种将数据包发送到多个特定接收者的方式,而不是广播给网络上的所有设备。常用于视频会议、流媒体传输等应用。
特殊的IP地址:
0.0.0.0表示主机
127.0.0.1表示本机回环地址,通常用于本机ping此地址来检查TCP/IP协议安装是否正确
255.255.255.255表示当前子网
IP地址的配置和检测:
查看本机的IP地址:
ipconfig
测试网络是否通畅:
ping 目标IP地址
DNS域名解析(域名系统):
访问网站时,为什么输入网址而不是IP地址?
因为ip地址都是数字,人们输入的时候太枯燥,所以需要一个系统将一个名称映射为它的IP地址,DNS域名解析系统。
网络通信协议:
TCP/IP:五层:
首先应用层准备好要发送的数据,然后给了传输层。
传输层的主要作用就是为发送端和接收端提供可靠的连接服务,传输层将数据处理完后就给了网络层。
网络层的功能就是管理网络,其中一个核心的功能就是路径的选择(路由选择),从发送端到接收端有很多条路,网络层就负责管理下一步数据应该到哪个路由器。选择好了路径之后,数据就来到了数据链路层,
数据链路层就是负责将数据从一个路由器送到另一个路由器。然后就是物理层了,可以简单的理解,
物理层就是网线一类的最基础的设备。
TCP协议:
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议
例子:
发货之前工作人员首先得要确认一下路是不是通吧,比如现在是过年,物流全部停运不就是路不通吗,那还发什么货呀。要是没什么问题,物流全部正常运作就发货呗。手机到达小明家后,小明先拆开看看手机在运输途中有没有损坏,有损坏就联系客服处理,没问题就确认收货。再回到上面的定义中,面向连接指的是先建立连接再发送数据,也就是先确认路可以走得通再发货。可靠就是如果货物在运输过程中有损坏或者丢失就让京东重新发货,确保小明收到的手机是没有任何问题的。基于字节流的意思就是比如小明买了手机又买了手机配件,分开发货,几件物品不是在一个包裹里,一个一个发。在这个例子中,京东的工作人员和小明充当了TCP协议的角色,他们两个共同确保了货物的完整性。
总结:确定是通的,如果丢失就重新再发,数据是以字节流的方式传送的。
建立连接 接收数据 发送数据
三次握手四次挥手:
三次握手:
A:是B吗?我要跟你通信,听得到我说话吗?
B:可以通信,你听得到我说话吗?
A:我也听得到。
之所以挥手三次是为了防止丢包的链接被服务端等待
四次挥手:
A:我困了,先不聊了吧
B:还有几件事,说完我们就睡觉…… ……(说完之后)
B:好了,说完了,我挂线了
A:好,你挂吧
B挂断电话
A说完之后就直接睡觉了,不知道电话挂没挂断,在几分钟后听到手机没有声音传来了,即使不用睁眼也知道B挂断了
四次挥手如图:
之所以还需要最后的ack发送后还需要等待,是因为服务端需要收到后才可以断开连接,所有如果ack丢失的话服务端可以再给客户端发一次。
UDP协议:
面向无连接的
TCP和Socket:
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,通信之前要保证连接已建立。
通过Socket产生I/O流来进行网络通信。
客户端:
public class Socket01 {
public static void main(String[] args) {
Socket socket =null;
OutputStream os =null;
//客户端
try {
socket = new Socket("127.0.0.1",8888);
//创建输出流
os = socket.getOutputStream();
os.write("chenmengyu Hello".getBytes());
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务器端:
首先要运行接收端,不然发送端连接不上就不会发送数据
Socket s = ss.accept();会一直等待发送端的连接,死等
第一种:循环的接收
不需要字符串了,但是缺点是只能够传输字母,不能传中文
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream inputStream =null;
try {
//创建serverSocket
serverSocket = new ServerSocket(8888);
//监听客户端获取socket对象
socket = serverSocket.accept();
//获取输出流
inputStream = socket.getInputStream();
int read;
while ((read=inputStream.read())!=-1){
System.out.print((char)read);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
serverSocket.close();
socket.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
第二种数组接收:
byte b[] = new byte[100];
String str = new String(b);
用byte数组接收再转成字符串输出
public class Socket02 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream inputStream =null;
try {
//创建serverSocket
serverSocket = new ServerSocket(8888);
//监听客户端获取socket对象
socket = serverSocket.accept();
//获取输出流
inputStream = socket.getInputStream();
byte b[] = new byte[100];
inputStream.read(b);
String str = new String(b);
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
serverSocket.close();
socket.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
TCP传输序列化对象:
客户端:
public class ObjSocket01 {
public static void main(String[] args) {
//客户端
Socket socket = null;
OutputStream os = null;
ObjectOutputStream objectOutputStream = null;
try {
socket = new Socket("127.0.0.1",8888);
os = socket.getOutputStream();
objectOutputStream = new ObjectOutputStream(os);
objectOutputStream.writeObject(new Student("陈梦雨",21));
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
socket.close();
os.close();
objectOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
服务器端:
readObject()
方法的作用是从输入流中读取一个序列化的对象,并将其转换为Java对象。它会返回一个Object
类型,通常需要进行类型转换
public class ObjSocket02 {
public static void main(String[] args) {
ServerSocket socket = null;
InputStream inputStream = null;
ObjectInputStream ois = null;
try {
socket = new ServerSocket(8888);
//监听客户端
Socket socket1 = socket.accept();
//获取输入流
inputStream= socket1.getInputStream();
//创建一个反序列化对象,并且把输入流当参数放进去
ois = new ObjectInputStream(inputStream);
//readObject()方法则从流中读取对象并将其转换为Student类型的实例
Student stu= (Student)ois.readObject();
System.out.println(stu.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
例题:多客户端用户登录:
TCP的Socket和多线程联动
需要创建子线程的是服务端,它创建多个子线程去处理客户端发送的东西
不使用多线程:
就是用while(true)使服务端一直等一直监听
服务器端:
package QuestionPpt02;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class demo01 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
//服务器端
try {
serverSocket = new ServerSocket(8888);
socket = new Socket();
while(true){
//监听
socket = serverSocket.accept();
InputStream is = socket.getInputStream();
byte b[] = new byte[50];
is.read(b);
String msg = new String(b);
System.out.println(msg);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
客户端:
public class demoCelint01 {
public static void main(String[] args){
//客户端1
System.out.println("客户端01————————————");
try {
Socket socket = new Socket("127.0.0.1",8888);
OutputStream os = socket.getOutputStream();
os.write("用户名:张三".getBytes());
os.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
使用多线程:
它的客户端和上面一样
服务端主线程:
通过构造方法传入监听过来的socket对象,然后进入子线程
package QuestionPpt02;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class demo02 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
//服务器端
try {
serverSocket = new ServerSocket(8888);
socket = new Socket();
while(true){
//监听
socket = serverSocket.accept();
//一旦有用户来就创建一个子线程
ThreadTest threadTest = new ThreadTest(socket);
threadTest.start();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
服务端子线程:
package QuestionPpt02;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class ThreadTest extends Thread{
private Socket socket;
public ThreadTest(Socket socket){
this.socket = socket;
}
//服务端创建子线程
@Override
public void run(){
try {
InputStream is = null;
is = socket.getInputStream();
byte b[] = new byte[50];
is.read(b);
String msg = new String(b);
System.out.println(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
UDP和socket:
DatagramSocket:用来发送和接收数据包
DatagramPacket:用来装包和拆包
dp.getData()拆包
ds.send(dp);发送包
基于UDP协议的sokect实现
发送端(客户端):
DatagramPacket的构造方法最常用的,接收(发送内容的比特数组,数组长度,地址对象,端口号)
InetAddress.getByName("127.0.0.1")是一个地址对象,反正这里不能直接填地址
public class LoginCelint {
//客户端
public static void main(String[] args) {
System.out.println("我是客户端---------------");
//发送信息
Scanner scanner = new Scanner(System.in);
DatagramPacket dp = null;
DatagramSocket ds = null;
try {
System.out.print("客户端请输入:");
String msg = scanner.nextLine();
dp = new DatagramPacket(msg.getBytes(),
msg.getBytes().length,
InetAddress.getByName("127.0.0.1"),
8888);
ds = new DatagramSocket();
ds.send(dp);
} catch (Exception e) {
e.printStackTrace();
}finally {
ds.close();
}
}
}
接收端(服务器):
- 一定要先运行接收方, 在 UDP 通信中,接收方需要在指定的端口上监听和等待数据包。
DatagramPacket的构造方法(byte数组,数组长度)
- String msg = new String(dp.getData());先 获取
DatagramPacket
中存储的数据的字节数组。再转成String
public class LoginServer {
//服务器
public static void main(String[] args) {
System.out.println("我是服务端——————————");
Scanner scanner = new Scanner(System.in);
DatagramPacket dp = null;
DatagramSocket ds = null;
try {
ds = new DatagramSocket(8888);
byte b[] = new byte[100];
dp = new DatagramPacket(b, b.length);
//等待接收
ds.receive(dp);
//拆包
String msg = new String(dp.getData());
System.out.println("客户端对我说:" + msg);
} catch (Exception e) {
e.printStackTrace();
}finally {
ds.close();
}
}
}
原文地址:https://blog.csdn.net/qq_62859013/article/details/142361097
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!