基于线程池的TCP套接字通信

基于线程池的TCP套接字通信

还是只改变server.cpp

其中main函数, 也就是主线程中的处理流程:

  • 创建监听的套接字
  • 绑定IP和端口
  • 设置监听
  • 创建线程池实例对象
  • 添加监听任务 acceptConn
  • 主线程退出

监听任务函数的处理流程如下:

  • 阻塞等待并接受客户端连接
  • 检测有客户端连接时, 添加通信任务worker
  • 释放资源

通信任务函数的处理流程:

  • 打印客户端的信息
  • 和客户端通信
  • 释放资源

其中添加监听和通信任务函数的时候的参数问题需要创建两个结构体, 具体细节在代码中已经体现出来了

cpp 复制代码
//
// Created by 47468 on 2024/1/22.
//
#include <iostream>
#include "arpa/inet.h"
using namespace std;
#include "cstring"
#include "unistd.h"
#include "pthread.h"
#include "ThreadPool.h"

// 监听任务函数传递给通信任务函数参数时用到的结构体
struct SockInfo{
    int fd; // 通信描述符
    pthread_t tid; // 子线程的线程id
    struct sockaddr_in addr; // 把客户端的ip和端口信息传递给通信任务函数
};

// 主函数传递给监听任务函数参数时用到的结构体
struct PoolInfo{
    ThreadPool* pool; // 主线程中创建的线程池实例
    int fd; // 监听描述符
};

// 函数声明
void acceptConn(void* arg);
void worker(void* arg);


int main() {
    // 1. 创建监听的套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1) {
        perror("socket");
        exit(0);
    }

    // 2. 将socket()返回值和本地的IP端口绑定到一起
    sockaddr_in addr{}; // 初始化列表
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10000);

    // INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址
    // 这个宏可以代表任意一个IP地址
    // 这个宏一般用于本地的绑定操作
    addr.sin_addr.s_addr = INADDR_ANY;

    // inet_pton(AF_INET, "192.168.237.131", &addr.sin_addr.s_addr);

    int ret = bind(lfd, (struct sockaddr *) &addr, sizeof(addr));
    if (ret == -1) {
        perror("bind");
        exit(0);
    }

    // 3. 设置监听
    ret = listen(lfd, 128);
    if (ret == -1) {
        perror("listen");
        exit(0);
    }

    // 创建线程池
    auto* pool = new ThreadPool(3, 10);
    auto* poolInfo = new PoolInfo;
    poolInfo->fd = lfd;
    poolInfo->pool = pool;
    // 添加监听任务
    pool->addTask(Task(acceptConn, poolInfo));
    pthread_exit(nullptr);
}

// 监听任务函数
void acceptConn(void* arg){
    auto* poolInfo = static_cast<PoolInfo*>(arg);
    // 4. 阻塞等待并接受客户端连接
    int len = sizeof(sockaddr);

    while (true) {
        auto* pinfo = new SockInfo;
        pinfo->fd = accept(poolInfo->fd, (struct sockaddr *) &pinfo->addr, (socklen_t *) (&len));
        if (pinfo->fd == -1) {
            perror("accept");
//            exit(0);
            break;
        }
        // 添加通信任务
        poolInfo->pool->addTask(Task(worker, pinfo));
    }
    // 释放资源
    close(poolInfo->fd);
}

// 通信任务函数
void worker(void* arg){
    auto* info = static_cast<SockInfo*>(arg);
    // 打印子线程id
    cout << "子线程id: " << info->tid << endl;
    // 打印客户端的地址信息
    char ip[24] = {0};
    cout << "客户端的ip地址: " << inet_ntop(AF_INET, &info->addr.sin_addr.s_addr, ip, sizeof(ip))
         << ", 端口: " << ntohs(info->addr.sin_port) << endl;

    // 5. 和客户端通信
    while(true){
        // 接收数据
        char buf[1024];
        memset(buf, 0, sizeof(buf));
        auto len = read(info->fd, buf, sizeof(buf));
        if(len > 0){
            cout << "客户端say: " << buf << endl;
            write(info->fd, buf, len);
        } else if (len == 0){
            cout << "客户端断开了连接" << endl;
            break;
        } else {
            perror("read");
            break;
        }
    }
    close(info->fd);
}

客户端的代码和线程池代码在之前的文章里已经有了, 都不变

测试情况:

实现了一台服务器和4个客户端的通信(最多的时候5个线程), 因为我们输入参数中最少三个线程, 所以在最后只删除了两个线程, 留有三个live线程, 其中一个主线程是busy线程, 还有两个线程没事做

相关推荐
mingzhi6117 分钟前
应届生必看 | 毕业第一份工作干销售好不好?
网络·web安全·面试
Linux猿19 分钟前
828华为云征文 | 云服务器Flexus X实例:one-api 部署,支持众多大模型
服务器·大模型·llm·华为云·flexus云服务器x实例·华为云服务器·one-api
白帽黑客cst22 分钟前
网络安全(黑客技术) 最新三个月学习计划
网络·数据结构·windows·学习·安全·web安全·网络安全
网络研究院24 分钟前
网络安全:建筑公司会计软件遭受暴力攻击
网络·安全·研究·漏洞·报告·分析
凯子坚持 c1 小时前
0基础带你入门Linux之使用
linux·运维·服务器
迈威通信1 小时前
从EtherCAT到PROFINET,迈威通信带你解锁工业网络!
网络·自动化·信息与通信
hgdlip1 小时前
电脑ip会因为换了网络改变吗
服务器·网络·tcp/ip·电脑
EterNity_TiMe_1 小时前
【Linux基础IO】深入Linux文件描述符与重定向:解锁高效IO操作的秘密
linux·运维·服务器·学习·性能优化·学习方法
python-码博士1 小时前
Rosetta 一:手把手教你用Linux安装Rosetta(全网最简洁)
linux·运维·服务器
你可以自己看2 小时前
python中函数式编程与高阶函数,装饰器与生成器,异常处理与日志记录以及项目实战
服务器·开发语言·python