服务器并发IO性能提升之路 — 从网络编程基础到epoll

从网络编程基本概念说起

      我们常常使用HTTP协议来传输各种格式的数据,其实HTTP这个应用层协议的底层,是基于传输层TCP协议来实现的。TCP协议仅仅把这些数据当做一串无意义的数据流来看待。所以,我们可以说:客户端与服务器通过在建立的连接上发送字节流来进行通信。
      这种C/S架构的通信机制,需要标识通信双方的网络地址和端口号信息。对于客户端来说,需要知道我的数据接收方位置,我们用网络地址和端口来唯一标识一个服务端实体;对于服务端来说,需要知道数据从哪里来,我们同样用网络地址和端口来唯一标识一个客户端实体。那么,用来唯一标识通信两端的数据结构就叫做套接字。一个连接可以由它两端的套接字地址唯一确定:

(客户端地址:客户端端口号,服务端地址:服务端端口号)

有了通信双方的地址信息之后,就可以进行数据传输了。那么我们现在需要一个规范,来规定通信双方的连接及数据传输过程。在Unix系统中,实现了一套套接字接口,用来描述和规范双方通信的整个过程。

socket():创建一个套接字描述符

connect():客户端通过调用connect函数来建立和服务器的连接

bind():告诉内核将socket()创建的套接字与某个服务端地址与端口连接起来,后续会对这个地址和端口进行监听

listen():告诉内核,将这个套接字当成服务器这种被动实体来看待(服务器是等待客户端连接的被动实体,而内核认为

socket()创建的套接字默认是主动实体,所以才需要listen()函数,告诉内核进行主动到被动实体的转换)

accept():等待客户端的连接请求并返回一个新的已连接描述符

最简单的单进程服务器

      由于Unix的历史遗留问题,原始的套接字接口对地址和端口等数据封装并不简洁,为了简化这些我们不关注的细节而只关注整个流程,我们使用PHP来进行分析。PHP对Unix的socket相关接口进行了封装,所有相关套接字的函数都被加上了socket_前缀,并且使用一个资源类型的套接字句柄代替Unix中的文件描述符fd。在下文的描述中,均用“套接字”代替Unix中的文件描述符fd进行阐述。一个PHP实现的简单服务器伪代码如下:

<?php

if (($listenSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP))=== false) {
    echo '套接字创建失败';
}
if (socket_bind($listenSocket, '127.0.0.1', 8888) === false) {
    echo '绑定地址与端口失败';
}
if (socket_listen($listenSocket) === false) {
    echo '转换主动套接字到被动套接字失败';
}
while (1) {
    if (($connSocket = socket_accept($listenSocket)) === false) {
        echo '客户端的连接请求还没有到达';
    } else {
        socket_close($listenSocket); //释放监听套接字
        socket_read($connSocket);  //读取客户端数据,阻塞
        socket_write($connSocket); //给客户端返回数据,阻塞
        
    }
    socket_close($connSocket);
}

我们梳理一下这个简单的服务器创建流程

    socket_create():创建一个套接字,这个套接字就代表建立的连接上的一个端点。第一个参数AF_INET为使用的底层协议为IPv4;第二个参数SOCK_STREAM表示使用字节流进行数据传输;第三个参数SQL_TCP代表本层协议为TCP协议。这里创建的套接字只是一个连接上的端点的一个抽象概念。

    socket_bind():绑定这个套接字到一个具体的服务器地址和端口上,真正实例化这个套接字。参数就是你之前创建的一个抽象的套接字,还有你具体的网络地址和端口。

    socket_listen():我们观察到只有一个函数参数就是之前创建的套接字。有些同学之前可能认为这一步函数调用完全没有必要。但是它告诉内核,我是一个服务器,将套接字转换为一个被动实体,其实是有很大的作用的。

    socket_accept():接收客户端发来的请求。因为服务器启动之后,是不知道客户端什么时候有连接到来的。所以,需要在一个while循环中不断调用这个函数,如果有连接请求到来,那么就会返回一个新的套接字,我们可以通过这个新的套接字进行与客户端的数据通信,如果没有,就只能不断地进行循环,直到有请求到来为止。

注意,在这里我将套接字分为两类,一个是监听套接字,一个是连接套接字。注意这里对两种套接字的区分,在下面的讨论中会用到:

监听套接字:服务器对某个端口进行监听,这个套接字用来表示这个端口($listenSocket)

连接套接字:服务器与客户端已经建立连接,所有的读写操作都要在连接套接字上进行($connSocket)

那么我们对这个服务器进行分析,它存在什么问题呢?

    一个这样的服务器进程只能同时处理一个客户端连接与相关

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值