首页 > 关于socket通信的疑问?

关于socket通信的疑问?

一个服务器应用程序一般来说只通过一个端口来与多个客户端“同时”进行通信,比如一个web应用程序一般来说通过80端口来和多个客户端通信,那么是如何实现一个应用“同时”和多个客户端通信的呢?

1.这里的“同时”我不理解是一个服务器应用真的能够同时和多个客户端通信?还是实际上同时只有一个客户端能够和服务器应用通信,而因为单个连接通信时间较短造成一个服务器应用和多个客户端“同时”通信的假象?

2.在socket的层次上是否可以这么理解:服务器应用是真正能做到同时和多个客户端通信的,端口相当于一个导线,这条导线上连接一个插板,插板上有很多插座(server socket),这样,多个插头(client socket)就可以同时插在一个插板上。完成多个客户端“同时”和一个服务器应用通信?

2015.11.28更新

3.每个应用只能通过一个端口来和客户端通信,那么,大型网站或者im服务器是如何保证海量客户端和服务器应用的稳定、高效连接?

2015.11.29更新

感谢大家的回答!我其实更希望得到一个socket层面上的回答(一个服务器程序如何通过调用socket接口来实现与多个客户端的高效通讯?)。我现在还没到理解socket实现机制的层次,请大家谅解。


一个Web服务器程序可以使用多个端口(同时也是多个socket)来对多个客户端进行通信。

所谓“同时和多个客户端通信“ 有两种理解方式:

关于 socket, 单一一个 socket 也是可以并发地和并行地与多个客户通信,这与操作系统的提供的API有关。


你可以把一个socket看作是一个文件(实际上他就是一个网络文件),
而端口是个文件夹,服务器监听一个端口,就是监听一个文件夹,
每一个客户端的socket都会生成一个socket文件,每次文件传输好之后,
操作系统就会通知这个文件夹的监听者,
然后服务器应用就开始读取socket文件的数据.
而服务器要返回数据的时候,就继续往这个文件里面写入数据,
数据写入完成后,内核会负责把写入的数据发送给客户端.
其实客户端在建立socket的时候也建立了一个socket文件,
只是端口是随机的,
服务器返回给客户端的数据其实就是往客户端的这个端口发数据


对于某一个初始化socket连接来说,服务器端会分配一个sockekt句柄来维护当前通信的上下文,这个socket句柄是有限制的,受限于linux系统最大打开文件句柄数,一般来说这个数值初始化是1024,你可以通过修改系统配置将其改为65535,但是你不要以为65535就全部都是socket用,所有打开文件的操作都会占用一个句柄,socket只不过是一种特殊的文件句柄而已,我们就假设操作系统中其它操作占据了500多个句柄,那么就剩下6500多个句柄供socket使用。
socket编程中对于一个请求来说,有时候会使用一个线程或者进程来维护,这时候程序连接数还会受限于最大线程或者进程数,当然现在很多socket程序是使用线程池或者工作进程的方式来进行业务处理,这时候就没有这个限制了。
如果当前请求的业务处理是cpu密集型操作,致使cpu居高不下,或者慢IO操作,致使业务迟迟得不到响应,当然会使请求拥塞住,这时候客户端的并发数也上不去。
反正影响并发数的影响因素是多方面的,有时候对于垃圾回收型语言,就是内存太小,导致频繁垃圾回收,拖垮了cpu,也会有问题。


我猜楼主可能不清楚 socket 和 端口以及TDP/UDP connection 这些之间的关系以及操作系统里面的对应.
拿*unix操作系统举例:

socket 在操作系统里面是一个文件描述符, 和普通的文件的文件描述符一样,可以通过系统调用 read(), write()进行读取和写入数据.

每一个socket 其实有几个重要的参数, 本机ip,本机port,对端ip,对端port,以及协议等, 这几个通常要是unique的, 操作系统才不会把socket 和 socket 之间搞混.

服务器端的程序,一般会把socket 和某一个ip,port 端口bind 起来, 这样构成了一个特殊的socket, 这个socket 没有对端ip和对端port, 只有本机ip和本机port.

你可以把这个socket 看成是一个特殊的文件(好比登记表), 当有新的连接进来的时候, 都可以通过 accept 系统调用让操作系统接受一个新的连接, 同时返回一个新的socket, 这个socket 是绑定在(本机ip,本机port,对端ip,对端port) 这个上面的, 也就是对应了一个unique的连接.

然后你可以拿着这个新产生的socket 进行写入和读取等操作(因为它是一个文件描述符). 那个用来listen的socket 和这个新的socket 是不相关的,在操作系统内部也是分开的,会有各自的缓冲区等.

至于你提出的问题: 如何用一个端口来与多个客户端“同时”进行通信? 我粗浅的理解, 端口只是在操作系统中用来区分一个连接和另一个连接的其中一个项(剩下的还有本机ip,对端ip,和对端port), 因此和真正的通信并无太大关系, 你一定是把它想象成了和网卡的出口类似的东西.


我猜题主可能不清楚的是服务器的实现方式。举个例子,一个老大接到很多用户的请求,为了及时响应,他可以把每个任务分派给一个小弟。这是典型的apache的模型。另外一种做法是全部的用户请求老大自己完成,前提是有个秘书帮老大收集好用户需求后才告诉老大,并且这些用户请求不能特别费时间(这两点你自己想为啥吧)。这是利用epoll的nginx网络模型,只不过nginx有多个老大,但没有本质区别。


首先,一个服务器可以通过多个端口与客户端通信。在应用程序上,可以多个进程监听同一个端口,这对于多核机器而言,可以理解为就是同时的。然而,这仅仅在应用程序方面。应用程序将数据包写到协议栈,再到总线,再到网卡,可以理解为串行的,也就是它不是同时的。


可以参考下我的专栏中第一篇文章对Linux下Socket通信的源码,对于连接的每一个请求,虽然都会有一个socket,但是并不是每一个socket都需要再重新绑定一个端口。对于连接请求会再独立的开辟线程或者进程去处理去轮询消息的接收和发送,每个连接根据远端的socket来初始化一个本地的socket,然后通过这个socket和其进行通信。最开始的socket还是在用来处理连接的请求建立上。


一个socket是在监听(listen())时是可以同时与多个客户端连接的(以Tcp为例,可以与多个客户端进行三次握手)。然后select/poll/epoll 取出建立好的链接进行处理。
至于大量的连接如何处理,瓶颈可能在于I/O复用,可以了解下epoll,libev等。


高负荷除了需要采用内核最优的socket模型(win32下iocp linux的epoll)之外,多进程调度也很关键,即使这样,单台机器的处理也是有瓶颈的,cpu/内存/端口数等,这时就要考虑网关模式,或服务器集群

【热门文章】
【热门文章】