平时我们使用ServerSocket指定了某个端口(例如8080),然后多个客户端连接上socket之后就都用这个8080端口和服务器端通讯。或者http服务器使用80端口也是和多个浏览器进行连接通讯。为什么可以这样呢?操作系统的进程在同个端口的多个连接是如何进行分辨的?
译文
我们这里讲Socket连接:
1. 端口只是一个数字辨识,不是真正的物理端口;
2. 一个Socket连接的主键(即不同socket之间的区分)是由一个五元组{SRC-IP, SRC-PORT, DEST-IP, DEST-PORT, PROTOCOL}组成,即{源地址,源端口,目标地址,目标端口,协议}组成,那些说四元组不包含协议的说法是错误的。
3. 一个进程可以拥有多个socket连接。
例子一、两个客户端连接在同个服务器的同个端口80,即有两个socket连接:
- socket1 {SRC-A, 100, DEST-X,80, TCP}
- socket2{SRC-B, 100, DEST-X,80, TCP}
主机A和主机B的地址不同,两台主机同时连接到服务器X的80端口。服务器要怎么处理这个连接是它的事,我们要理解的是为什么一个主机同个端口能监听多个客户端Socket连接。
解释:
1. 是因为两个客户端的IP不同,服务器能识别出不同的Socket;
2. 即使IP地址相同,端口不同,服务器也能够分辨;
3. 只要服务器知道收到的请求和哪个socket相关,那么它就能使用这个socket正确地回复那个客户端;
4. 如果对于不同的socket需要不同的端口,那么不仅仅浪费服务器资源,而且每次客户端连接上serverSocket之后还要另外分配新的端口和客户端通信。没必要。
例子二、不同的进程可以监听同一个端口。
- 因此在服务器的两个使用不同协议的进程可以监听同一个端口。
- 如果一个socket的辨识只是四元组不包括协议{SRC-IP, SRC-PORT, DEST-IP, DEST-PORT},那么不同进程是不可能同时监听同一个端口的。没有协议的话一个客户端连接到同一台某个有两个进程监听端口的服务器,那么就没有什么机制可以确定客户端是要连接哪一个进程了。
操作系统(特别是UNIX)中,子进程能够继承父进程的所有文件描述File-descriptors (FD),因此父进程A中监听着的所有socket,也可以被进程的所有子进程A1,A2监听。但是不同进程B是不能监听同一个端口的。
翻译自stackoverflow的第二个答案:
https://stackoverflow.com/questions/3329641/how-do-multiple-clients-connect-simultaneously-to-one-port-say-80-on-a-server
疑问
但是里面说了:
For example two unrelated clients (say one is using TCP and another is using UDP) can bind connect and communicate to the same server node and to the same port but they must be served by two different server-processes.
两个不同的客户端(一个使用tcp一个用udp)可以连接同一个服务器的同一个端口,但是需要两个不同的服务器进程。
我觉得是错的,所以我尝试了一下:
服务器:
public static void main(String[] args) throws IOException {
//很明显如下程序在同一个进程
//udp使用5555端口
DatagramSocket ds = new DatagramSocket(5555);
byte[] buf = new byte[1024];
DatagramPacket dp_receive = new DatagramPacket(buf, 1024);
ds.receive(dp_receive);
System.out.println("收到udp消息");
System.out.println("客户端发送udp端口为" + dp_receive.getPort());
//tcp使用5555端口
ServerSocket serverSocket = new ServerSocket(5555);
Socket socket = serverSocket.accept();
System.out.println("tcp建立了连接");
System.out.println("客户端发送tcp端口为" + socket.getPort());
}
客户端:
public static void main(String[] args) throws IOException, InterruptedException {
//udp使用12345端口发出udp消息
DatagramSocket ds = new DatagramSocket(12345);
String str_send = "Hello";
InetAddress loc = InetAddress.getLocalHost();
//udp发送到服务器的5555端口
DatagramPacket dp_send = new DatagramPacket(str_send.getBytes(), str_send.length(), loc, 5555);
ds.send(dp_send);
//tcp使用12345端口发送到服务器的5555端口
Socket socket = new Socket("127.0.0.1", 5555, null, 12345);
}
服务器输出:
收到udp消息
客户端发送udp端口为12345
tcp建立了连接
客户端发送tcp端口为12345
所以很明显,同个进程也可以通过不同的协议监听同一个端口。
总结
- 不同协议可以监听同一个端口(不管是不是在服务器的同个进程)
- 某个协议的进程可以监听多个客户端的连接,因为只要五元组不同进程就能分辨。
- 从上面的例子可以知道,客户端同个进程也可以在同个端口用不同的协议与客户端建立连接。