目录
网络编程基础
基本概念
发送端和接收端
请求和响应
客户端和服务端
常见的客户端服务端模型
Socket套接字
TCP/UDP特点
Java数据报套接字通信模型(UDP通信)
UDP数据报套接字编程
DatagramSocket
1.类定义
2.构造方法
3.核心方法
4.特性说明
DatagramPacket
1.类定义
2.构造方法
3.关键方法
4.数据处理
协作机制
1.发送端流程
2.接收端流程
创建回显服务器--客户端
网络编程基础
网络编程,是指网络上的主机,通过不同的进程,以编程的方式实现网络通信(数据交互)
当然,我们主要满足进程不同就行;所以即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程
特殊的,对于开发来说,在条件有限的情况下,一般也都是在一个主机中运行多个进程来完成网络编程
网络上的不同主机,基于网络来传输数据资源:
·进程A:编程来获取网络资源
·进程B:编程来提供网络资源
基本概念
发送端和接收端
在一次网络数据传输时:
发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机
接收端:数据的接受方进程,称为接收端。接受端主机即网络通信中的目的主机
收发端:发送端和接受端两端,也简称为收发端
注意:发送端和接受端是相对的。如下图:
请求和响应
一般来说,获取一个网络资源,涉及到两次网络数据传输
第一次:请求数据的发送
第二次:响应数据的发送
客户端和服务端
客户端:获取服务的一方进程,称为客户端
服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务
对于服务端来说,一般都是提供服务:
1.客户端获取服务资源(请求)--》服务端返回服务资源(响应)
2.客户端保存资源在服务端(请求服务端保存用户资源)--》服务端返回处理结果(响应)
常见的客户端服务端模型
最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:
1.客户端先发送请求到服务端
2.服务端根据请求数据,执行相应的业务处理
3.服务端返回响应,发送业务处理结果
4.客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)
Socket套接字
概念:Socket(套接字)是网络通信的核心编程接口,它是操作系统提供的、基于TCP/IP协议的网络通信的基本操作单元,用于实现进程间网络通信的底层工具。无论是Web服务器、即使通讯软件还是P2P应用,底层都依赖Socket实现数据传输。基于Socket套接字的网络程序开发就是网络编程。
socket在英文上有“插座”的意思,插座上有不同规格的插孔,我们将插头插入到对应的插孔当中就能实现电流的传输
在进行网络通信时,客户端就相当于插头,服务端就相当于一个插座,但服务端上可能会有多个不同的服务进程(多个插孔),因此当我们在访问服务端时,需要指明服务进程的端口号(对应规格的插孔),才能享受对应服务进程的服务
作用:Socket建立在TCP/IP协议之上,用于管理连接和传输数据。它允许在两台计算机之间通过网络进行数据交换
分类
Socket套接字主要针对传输层协议分为如下三类:
流套接字:使用传输层TCP协议
对于字节流来说,可以简单的理解为,传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收
数据报套接字:使用传输层UDP协议
对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1字节
原始套接字:用于自定义传输层协议,用于读写内核没有处理的IP协议数据(简单了解即可)
TCP/UDP特点
TCP:有连接、可靠传输、面向字节流、有接收缓冲区,也有发送缓冲区、大小不限
UDP:无连接、不可靠传输、面向数据报:有接受缓冲区,无发送缓冲区、大小受限(一次最多传输64k)
后续会详细讲解其中细节
Java数据报套接字通信模型(UDP通信)
对于UDP协议来说,具有无连接,面向数据报的特性,即每次都是没有建立连接,并且一次发送全部数据报,一次接收全部的数据报
java中使用UDP协议通信,主要基于DatagramSocket类来创建数据报套接字,并使用DatagramPacket作为发送或接收的UDP数据报。
在Java网络编程中,DatagramSokcet和DatagramPacket是UDP通信的核心搭档,它们的关系可以比喻为"邮局"和"信件":
1.DatagramSocket(邮局):
·是实际进行网络通信的端点,相当于一个能收发电报的邮局
·负责绑定端口、建立通信通道
·提供send()方法和receive()方法实现数据报传输
2.DatagramPacket(信件):
·是要传输的数据载体,相当于装有具体内容的信封
·包含:
·数据缓冲区(信件内容)
·目标地址和端口(发送时的收件人信息)
·源地址和端口(接收时的发件人信息)
3.协作关系:
·发送过程:
1.将数据放入DatagramPacket(写信)
2.通过DatagramSocket的send()方法投递(寄信)
·接收过程
1.准备空的DatagramPacket作为容器(准备空信封)
2.通过DatagramSocket的receive方法收取(收信)
3.从DatagramPacket中提取数据和来源信息(拆信阅读)
4.依赖关系:
·DatagramSocket必须依赖DatagramPacket来传输实际数据
·没有DatagramPacket,DatagramSocket就像没有信封的邮局,无法完成通信任务
这种分工使得UDP通信既灵活又高效:DatagramSocket处理网络通信细节,DatagramPacket专注数据封装,二者配合实现了无连接的数据报传输模式
对于一次发送及接收UDP数据报的流程如下:
以上只是一次发送端的UDP数据报发送,及接受端的数据报接收,并没有返回的数据。也就是只有请求,没有响应。
对于一个服务端来说,重要的是提供多个客户端的请求处理及响应,流程如下:
·
我们上述学习了UDP数据报的发送和接收的大致流程。现在我们学习如何用代码完成上述网络通信
UDP数据报套接字编程
我们在这里详细介绍DatagramSocket和DatagramPacket--UDP通信的核心搭档
DatagramSocket
1.类定义
public class DatagramSocket extends Object
表示用于发送和接收数据报的套接字,基于UDP协议实现
2.构造方法
1.DatagramSocket():创建绑定到本地任意可用端口的套接字
2.DatagramSocket(int port):创建绑定到指定本地端口的套接字
3.DatagramSocket(int port,InetAddress addr):创建绑定到指定端口和本地地址的套接字
3.核心方法
注意:在send和receive方法中,传参为DatagramPacket类型。即:封装的数据报(在后续的DatagramPacket会学习到)
4.特性说明
·无连接:通信前无需建立连接,直接发送数据报
·不可靠传输:不保证数据送达,可能丢包或乱序
·高效性:适合实时性要求高的场景(如视频流传输)
DatagramPacket
1.类定义
public final class DatagramPacket extends Object
表示数据报,用于封装要发送或接收的数据
2.构造方法
3.关键方法
这里的getData()方法获取到的数据报字节数组是包含数据的
4.数据处理
·发送端:需明确指定目标地址和端口
·接受端:通过getData()和getLength()提取有效数据
·缓冲区管理:接收时需重置缓冲区长度(setLength() )以准备接收新数据
协作机制
1.发送端流程
1.创建DatagramSocket实例
2.准备发送数据并转换为字节数组
3.创建DatagramPacket,指定目标地址和端口
4.调用socket.send(packet)发送数据
5.关闭套接字(可选,长期运行的服务器通常不关闭)
代码示例:
// 创建发送端套接字
DatagramSocket senderSocket = new DatagramSocket();// 准备数据
String message = "Hello UDP Server!";
byte[] data = message.getBytes();// 创建目标地址
InetAddress serverAddress = InetAddress.getByName("localhost");
int serverPort = 9876;// 构造数据包
DatagramPacket sendPacket = new DatagramPacket(data, data.length, serverAddress, serverPort
);// 发送数据
senderSocket.send(sendPacket);
2.接收端流程
1.创建DatagramSocket并绑定端口
2.创建DatagramPacket作为接收容器
3.循环调用socket.receive(packet)等待数据
4.解析数据报内容并处理
5.关闭套接字
代码示例:
// 创建接收端套接字
DatagramSocket receiverSocket = new DatagramSocket(9876);// 准备接收缓冲区
byte[] buffer = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);while(true) {// 接收数据(阻塞方法)receiverSocket.receive(receivePacket);// 解析数据String receivedData = new String(receivePacket.getData(), 0, receivePacket.getLength());// 获取发送方信息InetAddress clientAddress = receivePacket.getAddress();int clientPort = receivePacket.getPort();// 处理数据(示例:打印信息)System.out.println("Received from " + clientAddress.getHostAddress() + ":" + clientPort + " - " + receivedData);// 重置缓冲区receivePacket.setLength(buffer.length);
}
创建回显服务器--客户端
回显服务器--客户端:客户端发送什么请求,服务器便返回什么
回显服务器UdpEchoServer
public class UdpEchoServer {//回显服务器//创建UDP套接字private DatagramSocket socket=null;//构造方法public UdpEchoServer(int port) throws SocketException {socket=new DatagramSocket(port);}//启动服务器public void start() throws IOException {System.out.println("服务器启动");while(true){//循环一次,就相当于处理一次请求//处理请求的过程,典型的服务器都是分成三个步骤的//1.读取请求并解析//DatagramPacket表示一个UDP数据报。此处传入的字节数组,就保存了UDP的载荷部分(数据)DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);socket.receive(requestPacket);//用套接字socket接收数据报//把读取到的二进制数据转成字符串。String request=new String(requestPacket.getData(),0,requestPacket.getLength());//2.根据请求,计算响应//但是我们这里是回显服务器,这个环节相当于省略了String response=process(request);//3.把响应返回给客户端//根据response构造Data'gramPacket,发送给客户端//注意这里使用的是getBytes方法,传入字节数组以及其数组长度//还需要传递 指定的目的ip和目的端口--接收到的请求中包含DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//4.打印日志System.out.printf("[%s:%d]req:%s,resp:%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}//根据请求得到响应private String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server=new UdpEchoServer(9090);server.start();}
}
回显客户端UdpEchoClient
public class UdpEchoClient {//回显客户端--UDPprivate DatagramSocket socket=null;//UDP本身不保存对端的信息,我们这里在代码中可以自己手动保存private String serverIp;private int serverPort;public UdpEchoClient(String serverIp,int serverPort) throws SocketException {this.serverIp=serverIp;this.serverPort=serverPort;socket=new DatagramSocket();}public void start() throws IOException {Scanner sc=new Scanner(System.in);while(true){//1.从控制台读取用户输入的内容System.out.println("请输入要发送的内容:");if(!sc.hasNext()){//如果无输入,则结束循环break;}String request=sc.next();//2.把请求发送给服务器--->构造DatagramPacket对象//注意:构造过程中,不光需要载荷(数据),还需设置服务器的IP(目的ip)和端口(目的端口)DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(serverIp),serverPort);//3.发送数据报socket.send(requestPacket);//4.接收服务器的响应DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);//5.将从服务器读取的数据进行解析,并打印下来String response=new String(responsePacket.getData(),0,responsePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient client=new UdpEchoClient("127.0.01",9090);client.start();}
}
运行结果:
服务器:
客户端: