计算机网络 | 套接字 Socket 基础知识

Moutxing

读完需要

6分钟

速读仅需 2 分钟


上一篇说了计算机网络的 TCP 与 UDP协议基础知识,今天我们接着说套接字 Socket 基础理论理解。

一、何为套接字

套接字是指通过软件来实现作为插口或者插槽,一端插在客户端,一端插在服务端。

Socket 编程进行的是端到端的通信,往往意识不到中间经过多少局域网,多少路由器,因此,能设置的参数也只能是端到端的协议之上网络层和传输层。

在网络层:Socket 函数需要指定是 IPV4 还是 IPV6,分别对应设置为 AF_INET 和 AF_INET6。

在传输层:需要指定是 TCP 还是 UDP 。TCP 协议是基于数据流的,所以设置为 SOCK_STERAM,而 UDP 是基于数据包的,所以设置为 SOCK_DGRAM。

二、基于 TCP 协议的 Socket 程序函数调用过程

TCP 的服务端要监听一个端口,一般是先调用 bind 函数,给这个 Socket 赋予一个 IP 地址和端口。

当服务端有了 IP 地址和端口号,就可以调用 listen 函数进行监听。客户端就可以发起连接了。

在内核中,每个 Socket 维护两个队列。

已经建立连接队列,完成三次握手完毕,处于 established 状态。

未完成建立连接的队列,没有三次握手,处于 syn_rcvd 的状态。

服务端调用 accept 函数,拿出一个已经完成的连接进行处理,如果还没有完成就要等着。

在服务端等待的时候,客户端可以通过 connect 函数发起连接。先在参数中指明要连接的 IP 地址和端口号,然后发起三次握手。内核会给客户端分配一个临时的端口。一旦握手成功,服务端的 accep 就会返回另一个 Socket 。

监听的 Socket 和真正用来传输数据的 Socket 是不同的两个,一个是叫做监听 Socket,一个叫做已连接 Socket。

建立连接成功后,双方开始通过 read 和 write 函数来读写数据。

具体连接如下图所示:


三、基于 UDP 协议的 Socket 程序函数调用过程

因为 UDP 没有连接,所以不需要三次握手,也不需要 listen 和 connect。同时 UDP 因为没有维护连接状态,所以,也不需要每对连接建立一组 Socket ,只需要一个 Socket 就能够和多个客户端通信。

UDP 也需要 IP 地址和端口号,因此也需要 bind 。每次通信时候,调用 sendto 和 recvfrom 就可以传入 IP 地址和端口号。

具体连接如下图所示:

四、服务器如何接更多的项目?

方式一:将项目外包给其他公司(多进程方式)

相当于你是一个代理,一直监听请求,一旦建立一个连接,就会有一个已连接的 Socket ,这时候就可以创建一个子进程,然后将基于连接 Socket 的交互交给这个新的子进程来做。

在 Linux 下,创建进程用 FORK 函数。两个进程复制完成时候一模一样,通过返回值为 0 则判断为子进程,返回其他的整数,则判断为父进程。

FORK 的子进程和父进程完全一样,所以判断子进程是否完成了通信退出的方法是,父进程通过子进程的 ID 查看是否完成而选择是否退出。

方式二:将项目转包给独立的项目组(多线程方式)

在 Linux 下,通过 pthread_create 创建一个线程,也是调用 do_fork 。不同的是,虽然新的 task 列表会创建一项,但是很多资源还是共享的。不是多了一个引用而已。

注:基于进程或者线程模型的通信方式,存在 C10K 问题,所谓 C10K 问题就是 1 台机器要维护 1 万个连接,就要创建 1 万个进程或者线程。显然这样是很不科学且实用的。

方式三:一个项目组支持多个项目(IO 多路复用,一个线程维护多个 Socket)

一个项目组可以看多个项目,通过项目进度墙来查看项目的进度。一旦项目有进展,就派人去盯一下。

由于 Socket 是文件描述符,因而某个线程盯的 Socket ,都放在一个文件描述集合 fd_set 中,这就是项目墙。然后调用 select 函数来监听文件描述符集合是否有变化。

方式四:以恶搞项目组支撑多个项目(IO 多路复用,从“派人盯着”到“有事通知”)

由于方式三是通过轮询的方式来盯着这个项目,所以还是不够好。因此,采用 epoll 函数通过注册 callback 函数的方式,来当文件描述符发送变化的时候就主动通知。

------------------------(end)-------------------

原文链接:,转发请注明来源!