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

这篇具有很好参考价值的文章主要介绍了基于多线程实现服务器并发。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

看大丙老师的B站视频总结的笔记19-基于多线程实现服务器并发分析_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1F64y1U7A2/?p=19&spm_id_from=pageDriver&vd_source=a934d7fc6f47698a29dac90a922ba5a3

思路:首先accept是有一个线程的,另外只要这个accept成功的和一个客户端建立了连接,那么我们就需要创建一个对应的线程,用这个线程和客户端进行网络通信。每建立一个连接,通信的线程就需要创建出来一个。这样的话,能够保证通信的线程和客户端是一个一一对应的关系,也就是说用于通信的线程一共是有n个,用于建立连接的线程只有一个。在线程里边一共分为两类,一类是主线程,一类是子线程,只要是建立了新连接,主线程创建一个子线程,让子线程和对应建立连接的那个客户端去通信就行了。

基于多线程实现服务器并发,服务器,java,运维

这个图的思路和分析:我们需要在主线程里面不停的进行accept操作,如果说有新的客户端连接就建立连接。如果说没有新的客户端连接,主线程就阻塞在accept这个函数上。在主线程里边每创建一个新连接,就需要调用pthread_create创建一个子线程让这个子线程和对应的那个客户端进行网络通信。

考虑细节:多线程之间有哪些资源是共享的?哪些资源是不共享的?

全局和堆区是共享的,他们可以共同访问全局数据区里面的某一块内存或者说堆区里边的某一块内存。如果说有三个线程,那么这个栈区会被分成三份,每个线程都有一块属于自己的独立的栈空间,因此对于多个线程来说,他们并不是共享的。

注意细节:

// 信息结构体
struct SockInfo {
    struct sockaddr_in addr;
    int fd;
};
struct SockInfo infos[512];

把结构体数组里边的每一个元素中的文件描述符设置为-1,这样的话,可以通过这个服务器来判断当前的数组元素是不是被占用的。如果这个数组元素被占用了,它的文件描述符的值应该是一个有效值。如果是-1,是无效值。也就意味着这个元素是空闲的,是可用的

pthread_server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>

// 信息结构体
struct SockInfo {
    struct sockaddr_in addr;
    int fd;
};
struct SockInfo infos[512];

void* working(void* arg);

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

    // 2.绑定本地的IP port
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY; // 0 = 0.0.0.0 对于0来说,大端和小端是没有区别的的,因此不需要转换
    saddr.sin_port = htons(9999);//主机字节序转换成网络字节序
    
    int ret = bind(fd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(ret == -1) {
        perror("bind");
        return -1;
    }

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

    //初始化结构体数组
    int max = sizeof(infos) / sizeof(infos[0]);
    for(int i = 0;i < max; i++) {
        bzero(&infos[i],sizeof(infos[i]));
        infos[i].fd = -1;
        /*
            把结构体数组里边的每一个元素中的文件描述符设置为-1
            这样的话,可以通过这个服务器来判断当前的数组元素是不是被占用的
            如果这个数组元素被占用了,它的文件描述符的值应该是一个有效值
            如果是-1,是无效值。也就意味着这个元素是空闲的,是可用的
        */
    }

    // 4.阻塞并等待客户端的连接
    int addrlen = sizeof(struct sockaddr_in);
    while(1) {
        struct SockInfo* pinfo;
        for(int i = 0;i < max; i++) {
            if(infos[i].fd == -1) {
                pinfo = &infos[i];
                break;
            }
        }

        int cfd = accept(fd,(struct sockaddr*)&pinfo->addr,&addrlen);
        pinfo->fd = cfd;
        if(cfd == -1) {
            perror("accept");
            break;
        }
        // 创建子线程
        pthread_t tid;
        pthread_create(&tid,NULL,working,pinfo);
        pthread_detach(tid);
    }
    // 关闭监听描述符
    close(fd);
    return 0;
}

void* working(void* arg) {
    struct SockInfo* pinfo = (struct SockInfo*)arg;
    // 连接建立成功,打印客户端的IP和端口信息
    char ip[32];
    
    printf("客户端的IP: %s,端口: %d\n",
            inet_ntop(AF_INET,&pinfo->addr.sin_addr.s_addr,ip,sizeof(ip)),
            ntohs(pinfo->addr.sin_port));
    
    // 5.通信
    while(1) {
        // 接收数据
        char buff[1024];
        int len = recv(pinfo->fd,buff,sizeof(buff),0);
        if(len > 0) {
            printf("client say: %s\n",buff);
            send(pinfo->fd,buff,len,0);
        }else if(len == 0) {
            printf("客户端已经断开了连接...\n");
            break;
        }else{
            perror("recv");
            break;
        }
    }
    
    // 关掉文件描述符
    close(pinfo->fd);
    pinfo->fd = -1;
    return NULL;
}

client.c文章来源地址https://www.toymoban.com/news/detail-622629.html

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

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

    // 2.连接服务器IP port
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    inet_pton(AF_INET,"192.168.88.129",&saddr.sin_addr.s_addr);
    int ret = connect(fd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(ret == -1) {
        perror("connect");
        return -1;
    }

    int number = 0;
    // 3.通信
    while(1) {
        // 发送数据
        char buff[1024];
        sprintf(buff,"你好,呵呵哒,%d...\n",number++);
        send(fd,buff,strlen(buff) + 1,0);

        //接收数据
        memset(buff,0,sizeof(buff));
        int len = recv(fd,buff,sizeof(buff),0);
        if(len > 0) {
            printf("server say: %s\n",buff);
        }else if(len == 0) {
            printf("服务器已经断开了连接...\n");
            break;
        }else{
            perror("recv");
        }
        sleep(1);
    }
    // 关闭文件描述符
    close(fd);
    return 0;
}

到了这里,关于基于多线程实现服务器并发的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C/S架构学习之多线程实现TCP并发服务器

    并发概念: 并发是指两个或多个事件在 同一时间间隔 发生; 多线程实现TCP并发服务器的实现流程: 一、创建套接字(socket函数): 通信域选择IPV4网络协议、套接字类型选择流式; 二、填充服务器的网络信息结构体: 1.定义网络信息结构体变量; 2.求出结构体变量的内存

    2024年02月06日
    浏览(43)
  • 多线程并发服务器

    代码: 一、多线程中的newfd,能否修改成全局,为什么? 答:不能修改成全局; 当使用全局变量时,线程中的newfd会发生覆盖; 二、多线程中分支线程的newfd能否不另存,直接用指针间接访问主线程中的newfd,为什么? 答:不能  现象表明,线程中的newfd会发生覆盖,只保留

    2024年02月13日
    浏览(32)
  • 【实战项目】c++实现基于reactor的高并发服务器

    基于Reactor的高并发服务器,分为反应堆模型,多线程,I/O模型,服务器,Http请求和响应五部分 ​全局 Channel 描述了文件描述符以及读写事件,以及对应的读写销毁回调函数,对应存储arg读写回调对应的参数 ​Channel 异或 |:相同为0,异为1 按位与:只有11为1,其它组合全部

    2024年02月12日
    浏览(38)
  • 多线程并发服务器(TCP)

    服务器       客户端     结果    

    2024年02月12日
    浏览(31)
  • Web服务器实现|基于阻塞队列线程池的Http服务器|线程控制|Http协议

    代码地址:WebServer_GitHub_Addr 摘要 本实验通过C++语言,实现了一个基于阻塞队列线程池的多线程Web服务器。该服务器支持通过http协议发送报文,跨主机抓取服务器上特定资源。与此同时,该Web服务器后台通过C++语言,通过原生系统线程调用 pthread.h ,实现了一个 基于阻塞队列

    2024年02月07日
    浏览(52)
  • 【Linux网络编程】高并发服务器框架 线程池介绍+线程池封装

    前言 一、线程池介绍 💻线程池基本概念 💻线程池组成部分 💻线程池工作原理  二、线程池代码封装 🌈main.cpp 🌈ThreadPool.h 🌈ThreadPool.cpp 🌈ChildTask.h  🌈ChildTask.cpp 🌈BaseTask.h 🌈BaseTask.cpp 三、测试效果 四、总结 📌创建线程池的好处 本文主要学习 Linux内核编程 ,结合

    2024年01月16日
    浏览(81)
  • 在QT中使用多线程并发服务器(C++)

    什么是多线程并发服务器?在QT里如何使用多线程并发服务器呢? 多线程并发服务器是一种网络服务器设计,它能够同时处理多个客户端的请求。在多线程服务器中,主线程负责监听和接受来自客户端的连接请求,每当有一个新的连接请求到来时,服务器就会创建一个新的线

    2024年04月11日
    浏览(29)
  • 【高并发服务器 02】——线程池与IO多路复用

    线程池的好处 :所有的池都是为了事先把资源准备好,在后续用的时候可以更加方便的拿到这个资源—— 不用去申请、释放资源 什么时候用线程池 ? IO事务并发较高 :人在杭州,但是数据库在北京,想要查询数据库,需要通过互联网建立TCP三次握手,频繁地创建和销毁线

    2024年03月23日
    浏览(34)
  • 基于linux下的高并发服务器开发(第一章)- 模拟实现 ls-l 命令

     这一小节会用到上面两张图的红色框里面的变量 任务: 模拟实现 ls -l 指令 -rw-rw-r-- 1 nowcoder nowcoder 12 12月  3 15:48 a.txt    

    2024年02月16日
    浏览(32)
  • Linux网络编程:多进程 多线程_并发服务器

    文章目录: 一:wrap常用函数封装 wrap.h  wrap.c server.c封装实现 client.c封装实现 二:多进程process并发服务器 server.c服务器 实现思路 代码逻辑  client.c客户端 三:多线程thread并发服务器 server.c服务器 实现思路 代码逻辑  client.c客户端 ​​​​   read 函数的返回值 wrap.h  wrap

    2024年02月12日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包