网络编程

最简单socket编程

int sockfd = socket(AF_INET, (struct sockaddr*)&serve_addr, sizeof(struct sockaddr));
listen(sockfd, 10);

struct sockaddr_in client;
socklen_t len = sizeof(client);
int connfd = accept(sockfd, (struct sockaddr*)&client, &len);

while (1) {
unsigned char buffer[BUFFERSZ] = {0};
int ret = recv(connfd, buffer, BUFFERSZ, 0);
if (ret == 0) {
shutdown(connfd, SHUT_WR);
}
ret = send(connfd, buffer, ret, 0);
}

问题

  1. 客户端发送时,另外一个客户端不能建立连接
  2. 断开连接时,循环recv
  3. 只能连接一次

解决方案

  • 多线程、多进程

    • 一个请求一个线程
    void *routine(void *arg) {
    int connfd = *(int *)arg;
    while (1) {
    unsigned char buffer[BUFFERSZ] = {0};
    int ret = recv(connfd, buffer, BUFFERSZ, 0);
    if (ret == 0) {
    shutdown(connfd, SHUT_WR);
    }
    ret = send(connfd, buffer, ret, 0);
    }
    }

    while (1) {
    int connfd = accept(sockfd, (struct sockaddr*)&client, &len);
    pthread_t thread_id;
    pthread_create(&thread_id, NULL, routine, (void*)&connfd);
    }
  • IO多路复用

epoll

  • epoll本身在内存足够情况下,可以支持100W并发连接

connection

  • 逻辑概念
  • fd、buffer、callback作为连接的属性,为一对一关系
struct connection {
int fd;
Buffer rbuf;
int rc;
Buffer wbuf;
int wc;
Callback recv_cb;
Callback send_cb;
};

Reactor

为什么需要

  • 需要存储IO未读完的数据
  • 就绪IO占比较少

特点

  • 事件驱动:EPOLLIN/EPOLLOUT
  • 回调:
    • listenfd —-> accept_cb
    • clientfd —-> recv_cb
    • clientfd —-> send_cb

性能

  • 并发量(服务器同时承载的客户端数量)
  • QPS:每秒请求数
  • 最大时延
  • 每秒新建量、建链
  • 吞吐量

TCP系统中信息

net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_mem = 262144 524288 786432 // 单位为页(4K)
net.ipv4.tcp_wmem = 1024 1024 2048 // min 默认 max 大小为byte
net.ipv4.tcp_rmem = 1024 1024 2048
fs.file-max = 1048576

网络库封装

  • 对Reactor模式的Server进行封装;业务层只需要考虑如何操纵Buffer中的数据
  • 网络库内部实现通信机制