手把手写C++服务器(24):socket响应一般框架TCP修改缓冲区内核监听listen最大长度

Posted 沉迷单车的追风少年

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手写C++服务器(24):socket响应一般框架TCP修改缓冲区内核监听listen最大长度相关的知识,希望对你有一定的参考价值。

本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 

前言:本系列文章手把手写C++服务器(15):网络编程入门第一个TCP项目以封装好的网络库为例,重点讲解了如何正确的建立TCP连接,如何正确地销毁TCP连接,如何在安全的时机关闭连接,如何处理丢包问题。本文在上一篇文章手把手写C++服务器(21):Linux socket网络编程入门基础的基础上,从原生的socket角度出发,进一步深入玩转TCP编程。

目录

通过socket监听来自用户的请求——socket响应一般框架

内核监听listen最大长度实验

源代码

编译、运行

查看当前网络状态

TCP修改缓冲区实验

发送端

接收端

编译、运行

参考


通过socket监听来自用户的请求——socket响应一般框架

大部分socket监听的代码都和这个差不多,后面真正放到项目中会补全完成的异常处理和日志系统,但整体的简易框架大体不变了。

这里面包含的知识点有:

  • 创建socket连接的一般流程
  • 文件描述符
  • socket地址的表示方法
  • socket端口复用
  • socket选项

还不清楚的可以看一下前面两篇文章:

#include <sys/socket.h>
#include <netinet/in.h>
/* 创建监听socket文件描述符 */
int listenfd = socket(PF_INET, SOCK_STREAM, 0);
/* 创建监听socket的TCP/IP的IPV4 socket地址 */
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);  /* INADDR_ANY:将套接字绑定到所有可用的接口 */
address.sin_port = htons(port);

int flag = 1;
/* SO_REUSEADDR 允许端口被重复使用 */
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
/* 绑定socket和它的地址 */
ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));  
/* 创建监听队列以存放待处理的客户连接,在这些客户连接被accept()之前 */
ret = listen(listenfd, 5);

内核监听listen最大长度实验

创建监听队列用来存放待处理的客户连接,用backlog参数提示内核监听队列的最大长度。如果监听队列的长度超过了backlog,服务器将不会再受理新的客户连接客户端也将收到错误信息。

源代码

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>

static bool stop = false;

/*SIGTERM信号的处理函数, 触发时结束主程序中的循环*/
static void handle_term(int sig)
{
    stop=true;
}

int main(int argc,char*argv[])
{
    signal(SIGTERM, handle_term);
    // 输入格式
    if(argc <= 3)
    {
        printf("usage:%s ip_address port_number backlog\\n", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);
    int backlog = atoi(argv[3]);
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    assert(sock >= 0);
    /*创建一个IPv4 socket地址*/
    struct sockaddr_in address;
    bzero(&address,sizeof(address));
    address.sin_family=AF_INET;
    inet_pton(AF_INET,ip, &address.sin_addr);
    address.sin_port=htons(port);
    int ret = bind(sock,(struct sockaddr*)&address,sizeof(address));
    //assert(ret != -1);
    // 内核监听队列的最大长度
    ret = listen(sock, backlog);
    assert(ret != -1);
    /*循环等待连接, 直到有SIGTERM信号将它中断*/
    while(!stop)
    {
        sleep(1);
    }
    //关闭socket
    close(sock);
    return 0;
}

编译、运行

g++ -std=c++11 backlog.cpp -o backlog

查看当前网络状态

netstat -nt 

TCP修改缓冲区实验

这个实验主要用于熟悉socket选项。不熟悉的可以参考:手把手写C++服务器(21):Linux socket网络编程入门基础手把手写C++服务器(22):Linux socket网络编程进阶第一弹

我们可以通过SO_RCVBUF和SO_SNDBUF这两个选项设置TCP接受和发送缓冲区的大小。

TCP接收缓冲区和发送缓冲区的最小值是256字节,最大值是2048个字节。确保一个TCP连接有足够的空间来处理拥塞。

可以通过修改内核参数/proc/sys/net/ipv4/tcp_rmem和/proc/sys/net/ipv4/tcp_wmem来强制TCP接收缓冲区和发送缓冲区的大小限制。

发送端

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 512

int main(int argc,char*argv[])
{
    // 正确的输入格式
    if(argc <= 2) {
        printf("usage:%s ip_address port_number send_bufer_size\\n",basename(argv[0]));
        return 1;
    }
    // 转换IP和端口号
    const char*ip = argv[1];
    int port = atoi(argv[2]);
    // 地址格式
    struct sockaddr_in server_address;
    bzero(&server_address,sizeof(server_address));
    server_address.sin_family=AF_INET;
    inet_pton(AF_INET,ip, &server_address.sin_addr);
    server_address.sin_port=htons(port);
    // 创建socket
    int sock=socket(PF_INET,SOCK_STREAM,0);
    assert(sock >= 0);
    // 发送区buffer
    int sendbuf=atoi(argv[3]);
    int len=sizeof(sendbuf);
    /*先设置TCP发送缓冲区的大小, 然后立即读取之*/
    setsockopt(sock, SOL_SOCKET,SO_SNDBUF, &sendbuf, sizeof(sendbuf));
    getsockopt(sock, SOL_SOCKET,SO_SNDBUF, &sendbuf, (socklen_t*)&len);
    printf("the tcp send buffer size after setting is%d\\n", sendbuf);
    // 连接、发送
    if(connect(sock,(struct sockaddr*)&server_address,sizeof(server_address)) != -1) {
        char buffer[BUFFER_SIZE];
        memset(buffer, 'a', BUFFER_SIZE);
        send(sock, buffer, BUFFER_SIZE,0);
    }
    close(sock);
    return 0;
}

接收端

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

// 缓冲区大小
#define BUFFER_SIZE 1024

int main(int argc, char*argv[])
{
    // 正确格式
    if(argc <= 2) {
        printf("usage:%s ip_address port_number recv_buffer_size\\n", basename(argv[0]));
        return 1;
    }
    // IP
    const char*ip = argv[1];
    // 端口号
    int port = atoi(argv[2]);
    // sock地址
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);
    // 创建socket连接
    int sock = socket(PF_INET,SOCK_STREAM,0);
    assert(sock >= 0);
    // 接收区大小
    int recvbuf = atoi(argv[3]);
    int len = sizeof(recvbuf);
    /*先设置TCP接收缓冲区的大小, 然后立即读取之*/
    setsockopt(sock,SOL_SOCKET,SO_RCVBUF, &recvbuf,sizeof(recvbuf));
    getsockopt(sock,SOL_SOCKET,SO_RCVBUF, &recvbuf,(socklen_t*)&len);
    printf("the tcp receive buffer size after settting is%d\\n",recvbuf);
    // 绑定端口
    int ret=bind(sock,(struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);
    // 监听
    ret=listen(sock,5);
    assert(ret != -1);
    // 接受地址
    struct sockaddr_in client;
    socklen_t client_addrlength=sizeof(client);
    int connfd = accept(sock, (struct sockaddr*)&client, &client_addrlength);
    if(connfd < 0) {
        printf("errno is:%d\\n",errno);
    } else {
        char buffer[BUFFER_SIZE];
        memset(buffer,'\\0',BUFFER_SIZE);
        while(recv(connfd, buffer, BUFFER_SIZE-1,0) > 0){}
        close(connfd);
    }
    close(sock);
    return 0;
}

编译、运行

不会编译的赶紧看第二讲和第四讲的教程吧:

https://xduwq.blog.csdn.net/article/details/117307884

https://xduwq.blog.csdn.net/article/details/117429821

参考

以上是关于手把手写C++服务器(24):socket响应一般框架TCP修改缓冲区内核监听listen最大长度的主要内容,如果未能解决你的问题,请参考以下文章

手把手写C++服务器(22):Linux socket网络编程进阶第一弹

手把手写C++服务器(22):Linux socket网络编程进阶第一弹

手把手写C++服务器(38):面试必背!Linux网络socket编程必会十问!

手把手写C++服务器(21):Linux socket网络编程入门基础

手把手写C++服务器(20):网络字节序与主机字节序大端小端与共用体

手把手写C++服务器(36):手撕代码——高并发高QPS技术基石之非阻塞recv万字长文