Sokcet


一. Socket套接字

1.1 什么是套接字


所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制,是支持TCP/IP协议的路通信的基本操作单元

1.2 套接字主要类型


TCP流套接字

流套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复送,并按顺序接收。流套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP协议。即传输层TCP通信满足如下条件:

  • 有连接可靠传输
  • 面向字节流,即可以利用IO流分开进行多次收发
  • 有接受的缓冲区和传输缓冲区,没有传输大小限制

UDP数据报套接字

数据报套接字提供一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP协议进行数据的传输。由于数据报套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在程序中做相应的处理。即传输层UDP通信满足如下条件:

  • 无连接不可靠传输
  • 面向数据报,即发送的内容必须一次性收发
  • 有接受的缓冲区,但没有发送的缓冲区,一次性最多传输64k大小

原始套接字

原始套接字与标准套接字(标准套接字指的是前面介绍的流套接字和数据报套接字)的区别在于:原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送的数据必须使用原始套接。

二. UDP常见API

2.1 DatagramSocket

DatagramSocket 用来构造传输UDP协议的类,表格介绍如何构造类对象

构造方法含义
DatagramSocket(int port)创建一个UDP数据报套接字Socket,绑定到指定的端口

表格介绍DatagramSocket 常用的传输和接受数据包方法

方法含义
void receive(DatagramPacket p)套接字接收数据报
void send(DatagramPacket p)套接字发送数据报包
void close()关闭套接字

2.2 DatagramPacket


DatagramPacket 用来构造传输数据包的类,表格介绍如何构造类对象

构造方法含义
DatagramPacket(byte[] buffer, int length)构造数据包接受数据,parm1:接受的内容,parm2:指定接受的长度
DatagramPacket(byte[] buffer, int offset, int length,SocketAddress address)构造数据包发送数据,parm1:发送内容,parm2:从字符组下标哪里开始发送,parm3:发送的长度、parm4:封装目的IP和端口对象

表格介绍DatagramPacket 常用的方法

方法含义
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
byte[] getData()获取数据报中的数据

2.3 SocketAddress


在上述DatagramPacket对象发送数据包时,需要构造SocketAddress 来封装IP和端口,我们常使用SocketAddress 的子类InetSocketAddress来构建对象

构造方法含义
InetSocketAddress(InetAddress address, int port)创建一个Socket地址,包含IP地址和端口号

三. C/S结构测试


3.1 TCP通信


TCP网络协议通信可查看这篇文章:
利用TCP网络编程实现客户端和服务端简单交互

3.2 UDP通信


下面讲解如何利用Socket实现C/S结构的UDP协议通信,如下图为传输过程

服务端Server

//构建UDP协议的服务端public class UDPServer {//构建套接字DatagramSocket socket;//构建协议对象(参数表示服务器端口号)public UDPServer(int serverPort) throws SocketException {socket = new DatagramSocket(serverPort);}//开启服务器进行监听public void startListening() throws IOException {System.out.println("===============服务器开启监听===================");Scanner scanner = new Scanner(System.in);//用来回复while (true){//1.构建数据包用来接受数据DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);//2.等待接受数据(没有返回值,但是已经接收到了数据)socket.receive(receivePacket);//3.将接受的数据取出String receiveData = new String(receivePacket.getData(),0,receivePacket.getLength());//如果客户端发送来ex或exit则退出if ("ex".equals(receiveData) || "exit".equals(receiveData)){System.out.println("===============服务器退出===================");break;}//4.根据请求进行回复System.out.println("收到消息:{"+receiveData+"}");System.out.println("请输入想要回复的信息:");String answer = scanner.nextLine();byte[] answerBytes = answer.getBytes();//5.将回复封装为数据包发送回客户端//注意:这里是根据接受的数据包来获取发送方的ip和端口DatagramPacket answerPacket = new DatagramPacket(answerBytes, 0, answerBytes.length, receivePacket.getSocketAddress());socket.send(answerPacket);}}public static void main(String[] args) throws IOException {UDPServer udpServer = new UDPServer(8899);udpServer.startListening();}}

客户端Client

//构建UDP协议的客户端public class UDPClient {//服务器端口号int serverPort;//服务器ipString serverIp;//构建套接字DatagramSocket socket;//构建协议对象(参数表示服务器端口号和服务器ip地址)public UDPClient(int serverPort, String serverIp) throws SocketException {this.serverIp = serverIp;this.serverPort = serverPort;this.socket = new DatagramSocket();}public void startSend() throws IOException {System.out.println("===============客户端开启接受===================");//1.获取输入Scanner scanner = new Scanner(System.in);while (true){System.out.println("请输入想要发送的信息:");String request = scanner.nextLine();byte[] requestBytes = request.getBytes();//2.构建数据包//InetAddress.getByName()表示根据服务器主机名称解析ip地址DatagramPacket requestPacket = new DatagramPacket(requestBytes, 0, requestBytes.length, InetAddress.getByName(serverIp), serverPort);//3.发送数据包socket.send(requestPacket);//设定退出if ("ex".equals(request) || "exit".equals(request)){System.out.println("===============客户端退出===================");break;}//4.构建数据包用来获取响应DatagramPacket receivePacket = new DatagramPacket(new byte[1024], 1024);socket.receive(receivePacket);//5.将获取的数据取出String receiveData = new String(receivePacket.getData(),0,receivePacket.getLength());System.out.println("收到消息:{"+receiveData+"}");}}public static void main(String[] args) throws IOException {UDPClient udpClient = new UDPClient(8899, "192.168.31.104");udpClient.startSend();}}

服务器端运行在192.168.31.104上,利用如下代码开启监听:

java -cp NetworkSocket-1.0-SNAPSHOT.jar com.nuaa.mySocket.server.UDPServer 

客户端运行在本地,通信对话如下