乐趣区

TCP与SOCKET

三次握手

四次挥手

SOCKET

在内核中,会维护两个队列:

  • 半连接队列(syn 队列),存放处于 SYN-RCVD 状态的 socket
  • 全连接队列(accept 队列),存放处于 ESTABLISHED 状态的 socket

accept 函数会从全连接队列里取出一个连接,如果没有则阻塞。这里取出的是一个新的连接,叫已连接 socket,不同于一开始创建的监听 socket。Socket 在 Linux 中以文件的形式存在,有自己的文件描述符,读写就如同一个文件流。


socket 结构里有一个发送队列和一个接收队列,里面保存着缓存 sk_buff,里面可以看到完整的包结构。

数据接收流程

网卡接收到数据后写入内存,并向 CPU 发起中断,操作系统执行中断程序唤醒对应 socket 等待队列里的进程。
如何同时监听多个 socket:

  • select

将进程放入所有需要监听的 socket 的等待队列里,如果有一个 socket 接收到了数据就唤醒进程,然后遍历 socket 列表查看是哪个 socket 接收到了数据。

  • epoll
int epfd = epoll_create(...)
epoll_ctl(epfd, ...)
while(1){int n = epoll_wait(...)
    for (接收到数据的 socket){// 处理}
}

利用 epoll_ctl 将需要监视的 socket 添加到 epoll_create 创建的对象(eventpoll)中,然后利用 epoll_wait 进行阻塞,将进程放入 eventpoll 的等待队列中。当有 socket 接收到数据,中断程序将其被 eventpoll 的就绪列表所(rdllist)引用,然后唤醒进程,进程只需要遍历就绪列表就可以了。socket 在 eventpoll 中利用红黑树存储,而就绪列表利用双向链表存储。

退出移动版