目录
Socket套接字
概念
Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。
分类
Socket套接字主要针对传输层协议划分为如下三类:
- 流套接字:使用传输层TCP协议
TCP,即Transmission Control Protocol(传输控制协议),传输层协议。
以下为TCP的特点:
- 有连接
- 可靠传输
- 面向字节流
- 有接收缓冲区,也有发送缓冲区
- 大小不限
对于字节流来说,可以简单的理解为,传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。
- 数据报套接字:使用传输层UDP协议
UDP,即User Datagram Protocol(用户数据报协议),传输层协议。
以下为UDP的特点:
- 无连接
- 不可靠传输
- 面向数据报
- 有接收缓冲区,无发送缓冲区
- 大小受限:一次最多传输64k
对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节。
- 原始套接字:原始套接字用于自定义传输层协议,用于读写内核没有处理的IP协议数据。简单了解即可。
UDP VS TCP
具体的网络编程
写一个应用程序,让这个程序可以网络通信,这里就需要调用传输层的api
传输层提供协议主要是两个:
- UDP;
- TCP。
两套不同的api => socket api
UDP和TCP之间的主要特点对比:
- UDP:无连接,不可靠传输,面向数据报,全双工
- TCP:有连接,可靠传输,面向字节流,全双工
有连接 VS 无连接
- TCP进行编程的时候,也是存在类似的建立连接的过程(打电话:打过去对方接通才能说话)
- UDP:发微信/短信就是不需要建立连接就能进行通信的
- 这里所谓的“连接”是抽象的。客户端和服务器之间,使用内存保存对端的信息,双方都保存这个信息,此时“连接”就出现了。这里最大的区别是一个客户端可以连接多个服务器,一个服务器也可以对应多个客户端的连接(多对多)
可靠传输 VS 不可靠传输
- 可靠传输,不是说A给B发的消息100%能到(这个要求太难了),A尽可能把消息传给B,并且传输失败的时候,A能感知到;或者传输成功的时候,A也能感知到
- TCP可靠(传输效率更低),UDP不可靠(传输效率更高),但TCP比UDP更安全这句话是错误的
注意:谈到“网络安全”
指的是如果你传输的数据是否容易被黑客截获以及如果被截获后是否会泄露一些重要信息。安全和入侵,破解,加密反编译。
面向字节流 VS 面向数据报
- TCP和文件操作类似,都是“流”式的(由于这里传输的单位是字节,称为字节流)
接100ml水,可以一次接100ml,也可以一次接50ml分两次,也可以一次接10ml分十次
通过tcp读写100字节数据,可以一次读写100字节,也可以一次读写50字节分两次,也可以一次读写10字节分十次
- UDP是面向数据报,读写的基本单位,是一个UDP数据报(包含了一系列数据/属性)
全双工 VS 半双工
- 全双工:一个通道可以双向通信
- 半双工:一个通道只能单向通信
UDP数据报套接字编程
两个核心的类:DatagramSocket
、DatagramPacket
DatagramSocket API
DatagramSocket是一个socket对象,操作系统,使用文件这样的概念,来管理一些软硬件资源,网卡,操作系统也是使用文件的方式管理网卡的,表示网卡的这类文件,称为Socket文件。Java中的socket对象,就对应这系统里的socket文件(最终还是要落到网卡),要进行网络通信,必须得先有socket对象
DatagramSocket
是UDP Socket,用于发送和接收UDP数据报。
DatagramSocket
构造方法:
方法签名 | 方法说明 |
---|---|
DatagramSocket() | 创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端) |
DatagramSocket(int port) | 创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端) |
第一个构造方法用于客户端,使用哪个端口由系统自动分配
第二个构造方法用于服务端,使用哪个端口手动指定
关于“五元组”:源IP、源端口、目的IP、目的端口、协议类型
其中的源端口和目的端口,此时就需要给客户端、服务器各自分配端口号
客户端给服务器发数据:源端口就是客户端的端口,目的端口就是服务器的端口
服务器给客户端发数据:源端口就是服务器的端口,目的端口就是客户端的端口
IP地址:对于服务器来说,需要有一个固定的端口号,方便其他客户端找到;对于客户端来说,端口号不能指定固定值
一个客户端的主机,上面运行的程序很多,天知道你手动指定的端口是不是被别的程序占用了,让系统自动分配一个端口是更明智的选择。
服务器是完全在程序员手里控制的,程序员可以把服务器上的多个程序安排好,让他们使用不同的端口
DatagramSocket
方法:
方法签名 | 方法说明 |
---|---|
void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待) |
void send(DatagramPacket p) | 从此套接字发送数据报包(不会阻塞等待,直接发送) |
void close() | 关闭此数据报套接字 |
注意:receive
这里传入的是个空对象,这是服务器先创建好放入的,然后一直阻塞等待着的,然后请求来了后再把数据解析放进去的
DatagramPacket API
DatagramPacket
是UDP Socket发送和接收的数据报。
DatagramPacket
构造方法:
方法签名 | 方法说明 |
---|---|
DatagramPacket(byte[] buf, int length) | 构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度作为存储数据的空间(第二个参数length) |
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) | 构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号 |
DatagramPacket
这个对象里就包含着通信双方的IP和Port
DatagramPacket
方法:
方法签名 | 方法说明 |
---|---|
InetAddress getAddress() | 从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址 |
int getPort() | 从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号 |
byte[] getData() | 获取数据报中的数据(UDP数据报的载荷部分,即完整的应用层数据报) |
构造UDP发送的数据报时,需要传入 SocketAddress
,该对象可以使用 InetSocketAddress
来创建。
InetSocketAddress API
InetSocketAddress
( SocketAddress
的子类 )构造方法:
方法签名 | 方法说明 |
---|---|
InetSocketAddress(InetAddress addr, int port) | 创建一个Socket地址,包含IP地址和端口号 |
一个服务器要给很多客户端提供服务,也不知道客户端什么时候来,只能“时刻准备着”,随时准备提供服务
服务器步骤:
- 读取数据,并解析(固定套路)
- 根据请求,计算出响应(对于回显服务器不关心这个过程,请求是啥就返回啥响应。但商业级的服务器主要的代码都是在这一步)
- 把响应写回客户端(固定套路)
客户端步骤:
- 从控制台读取用户输入内容
- 构造请求对象,并发给服务器
- 读取服务器的响应,并解析出响应内容
- 显示到屏幕上
UDP回显客户端服务器
服务器
//UDP回显服务器
//客户端法的请求是啥,服务器返回的相应就是啥
public class UdpEchoServer {
<