多线程实践(一)——# 基于多线程实现的并发服务器

基于多线程实现的并发服务器

cpp 复制代码
//server
#include <iostream>
#include <cstring>
#include <thread>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

struct SockInfo {
    int fd;      // 通信句柄
    pthread_t pid; // 线程id
    sockaddr_in addr; // 客户端地址
};

SockInfo sock_infos[128];
sockaddr_in server_addr;

int listenfd;

bool InitServer(unsigned int port, unsigned int backlog) {  // backlog为单个线程的最大连接数
    listenfd = socket(AF_INET, SOCK_STREAM, 0);  // 初始化服务端的监听
    if (listenfd < 0) {
        perror("listensocket init failed");
        return false;
    }
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有网卡
    int ret = bind(listenfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 绑定
    if (ret < 0) {
        perror("bind failed");
        return false;
    }
    ret = listen(listenfd, backlog);
    if (ret < 0) {
        perror("listen failed");
        return false;
    }
    return true;
}

void* worker(void* arg) {
    SockInfo* pinfo = (SockInfo*)arg;
    cout<<"child thread, connfd: " << pinfo->fd << endl;
    while (true) {
        char buff[1024];
        memset(buff, 0, sizeof(buff));
        int ret = recv(pinfo->fd, buff, sizeof(buff), 0);
        if (ret == 0) {
            cout << "client disconnected" << endl;
            pinfo->fd = -1;
            break;
        } else if (ret == -1) {
            perror("recv failed");
            break;
        }
        cout << "receive msg: " << buff << endl;
        ret=send(pinfo->fd,buff,strlen(buff),0); 
        if(ret==-1)
        {
            perror("send failed");
            return 0;
        }
        cout<<"send msg: "<<buff<<endl;
    }
    close(pinfo->fd);
    pthread_exit(NULL);
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
        cerr << "Usage: " << argv[0] << " <port>" << endl;
        return EXIT_FAILURE;
    }
    unsigned int port = atoi(argv[1]);
    if (!InitServer(port, 5)) {
        exit(EXIT_FAILURE);
    }

    int max = sizeof(sock_infos) / sizeof(sock_infos[0]);
    for (int i = 0; i < max; i++) {
        memset(&sock_infos[i], 0, sizeof(sock_infos[i]));
        sock_infos[i].fd = -1;
        sock_infos[i].pid = -1;
    }
    
    SockInfo* pinfo;
    while (true) {
        // 创建子线程
        int found = 0;
        for (int i = 0; i < max; i++) {
            if (sock_infos[i].fd == -1) {
                pinfo = &sock_infos[i];
                found = 1;
                break;
            }
        }

        if (!found) {  // 如果没有找到空闲位置
            // 拒绝新的连接请求
            int connfd = accept(listenfd, (struct sockaddr*)&pinfo->addr, (socklen_t*)&(pinfo->addr));
            if (connfd != -1) {
                char error_msg[] = "已达到连接数的最大值\n";
                send(connfd, error_msg, sizeof(error_msg) - 1, 0);  // 发送错误信息
                close(connfd);  // 关闭连接
                continue;
            }
        }

        int connfd = accept(listenfd, (struct sockaddr*)&pinfo->addr, (socklen_t*)&(pinfo->addr));
        if (connfd == -1) {
            perror("accept failed");
            continue;
        }
        pinfo->fd = connfd;
        cout << "parent thread, connfd: " << connfd << endl;

        // 创建线程处理客户端连接
        pthread_create(&pinfo->pid, NULL, worker, pinfo);
        pthread_detach(pinfo->pid);  // 设置线程为分离状态
    }

    close(listenfd);
    return 0;
}
cpp 复制代码
// client
#include <iostream>
#include <cstring>
#include <cstdio>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>


using namespace std;

int main(int argc,char *argv[])
{
    if(argc!=3)
    {
        cout<<"Examples: ./demo1 服务端 端口号"<<endl;
        return -1;
    }

    //创建socket套接字
    int socketid=socket(AF_INET,SOCK_STREAM,0);
    if(socketid==-1)
    {
        perror("socket error");
        return -1;
    }

    //绑定服务端
    struct hostent* h;  //存放服务端IP的数据结构
    if((h=gethostbyname(argv[1]))==0)
    {
        perror("gethostbyname error");
        close(socketid);
        return -1;
    }
    struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sin_family=AF_INET;
    serveraddr.sin_port=htons(atoi(argv[2]));
    if(connect(socketid,(struct sockaddr*)&serveraddr,sizeof(serveraddr))==-1)
    {
        perror("connect error");
        close(socketid);
        return -1;
    }

    //通讯
    int iret;
    char buffer[1024];
    memset(buffer,0,sizeof(buffer));
    for(int i=1;i<10;i++)
    {
        //发送报文
        sprintf(buffer,"这是第%d份报文",i);
        if((iret=send(socketid,buffer,strlen(buffer),0))==-1)
        {
            perror("send error");
            close(socketid);
            break;
        }
        sleep(10);
        cout<<"发送成功"<<endl;

        //接收回应报文
        memset(buffer,0,sizeof(buffer));
        if((iret=recv(socketid,buffer,sizeof(buffer),0))==-1)
        {
            perror("recv error");
            close(socketid);
            break;
        }
        cout<<"接收成功"<<endl;
    }

    //断开连接,释放资源
    close(socketid);
    cout<<"连接已断开"<<endl;
    return 0;
}
```
相关推荐
lihao lihao15 分钟前
C++stack和queue的模拟实现
开发语言·c++
姆路40 分钟前
QT中使用图表之QChart概述
c++·qt
西几1 小时前
代码训练营 day48|LeetCode 300,LeetCode 674,LeetCode 718
c++·算法·leetcode
风清扬_jd1 小时前
Chromium HTML5 新的 Input 类型week对应c++
前端·c++·html5
无际单片机项目实战1 小时前
为什么STM32的HAL库那么难用,ST还是要硬推HAL库?
c语言·stm32·单片机·嵌入式硬件·物联网
南东山人2 小时前
C++静态成员变量需要在类外进行定义和初始化-error LNK2001:无法解析的外部符号
c++
lqqjuly2 小时前
C++ 中回调函数的实现方式-函数指针
开发语言·c++
小卡皮巴拉2 小时前
【力扣刷题实战】相同的树
c语言·算法·leetcode·二叉树·递归
2401_871120352 小时前
数组与指针基础
c++
程序猿阿伟2 小时前
《C++中的魔法:实现类似 Python 的装饰器模式》
java·c++·装饰器模式