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

前言

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

  • 服务端
    主线程中循环调用accept函数接收新的连接,每收到一个连接之后开启一个接收线程接收对端的发送;可以进行注册回调函数的方式处理收到的数据
    mth_srv.c
/*!
 * \file mth_srv.c
 * \date 2019/10/12 16:47
 *
 * \author locki
 * Contact: user@company.com
 *
 * \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: user@company.com
 *
 * \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: user@company.com
 *
 * \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");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值