欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于多线程多客户端连接的TCP模型

程序员文章站 2024-03-23 10:02:10
...

前言

前面已经写了一篇简单的TCP模型,用于单线程单客户端链接,这种效率最高,但是适用场景也是最小的。今天再测试一番简单的多线程多客户端连接的TCP模型。== 这种模型目前也不常见,因为已经有很多的IO复用模型了 ==。但是基于对循序渐进的尊重,我依旧对这个模型进行了一次实验。

  • 服务端
    主线程中循环调用accept函数接收新的连接,每收到一个连接之后开启一个接收线程接收对端的发送;可以进行注册回调函数的方式处理收到的数据
    mth_srv.c
/*!
 * \file mth_srv.c
 * \date 2019/10/12 16:47
 *
 * \author locki
 * Contact: [email protected]
 *
 * \brief 多线程处理多连接
 *
 * TODO: long description
 *
 * \note
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>

#include "mth_srv.h"

srv_msg_t mgr ;

void* cli_proc(void* argv) {
    cli_node_t* client = (cli_node_t*)argv;

    int len = 0;
    char buf[1024] = { 0 };
    while (len = recv(client->_fd, buf, 1024, 0) > 0) {
        if (mgr._on_recv != NULL)
            mgr._on_recv(client->_fd, buf, len, mgr.hint);
        printf("recv: %s\n", buf);
    }

    // 对端断开;需要将管理中此客户端置为断开
    client->_status = SOCK_DISCON;
    close(client->_fd);
}

int test_mthsrv(int argc, char** argv)
{
    int ret = 0;
    int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    mgr._fd = fd;

    struct sockaddr_in srvaddr = { 0 };
    inet_aton("127.0.0.1", &srvaddr.sin_addr);
    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port = htons(8888);
    if (-1 == bind(fd, (struct sockaddr*)&srvaddr, sizeof(srvaddr))) {
        printf("bind failed errno:%d", errno);
        goto SOCKEXIT;
    }
    mgr._addr = srvaddr;

    if (-1 == (ret = listen(fd, 10))) {
        printf("listen 10 failed;errno:%d\n", errno);
        goto SOCKEXIT;
    }
    printf("listening...\n");

    while (mgr._cli_count < MAX_CLIENT) {
        int clifd = 0;
        struct sockaddr_in cliaddr = { 0 };
        socklen_t len = sizeof(cliaddr);

        if (-1 == (clifd = accept(fd, (struct sockaddr*)&cliaddr, &len))) {
            printf("accept failed:%d\n", errno);
            goto SOCKEXIT;
        }
        printf("new client, %s:%d fd = %d", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), clifd);
        // 有新客户端连接;
        cli_node_t* climgr = (cli_node_t*)calloc(1, sizeof(cli_node_t));
        climgr->_fd = clifd;
        climgr->_addr = cliaddr;
        mgr.cli[mgr._cli_count++] = climgr;

        // 启动一个客户端线程
        if (0 == pthread_create(&climgr->_thread_id, NULL, cli_proc, climgr)) {
            printf("new thread created\n");
        }
    }

    // 客户端过多;
    printf("client nums is %d ---\n", mgr._cli_count);

SOCKEXIT:
    close(fd);
    return ret;
}

mth_srv.h

/*!
 * \file mth_srv.h
 * \date 2019/10/12 16:55
 *
 * \author locki
 * Contact: [email protected]
 *
 * \brief 多线程socket头文件
 *
 * TODO: long description
 *
 * \note
*/
typedef struct cli_node_s cli_node_t;
typedef struct srv_mgr_s srv_msg_t;

typedef int(*onrecv)(int fd, char* data, int datalen, void* hint);

#define  MAX_CLIENT 100

#define  SOCK_CONN 1
#define  SOCK_DISCON 2 

struct cli_node_s {
    int _fd; /**< 连接fd */
    int _status; /**< 客户端连接状态 */
    struct sockaddr_in _addr; /**<当前客户端信息 */
    pthread_t _thread_id; /**< 当前线程ID */

};

struct srv_mgr_s {
    int _fd; /**< 服务端fd */
    int _cli_count; /**< 客户端数量 */
    cli_node_t* cli[MAX_CLIENT]; /**< 客户端信息 */
    struct sockaddr_in _addr; /**<当前服务端端信息 */
    onrecv _on_recv; /**< 接收回调 */
    void* hint;
};
#pragma once
  • 客户端
    客户端开启多个连接,然后使用每个连接发送数据到客户端
    mth_cli.c
/*!
 * \file mth_cli.c
 * \date 2019/10/14 19:53
 *
 * \author ycd
 * Contact: [email protected]
 *
 * \brief 多连接测试客户端
 *
 * TODO: long description
 *
 * \note
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int test_mthcli(int argc, char** argv)
{
    int ret = 0;
    int mthcli[100] = { 0 };
    for (int i = 0; i < 100; i++) {
        mthcli[i] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        struct sockaddr_in addr = { 0 };
        addr.sin_addr.s_addr = inet_addr("192.168.0.195");
        addr.sin_port = htons(8888);
        addr.sin_family = AF_INET;
        ret = connect(mthcli[i], (struct sockaddr*)&addr, sizeof(addr));
        if (ret == -1) {
            printf("connect to host failed, errno:%d\n", errno);
            continue;
        }
        printf("connect to host success\n");
    }
    char buf[32] = { 0 };
    while (1) {
        sprintf(buf, "%d", time(NULL));
        for (int i = 0; i < 100; i++) {
            ret = send(mthcli[i], buf, 32, 0);
            if (ret == -1) {
                printf("send to host failed: id= %d errno = %d\n", i, errno);
            }
        }
        usleep(1000);
        printf("send success\n");
    }
}