异步套接字服务器是如何工作的?

13

我需要说明的是,我现在不是在询问具体的实现细节,而是需要一个整体概述。我理解socket的基本概念,需要对整个过程进行澄清。目前我的(可能非常错误的)理解是:

一个套接字(socket)在自己的线程中不断地监听客户端连接请求,当连接发生时,会引发一个事件,然后将另一个线程分配给执行连接过程。在连接过程中,为客户端分配一个专用的套接字(socket)与服务器通信。服务器等待来自客户端的数据,当数据到达时,引发一个事件,然后将另一个线程从流中读取数据到缓冲区。

我的问题是:

我的理解有多偏离事实?

每个客户端套接字(socket)是否需要一个线程来监听数据?

如何将数据路由到正确的客户端套接字(socket)?这是TCP/UDP/kernel内部处理的吗?

在这种多线程环境下,通常共享哪些数据,存在哪些争用点?

如果有任何澄清和额外的解释,将不胜感激。

编辑:

关于典型共享的数据和争用点的问题,我意识到这更多是一个实现细节,而不是关于接受连接和发送/接收数据的一般过程的问题。我查看了几个实现(SuperSocket和Kayak)并注意到某些同步内容,例如会话缓存和可重用的缓冲池。可以忽略这个问题。感谢您的反馈。


你读过 Comer 的书吗?http://www.cs.purdue.edu/homes/dec/netbooks.html 请购买并阅读其中一两本。这个问题已经被全面解决了。 - S.Lott
在这种多线程的环境下,通常会共享哪些数据?答案是:“所有数据都由多线程应用程序共享。” 这就是定义。那么为什么要问:“有哪些争议点?”请澄清一下:“争议点”可能意味着任何事情。 - S.Lott
2个回答

16

每个连接一个线程是糟糕的设计(不可扩展,过于复杂),但不幸地太常见了。

套接字服务器的工作原理大致如下:

  • 设置侦听套接字以接受连接,并添加到套接字集中
  • 检查套接字集以获取事件
  • 如果有挂起连接,则通过接受连接创建新套接字,然后将其添加到套接字集中
  • 如果已连接套接字有事件,则调用相关 IO 函数
  • 再次检查套接字集以获取事件

这些操作都在一个线程中进行,您可以轻松处理数千个连接套接字,并且很少有有效的理由通过引入线程来使它更加复杂。

while running
    select on socketset
    for each socket with events
        if socket is listener
            accept new connected socket
            add new socket to socketset
        else if socket is connection
            if event is readable
                read data
                process data
            else if event is writable
                write queued data
            else if event is closed connection
                remove socket from socketset
            end
        end
    done
done

IP协议栈会处理所有细节,将数据包按照指定的顺序发送到相应的“套接字”。从应用程序的角度来看,套接字表示可靠有序的字节流(TCP)或不可靠无序的数据包序列(UDP)。

编辑:针对更新后的问题。

我不了解你提到的任何库,但就你提到的概念而言:

  • 会话缓存通常保存与客户端相关的数据,并可以重用该数据进行多个连接。当您的应用程序逻辑需要状态信息时,这是有意义的,但它比实际网络端口的层次高。在上面的示例中,“处理数据”部分将使用会话缓存。
  • 缓冲池也是高流量服务器的一种简单且常用的优化方法。该概念非常容易实现,您只需从缓冲池中获取预先分配的缓冲区并使用它,然后将其返回到缓冲池,而不是为存储读/写数据的空间分配/释放内存,这避免了(有时相对昂贵的)后端分配/释放机制。这与网络直接相关,您也可以将缓冲池用于读取文件块并处理它们等其他用途。

这正是我所寻找的解释类型。非常感谢。 - w.brian
1
那么一个线程如何从多个连接的套接字中读取数据呢?你会不断迭代所有连接的套接字,并为每个套接字执行同步读操作,消耗每个流中的数据(如果有的话),然后继续下一个吗? - w.brian
不要使用“轮询每个套接字以查看是否有可读数据”的机制,而是使用检查“哪个套接字有可读数据”的机制,例如select()、poll()、epoll()、kqueue()、WSAEventSelect() - 从您的操作系统中选择一个。然后迭代并读取那些确实有可读数据的套接字的数据。 - Erik

1
我的理解有多偏差?
相当远。
每个客户端套接字是否需要自己的线程来监听数据?
不需要。
如何将数据路由到正确的客户端套接字?这是TCP/UDP/kernel的内部机制处理的吗?
TCP/IP是一种分层协议。没有“内核”它。它是由几个独立的部件组成,每个部件之间有一个单独的API。
IP地址在一个地方处理。
端口号在另一个地方处理。
IP地址与MAC地址匹配以标识特定的主机。端口号是将TCP(或UDP)套接字绑定到特定应用程序软件的关键所在。
在这种线程环境中,通常共享什么类型的数据,以及争用点是什么?
哪种线程环境?
共享数据?什么?
竞争?物理通道是最重要的竞争点。(例如,Ethernet依赖于冲突检测。)此后,计算机系统的每个部分都是多个应用程序共享的稀缺资源,并且是争用点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接