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

从网络编程基本概念说起

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

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

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

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

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

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

  • listen():告诉内核,将这个套接字当成服务器这种被动实体来看待(服务器是等待客户端连接的被动实体,而内核认为socket()创建的套接字默认是主动实体,所以才需要listen()函数,告诉内核进行主动到被动实体的转换)

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

 【免费订阅,永久学习】学习地址: 

Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂

最简单的单进程服务器

由于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循环中不断调用这个函数,如果有连接请求到来,那么就会返回一个新的套接字,我们可以通过这个新的套接字进行与客户端的数据通信,如果没有,就只能不断地进行循环,直到有请求到来为止。

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

  • 监听套接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值