一、UDP特点:
- 无连接不可靠:通信双方不事先建立连接,直接发送数据。
- 数据封装:将数据封装在64KB的数据包中,包含接收端的IP和端口。
- UDP通信模型:
- 模型比喻:以抛韭菜为例,发送端像抛韭菜的人,接收端像接韭菜的人,数据如韭菜,数据包如韭菜盘子。
- 模型作用:帮助理解UDP通信过程,后续代码编写将严格按照此模型进行。
二、 UDP客户端代码实现:
- 创建发送端对象:使用
DatagramSocket
类创建,系统会默认分配端口。 - 创建数据包对象:使用
DatagramPacket
类,封装要发送的数据,需将数据转成字节数组,指定发送长度、目的地IP和端口。 - 发送数据包:调用
DatagramSocket
对象的send
方法。 - UDP服务端代码实现:
- 创建接收端对象:使用
DatagramSocket
类,需注册端口,与客户端指定的端口匹配。 - 创建数据包对象:使用
DatagramPacket
类,准备字节数组接收数据,大小最好设置为64KB。 - 接收数据:调用
DatagramSocket
对象的receive
方法,将数据封装到数据包对象的字节数组中。 - 处理数据:获取收到的数据长度,可通过数据包对象获取对方的IP和端口。
- 创建接收端对象:使用
- 代码测试与注意事项:
- 启动顺序:先启动服务端,再启动客户端。
- 资源关闭:客户端发完数据后应关闭通信管道,服务端不应关闭。
- UDP多发多收实现:
- 客户端改造:使用
while
死循环,让用户不断输入数据,封装成数据包发送,输入exit
退出。 - 服务端改造:使用
while
死循环,不断用数据包接收数据。
- 客户端改造:使用
- 服务端多客户端接收原理:
- 接收原理:服务端只负责接收流向本机和指定端口的数据包,不管数据来自哪个客户端,所以可以同时接收多个客户端的消息。
- 多开客户端:在IDE中配置允许多开实例,可同时启动多个客户端。
三、TCP通信基础 :
- 特点回顾 :TCP通信是面向连接的可靠通信,采用三次握手建立全双工可靠连接,能实现端到端通信,服务端确认接收后数据才被认为发送成功。
- 实现方式 :在Java中使用Socket类实现TCP通信,基于IO流进行数据传输。
- 通信模型 :通信分客户端和服务端,需建立Socket端到端的通信管道,通过字节输入流和输出流在管道中收发数据。
- 客户端开发 :
- 创建管道 :创建Socket对象请求与服务端连接,需填写服务器IP和端口,如使用本地IP可填127.0.0.1,端口可设为9999。
- 发送消息 :从Socket管道获取字节输出流,可将其包装成打印流、缓冲字节输入流或特殊数据流(DataOutputStream)发送数据,注意流的对应匹配。
- 资源释放 :实际开发中一般用户点退出时才关闭管道,一发一收场景可关闭。
- 服务端开发 :
- 创建对象 :使用ServerSocket类创建对象并注册端口,等待客户端连接,调用accept方法返回服务端的Socket对象。
- 接收数据 :从Socket管道获取字节输入流,包装成特殊数据输入流(DataInputStream)接收客户端数据,先接收整数,再接收消息。
- 获取信息 :可通过Socket管道获取客户端的IP和端口信息。
四、 测试与运行 :
- 启动顺序 :先启动服务端,服务端在accept方法处阻塞等待客户端连接,客户端启动后双方建立通信管道。
- 速度差异 :服务端和客户端执行速度快慢不影响数据接收,数据会缓存到管道中,确保对方收到。
- 多发多收实现 :
- 客户端改造 :使用死循环让用户反复输入消息,通过Scanner获取用户输入,发exit时关闭管道并退出循环。
- 服务端改造 :将接收消息的逻辑放入死循环,只接收文本消息,服务端不能关闭管道。
- 多客户端支持 :
- 单线程问题 :当前服务端单线程只能处理一个客户端消息,加死循环也无法解决。
- 多线程引入 :主线程负责接收客户端连接,每接到一个管道交给独立子线程处理消息,定义线程类(如ServerReader)重写run方法读取管道消息。
- 上下线追踪 :服务端可通过接收管道知道客户端上线,客户端异常退出或关闭程序时服务端捕获异常追踪下线逻辑,可获取客户端IP。
五、BS架构原理 :
- 架构特点 :CS架构需开发客户端和服务端,BS架构客户端为浏览器无需开发,服务端需开发响应网页。
- 请求与响应 :浏览器使用HTTP协议通过IP和端口请求服务端,服务端响应网页数据需遵循HTTP协议规定的格式,包括协议版本、头部字段、空行等。
- 代码开发 :复制之前的服务端代码进行修改,端口设为8080方便浏览器识别,使用打印流响应网页数据,响应完关闭管道。
- 线程池优化 :
- 优化原因 :每次请求开一个线程处理网站请求不合适,线程工作时间短,创建和销毁大量线程开销大,线程池适合处理网站请求。
- 实现方法 :将Socket管道包装成任务对象(实现Runnable接口)交给线程池处理,创建线程池时设置核心线程数、最大线程数、任务队列等参数。