网络基础——Socket编程

套接字底层原理

TCP套接字

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

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

在操作系统中,为每个Socket维护两个队列。一个是已经建立了连接的队列,三次握手已完毕,处于established状态。一个是还没有完全建立连接的,没有完成三次握手,处于syn_rcv的状态。

在服务端等待的时候,客户端可以通过connect函数发起连接。先在参数中指明要连接的IP地址和端口号,然后开始三次握手,操作系统会给客户端分配一个临时的端口。一旦握手成功,服务端的accept就会返回另一个用于传输数据的Socket。

连接建立成功之后,双发开始通过readwrite函数来读写数据,就像往一个文件流里面写东西一样。

UDP套接字

因为UDP通信无需建立连接看,所以也不需要三次握手,也就不需要调用listenconnect函数函数,但是,UPD的交互仍然需要IP地址和端口号,因而需要bind。UDP是没有维护连接状态的,因而不需要对没对连接建立一组Socket,而是只要有一个Socket,就能够和多个客户端通信。也正是因为没有连接状态,每次通信的时候,调用sendtorecvfrom,都可以传入IP地址和端口。

服务器如何提高并发量

Web请求一般都是HTTP请求,而HTTP协议又是基于TCP的,所以,我们主要探索如何让服务器同时处理更多TCP连接请求。

  1. 多进程

    当有新的请求进来,fork出一个子进程,让子进程处理该请求,提高并发量

  2. 多线程

    进程开销太大,线程则轻量级的多,所以我们还可以通过在进程中创建新的线程来处理请求。

    上面基于进程或线程的模型还是有问题,因为每新进来一个 TCP 连接请求,就需要分配一个进程或线程,从而引发著名的 C10K 问题(一台机器要维护 1 万个连接,就要创建 1 万个进程或者线程,操作系统是无法承受的。如果维持 1 亿用户在线需要 10 万台服务器,成本也太高了),为此,又诞生了一种新的技术 —— 多路 IO 复用。

  3. 多路IO复用

    所谓多路IO复用可以简单理解为一个线程维护多个Socket(前面多进程多线程说的都是一个进程或线程维护一个Socket),这也有两种实现方式:轮询和事件通知。

    因为 Socket 在 Linux 系统中以文件描述符形式存在,所以我们把一个线程维护的所有 Socket 叫做文件描述符集合,所谓轮询就是调用内核的 select 函数监听文件描述符集合是否有变化,一旦有变化,就会依次查看每个文件描述符,对那些发生变化的文件描述符进行读写操作,然后再调用 select 函数监听下一轮的变化。

    显然,轮询的效率有点低,因为每次文件描述符集合有变化,都要将全部 Socket 轮询一遍,这大大影响了系统能够支撑的最大连接数。如果改成事件通知的方式,情况要好很多。所谓事件通知,就是某个文件描述符发生变化,调用 epoll 函数主动通知。这种方式使得监听的 Socket 数据增加的时候,效率不会大幅度降低,能够同时监听的 Socket 的数目也非常多。上限就为系统定义的、进程打开的最大文件描述符个数。

    因此,epoll 被称为解决 C10K 问题的利器。

posted @ 2020-08-20 22:19  _大可乐  阅读(119)  评论(0编辑  收藏  举报