网络编程
一、概念
1.1 网络
-
网络
主机H1和H2之间通信需要中间很多其他的设备来完成,并且需要通过许多的网络才能到达H2所在的局域网
1.2 IP地址
主机A向主机B发送信息的时候,我们首先应该确认的就是主机B在哪里
- IP地址的表示形式
对于IPV4来说,需要四个字节,一个字节有8位,所以四个字节有32位,每个字节表示的范围是0~255
1.2.1 IPv4 介绍
根据IP地址的组成=网络地址+主机地址,对IPv4进行分类
-
IP地址分类
A类表示的主机要多一点,因为有24位主机号,可以表示主机 0~(2^24)-1个主机
B类用16位也就是2个字节来表示网络号,并且前面两位固定10;也用2个字节来表示主机号
C类用24位也就是3个字节表示网络号,并且前面三位固定号码110;主机号用8位也就是1个字节来表示,可以表示0~255个主机
D类多播组号类似广播组号
E类保留起来后面使用
1.2.2 IPv6 介绍
-
IPv6
IPv6使用128位标识一个地址,一共有16个字节(128/8=16,一个字节有8位),相当于是Ipv4的4倍(IPv4有4个字节)
IPv6是互联网工程任务组设计的用于替代IPv4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址
由于IPv4最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展。IPv6的使用,不仅能解决网络地址数量的问题,而且也能解决多种接入设备连入互联网的障碍
当时IPv4的设计者考虑的是将来我们连接到网络的设备都是主机/电脑,而且设计者还认为主机/电脑不会有太多,所以4个字节就够用了
但是随着物联网的发展,不仅主机可以连接网络,一个基本的网络设备(比如手机/相机等)都可以连接到网络上,那此时我们就需要更多的能够唯一标识设备的IP地址,于是乎提出了IPv6的概念
-
IPv6的表示方法
IPv6使用128位标识一个地址,一共有16个字节
128/8=16,一个字节有8位
IPv6有3种表示方法
-
冒分十六进制表示法
格式为X:X:X:X:X:X:X:X,其中每个X表示地址中的16b,以十六进制表示,例如:
ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
这种表示法中,每个X的前导0是可以省略的,例如:
2001:0DB8:0000:0023:0008:0800:200C:417A → 2001:DB8:0:23:8:800:200C:417A
-
0位压缩表示法
在某些情况下,一个IPv6地址中间可能包含很长的一段0,可以把连续的一段0压缩为“::”。但为保证地址解析的唯一性,地址中”::”只能出现一次,例如:
FF01:0:0:0:0:0:0:1101 → FF01::1101
0:0:0:0:0:0:0:1 → ::1
0:0:0:0:0:0:0:0 → ::
-
内嵌IPv4地址表示法
为了实现IPv4-IPv6互通,IPv4地址会嵌入IPv6地址中,此时地址常表示为:X:X:X:X:X:X:d.d.d.d,前96b采用冒分十六进制表示,而最后32b地址则使用IPv4的点分十进制表示
例如::192.168.0.1与::FFFF:192.168.0.1就是两个典型的例子,注意在前96b中,压缩0位的方法依旧适用
-
1.2.3 查看IP地址
ipconfig
我使用的WIFI,所以查看一下“无线局域网适配器 WLAN”
-
查看Internet 协议版本4(TCP/IPv4)属性,并且我们发现IP地址是自动获得的
这说明我们每次连接这个网络的话,都会自动给我们分配一个IP地址,并且每一次分配的IP是不一样的,由我们底层的一个协议分配的IP地址
什么时候我们的IP是固定的?
1.3 域名和端口
-
为了方便记忆,解决记ip的困难,比如www.baidu.com
-
表示形式:以整数形式,范围0~65535
为什么是0~65535?
0~1024已经被占用,比如ssh 22,ftp 21,smtp 25,http 80
两个不同的服务监听同一个端口会出问题。比如说网站服务和邮件服务都监听80端口,那我们访问ip+80端口具体访问的是网站服务还是邮件服务呢?
如果一个端口被监听/占用,其他服务是不能再去监听这个端口的,会报错“端口被占用”
1.4 网络协议
-
协议(TCP/IP)
中文名传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是Internet最基本的协议、Internet国际互联网络的基础
- 对方机器拿到后开始解包。先去掉“以太网首部”和”以太网尾部”(去帧),就是下图中自下而上的过程,首先得到的数据是”IP、TCP、 应用数据”
- 再经过张三电脑的”IP”层,将“IP首部去除掉”,进一步拿到了“TCP、应用数据”
- 再经过“TCP”层,我们可以获取到”App头和用户数据”
- 那电脑怎么知道把数据给哪个程序呢?电脑知道App头之后就可以知道将用户数据送到哪个应用程序里面
类似一个打包和拆包的过程
-
网络通信协议
TCP/IP协议有两个模型
一种是OSI模型,这种模型是一种理论性的东西,理论模型,现实的计算机里面并没有这个模型。因为在实际应用中这几层的意义分的太细
对OSI模型做了一个简化,就有了TCP/IP模型
传输层就是刚刚分析的TCP的那一层,数据经过TCP会打一个TCP的那一个包
网络层就是刚刚分析的IP层
传输层和网络层是最重要的
这一层主要是加帧头和帧尾的
1.5 TCP与UDP
TCP与UDP是网络协议中TCP/IP模型的传输层中的重要协议
- TCP协议
三次握手之后,客户端client就会给服务端server发送大量的数据,因为客户端client认为三次握手结束以后就可以确认服务端server一定可以接收到我们后面发送的数据
-
在连接中可进行大数据量的传输
-
传输完毕,须释放已经建立的连接,效率低
不释放连接有什么后果?
- UDP协议
1.6 InetAddress类
重要方法
-
获取本机InetAddress对象 getLocalHost
//TODO 1.获取本机的InetAddress对象 InetAddress localHost = InetAddress.getLocalHost(); //会输出本机的主机名 、IP地址 zhangjingqi-PC/192.168.101.1 System.out.println(localHost);
-
//TODO 2.根据指定主机名获取InetAddress InetAddress byName = InetAddress.getByName("zhangjingqi-PC"); System.out.println(byName);//zhangjingqi-PC/192.168.101.1
// TODO 3.根据指定域名获取ip地址对象
InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
// www.baidu.com/110.242.68.4
System.out.println(inetAddress);
-
获取InetAddress对象的主机名 getHostName
//TODO 4.通过InetAddress对象,获取对应的主机名 String hostName = inetAddress.getHostName(); //www.baidu.com System.out.println(hostName);
-
获取InetAddress对象的地址 getHostAddress
//TODO 5.通过InetAddress对象,获取对应的主机地址 String hostAddress = inetAddress.getHostAddress(); //110.242.68.3 System.out.println(hostAddress);
1.7 Socket
Socket有两种编程方式,TCP网络通信编程和UDP网络通信编程
- Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输
当我们需要通讯(读写数据)时,可以使用socket.getOutputStream()获得输出流,可以使用socket.getInputStream()获得输入流
服务器端主机的socket可以使用输入流从数据通道中读取数据
二、TCP网络通信编程
2.1 介绍
连接之后,客户端Client就可以得到一个输出流,就会把数据发送给服务端Server
服务端Server要想知道客户端Client发送的什么内容,需要先获取输入流,利用输入流从数据通道中读取数据
最后执行完成后一定要关闭Socket,因为太多Client连接Server不关闭之后,后续的客户端Client就会连接不上服务端Server。简单说就是Socket的连接数是有限的
2.2 案例
2.2.1 字节流编程案例1
应用案例
ServerSocket和Socket有什么区别?
ServerSocket可以对应创建很多Socket,只要有一个accept就会返回一个socket,可以处理多个客户端连接服务器时的并发问题
服务端代码
/**
* 服务端
*/
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//TODO 1.在本机的9999端口监听,等待连接
//一定要确认9999端口没有被其他服务在监听或占用
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在9999端口监听,等待连接....");
//TODO 2.当没有客户端连接9999端口时,程序会堵塞,等待连接
//如果有客户端连接,则会返回一个socket对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("服务器端 socket=" + socket.getClass());
//TODO 3.通过socket.getInputStream()读取客户端写入到数据端通道的数据
InputStream inputStream = socket.getInputStream();
//缓冲
byte[] bytes = new byte[512 * 2];
int readCount = 0;
while ((readCount = inputStream.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, readCount));
}
//TODO 4.关闭流和socket
inputStream.close();
socket.close();
serverSocket.close();//多了一个这个
}
}
客户端代码
public class SocketTCP01Client {
public static void main(String[] args) throws IOException {
//TODO 1.连接服务端(ip,端口)
//TODO 2.连接上后,生成Socket
//连接本机9999端口,如果连接成功的话会返回一个socket
Socket socket = new Socket(InetAddress.getLocalHost(),9999);
//Socket socket = new Socket("127.0.0.1",9999);
System.out.println("客户端 socket返回="+socket.getClass());
//TODO 3.得到和socket关联的socket.getOutputStream输出流对象
OutputStream outputStream = socket.getOutputStream();
//TODO 4.通过输出流,写入数据到数据通道
outputStream.write("hello,server".getBytes());
outputStream.fulsh;
//TODO 关闭流对象和socket对象,避免资源浪费
outputStream.close();
socket.close();
System.out.println("客户端退出了");
}
}
2.2.2 字节流编程案例2
-
编写一个服务器端,和一个客户端
-
服务器端在 9999端口监听
在下面编程的时候可能遇到这么一种情况
原因
我们客户端”hello,server”消息发送,服务端接收到信息之后,服务端并不知道什么时候代表结束
同样的道理,我们服务端给客户端回传“hello,client“,客户端读取之后,也并不知道什么时候代表结束,也就是客户端并不知道服务端有没有说完,服务端发送消息的时候没有一个结束标记
所以我们应该在客户端发送完消息后写一个结束标记,表示客户端已经说完了,服务端就不用再等着读取了
socket.shutdownOutput();
服务端代码
public class SocketTCP02Server {
public static void main(String[] args) throws IOException {
//TODO 1.在本机的9999端口监听,等待连接
//一定要确认9999端口没有被其他服务在监听或占用
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在9999端口监听,等待连接....");
//TODO 2.当没有客户端连接9999端口时,程序会堵塞,等待连接
//如果有客户端连接,则会返回一个socket对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("服务器端 socket=" + socket.getClass());
//TODO 3.通过socket.getInputStream()读取客户端写入到数据端通道的数据
InputStream inputStream = socket.getInputStream();
//缓冲
byte[] bytes = new byte[512 * 2];
int readCount = 0;
while ((readCount = inputStream.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, readCount));
}
//TODO 4.获取socket相关联输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,client".getBytes());
outputStream.flush();
//设置结束标记
socket.shutdownOutput();
//TODO 5.关闭流和socket
inputStream.close();//先打开的流后关闭
outputStream.close();
socket.close();
serverSocket.close();//多了一个这个
}
}
客户端代码
public class SocketTCP02Client {
public static void main(String[] args) throws IOException {
//TODO 1.连接服务端(ip,端口)
//TODO 2.连接上后,生成Socket
//连接本机9999端口,如果连接成功的话会返回一个socket
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
//Socket socket = new Socket("127.0.0.1",9999);
System.out.println("客户端 socket返回=" + socket.getClass());
//TODO 3.得到和socket关联的socket.getOutputStream输出流对象
OutputStream outputStream = socket.getOutputStream();
//TODO 4.通过输出流,写入数据到数据通道
outputStream.write("hello,server".getBytes());
outputStream.flush();
//设置结束标记
socket.shutdownOutput();
//TODO 5.获取输入流,读取数据
InputStream inputStream = socket.getInputStream();
//缓冲
byte[] bytes = new byte[512 * 2];
int readCount = 0;
while ((readCount = inputStream.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, readCount));
}
//TODO 6.关闭流对象和socket对象,避免资源浪费
outputStream.close();//先打开的流后关闭
inputStream.close();
socket.close();
System.out.println("客户端退出了");
}
}
2.2.3 字符流编程案例1
在网络通信时使用文本的形式更方便一点
- 编写一个服务端和一个客户端
- 服务端在9999端口监听
- 客户端连接到服务端,发送“hello,server”,并接收服务端返回的“hello,client”,再退出
- 服务端接收到客户端发送的信息,输出,并发送“hello,client”
设置结束标记
- 方式1
socket.shutdownOutput();
writer.newLine();//一个换行符
server服务端
public class SocketTCP03Server {
public static void main(String[] args) throws IOException {
//TODO 1.在本机的9999端口监听,等待连接
//一定要确认9999端口没有被其他服务在监听或占用
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在9999端口监听,等待连接....");
//TODO 2.当没有客户端连接9999端口时,程序会堵塞,等待连接
//如果有客户端连接,则会返回一个socket对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("服务器端 socket=" + socket.getClass());
//TODO 3.获取socket.getInputStream()字节流,并利用转换流将其转换成字符流
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//TODO 4.读取客户端发送过来的数据
//一次性最多读取1K的内容,因为char类型占用2个字节,一共有512*2=1024个byte,也就是1k
char[] chars = new char[512];
int readCount = 0;
while ((readCount = bufferedReader.read(chars)) != -1) {
System.out.println(new String(chars, 0, readCount));
}
//TODO 5.获取socket相关联输出流,并利用转换流将其转换成字符流
OutputStream outputStream = socket.getOutputStream();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
//BufferedWriter是字符缓冲流
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
//TODO 6.通过输出流,写入数据到数据通道
bufferedWriter.write("hello,client,字符流");
bufferedWriter.flush();
//设置结束标记
socket.shutdownOutput();
//TODO 7.关闭流和socket
bufferedReader.close();//关闭外层流即可,先打开的流后关闭
bufferedWriter.close();
socket.close();
serverSocket.close();//多了一个这个
}
}
client客户端
public class SocketTCP03Client {
public static void main(String[] args) throws IOException {
//TODO 1.连接服务端(ip,端口)
//TODO 2.连接上后,生成Socket
//连接本机9999端口,如果连接成功的话会返回一个socket
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
//Socket socket = new Socket("127.0.0.1",9999);
System.out.println("客户端 socket返回=" + socket.getClass());
//TODO 3.得到和socket关联的socket.getOutputStream输出流对象
OutputStream outputStream = socket.getOutputStream();
//TODO 4.利用转换流将字节流转换成字符流
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
//BufferedWriter是字符缓冲流
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
//TODO 5.通过输出流,写入数据到数据通道
bufferedWriter.write("hello,server,字符流");
bufferedWriter.flush();//如果使用的字符流,需要手动刷新一下,否则数据不会写入通道中
//设置结束标记
socket.shutdownOutput();
//TODO 5.获取输入流,并将字节输入流转换成字符输入流
//InputStream是字节流,InputStreamReader是字符流,BufferedReader是字符缓冲流
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//TODO 6.读取客户端发送过来的数据
//一次性最多读取1K的内容,因为char类型占用2个字节,一共有512*2=1024个byte,也就是1k
char[] chars = new char[512]; int readCount = 0;
while ((readCount = bufferedReader.read(chars)) != -1) {
System.out.println(new String(chars, 0, readCount));
}
//TODO 7.关闭流对象和socket对象,避免资源浪费
bufferedWriter.close(); //关闭外层流即可,先打开的流后关闭
bufferedReader.close();
socket.close();
serverSocket.close();//多了一个这个
System.out.println("客户端退出了");
}
}
2.3 网络文件上传
2.3.1 网络文件上传案例
- 编写一个服务端,一个客户端
- 服务端在9999端口监听
- 客户端连接到服务端,发送一张图片
- 服务器端接收到客户端发送的图片保存到src下,发送“收到图片”再退出
- 客户端接收到服务端发送的“收到图片”,再退出
简单来说是将客户端的图片,通过网络拷贝到服务器,并且服务器要给客户端回复一条消息
说明:使用BufferedInputStream和BufferedOutputStream字节流
示意图
服务端
/**
* 文件上传的服务端
*/
public class TCPFileUploadServer {
public static void main(String[] args) throws IOException {
//TODO 1.服务端在本机监听8888端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务端在8888端口监听....");
//TODO 2.等待连接
Socket socket = serverSocket.accept();
//TODO 3.读取客户端发送的数据
//通过socket得到输入流
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
//读取客户端传输过来的二进制数据数组
byte[] fileBytesArray = StreamUtils.streamToByteArray(bis);
//TODO 4.将得到的bytes数组写入到指定的路径,就会得到一个文件
String filePath = "src/qie2.jpg";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
bos.write(fileBytesArray);
bos.flush();
//TODO 5.服务端向客户端发送“收到图片”再退出
//使用字符缓冲流BufferedWriter,OutputStreamWriter是转换流,将字节流转换成字符流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("收到图片");
bw.flush();
//写出结束标记
socket.shutdownOutput();
//TODO 6.关闭流
bw.close();
bos.close();
bis.close();
socket.close();
serverSocket.close();
}
}
客户端
/**
* 文件上传的客户端
*/
public class TCPFileUploadClient {
public static void main(String[] args) throws IOException {
//TODO 1.客户端连接服务端8888,得到Socket对象
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
//TODO 2.把磁盘上的图片读取到文件字节数组中
//2.1 创建读取磁盘文件的输入流(字节流)
String filePath = "C:\Users\jd\Desktop\jpg1.jpg";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
//fileBytesArray数组就是filePath所对应的字节数组
byte[] fileBytesArray = StreamUtils.streamToByteArray(bis);
//TODO 3.利用IO流将字节数组放入Socket通道中,使服务端可以读取到(将二进制数组数据发送给客户端)
//借助socket获取字节流
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
// byte[] byteArray = new byte[1024];//一次性读取1024个byte,也就是1k,但是这里并不需要,直接写字节数组fileBytesArray即可
bos.write(fileBytesArray);
bos.flush();
//设置写入数据的结束标记
socket.shutdownOutput();
//TODO 4.读取客户端返回过来的数据
BufferedReader bw = new BufferedReader(new InputStreamReader(socket.getInputStream()));
char[] charArray = new char[512]; //一次性读取512个字符,一个字符是两个字节,也就是512*2=1024字节,1k
int len = 0;
StringBuilder stringBuilder = new StringBuilder();
while ( (len = bw.read(charArray))!=-1){
stringBuilder.append(new String(charArray, 0, len));
}
System.out.println(stringBuilder);
//或者使用工具类中的方法,都是可以的
// String resultFromServer = StreamUtils.streamToString(socket.getInputStream());
// System.out.println(resultFromServer);
//TODO 关闭流
bw.close();
bis.close();
bos.close();
socket.close();
}
}
工具类
/**
* IO流工具类
*/
public class StreamUtils {
/**
* 将输入流转换成byte[]
*/
public static byte[] streamToByteArray(InputStream is) throws IOException {
//1.创建输出流对象
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//2.创建字节数组,一次可以读取1024个字节,也就是1k
byte[] b = new byte[1024];
int len;
//每次最多读取b单位长度的数据
while ((len = is.read(b)) != -1) {
//把读取到的数据写入bos流
bos.write(b, 0, len);
}
bos.flush();
//3.循环读取,此时array就是我们读取的文件的所有二进制的内容(将文件一次性转成二进制)
byte[] array = bos.toByteArray();
bos.close();
//4.返回文件的二进制形式
return array;
}
/**
* 将输入流的数据直接转换成一个字符串
* InputStream字节流
*/
public static String streamToString(InputStream is) throws IOException {
//BufferedReader字符缓冲流
//InputStreamReader 转换流,可以将字节流转换成字符流
//InputStream字节流
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder = new StringBuilder();
String line;
//一次性读一行
while ( (line=reader.readLine())!=null){
builder.append(line+"rn");
}
reader.close();
return builder.toString();
}
}
2.4 netstat 指令
可以查看协议、本地地址、外部地址(有没有外部的连接连到本地)、状态(LISTENING正在监听、ESTABLISHED连接上了)
使用netstat也可以查看当前主机网络情况,只不过查询到的信息不够全
netstat
netstat -an
举例说明
客户端
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
服务端
服务端监听本机8888端口
ServerSocket serverSocket = new ServerSocket(8888);
目前有一个TCP协议的方式,有一个程序在本机的8888端口处于监听的状态(此时还没有连接来连到客户端)
假如客户端和服务端连接成功后,状态会变为“ESTABLISHED”
2.5 TCP网络通信编程说明
当我们的客户端和服务端连接成功之后,客户端的socket数据通道也会有一个端口,并且端口号是由TCP/IP协议随机分配的
也就是说客户端会有一个端口对应服务端进行通信
使用网络文件上传案例中的代码,将上传的文件由图片改成大一点的视频
服务端会监听8888端口
首先启动服务端,如下所示,并且没有外部地址对应本地8888端口
也就是说8888端口已经在监听,但是没有客户端连接过来
也就是说我们服务端的端口是60285,然后客户端的192.168.12.1:60285与服务端192.168.12.1:8888建立了一个数据通道(或者说是一个网络连接)
当在服务端输入netstat时才会出现下面标红的数据
当在客户端输入netstat命令时会出现上图表中数据的下一条数据
上面标红的数据紧挨这一条数据的原因:
当我们客户端与服务端的传输结束之后,客户端的60285端口就会被释放
总结:客户端也是通过一个端口来通讯的,但是客户端端口是不确定的,而服务端的端口是固定的
三、UDP网络通信编程(了解)
-
DatagramSocket(叫数据报套接字)类和DatagramPacket(叫数据报/数据包)类实现了基于UDP协议网络程序
两个核心对象类
-
UDP数据报通过数据报套接字DatagramSocket发送和接受,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达
-
DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接口端的IP地址和端口号
-
UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接
3.1 基本流程
-
建立发送端、接收端(现在没有服务端和客户端这个概念)
-
-
关闭DatagramSocket
3.2 案例
3.2.1 应用案例 1
需求说明
- 编写一个接收端A和一个发送端B
- 接收端A在9999端口等待接收数据receive
- 发送端B向接收端A发送数据“hello,明天吃火锅~”
- 接收端A接收到发送端B发送的数据,回复”好的,明天见“,再退出
- 发送端接收到回复的数据再退出
发送端
/**
* UDP发送端
*/
public class UDPSenderB {
public static void main(String[] args) throws IOException {
//TODO 1.创建DatagramSocket对象,准备发送和接受数据
DatagramSocket socket = new DatagramSocket(9998);
//TODO 2.将需要发送的数据封装到DatagramPacket对象中
byte[] bytes = "hello 明天吃火锅~".getBytes();
//参数1:要发送的数据
//参数2、3:发送哪一段的数据,0-bytes.length就是发送数组的全部数据
//参数4:主机,客户端和接收端不在一台电脑,InetAddress.getByName(IP)即可
//参数5:端口
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length,InetAddress.getByName("127.0.0.1"),9999);
//TODO 3.发送数据
socket.send(packet);
//TODO 4.接收 接收端 回复的消息
byte[] buf = new byte[1024*64];
DatagramPacket packetReturn = new DatagramPacket(buf,buf.length);
System.out.println("发送端B 等待接收数据....");
socket.receive(packetReturn);
int length = packetReturn.getLength();
//实际接收到的数据
byte[] data = packetReturn.getData();
String s = new String(data, 0, length);
System.out.println(s);
//TODO 关闭资源
socket.close();
}
}
接收端
/**
* UDP接收端
*/
public class UDPReceiverA {
public static void main(String[] args) throws IOException {
//TODO 1.创建一个DatagramSocket对象,准备在9999接收数据和发送数据
DatagramSocket socket = new DatagramSocket(9999);
//TODO 2.构建DatagramPacket对象,准备接收数据
//UDP协议最大的包64k,不适合传输大量的数据
byte[] buf = new byte[1024*64];
//参数1:存储将要发送或接收的数据
//参数2:指定要发送或接收的数据的长度
//此时packet对象是空的,是什么也没有的
DatagramPacket packet = new DatagramPacket(buf,buf.length);
//TODO 3.调用接收方法接收数据,将通过网络传输的DatagramPacket对象填充到packet对象里,此时packet对象便不是空对象了,是有数据的
//此方法表示会在9999端口等待,如果有一个数据报发送到了9999端口就会接收,没有发送到9999端口就会在此地方堵塞
System.out.println("接收端A 等待接收数据....");
socket.receive(packet);
//TODO 4.对packet拆包,取出数据并显示
//实际接收到的数据的长度,我们的byte数组大小是1024*64,但是不一定会有这么多的数据传输过来
int length = packet.getLength();
//实际接收到的数据
byte[] data = packet.getData();
String s = new String(data, 0, length);
System.out.println(s);
//TODO 5.给发送端回复消息
byte[] bytes = "好的,明天见".getBytes();
DatagramPacket packetReturn = new DatagramPacket(bytes,0,bytes.length, InetAddress.getByName("127.0.0.1"),9998);
socket.send(packetReturn);
//TODO 6.关闭资源
socket.close();
}
}
原文地址:https://blog.csdn.net/weixin_51351637/article/details/134713267
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_19041.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!