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

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

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

这个图的思路和分析:我们需要在主线程里面不停的进行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 portstruct 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

#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 portstruct 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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/52815.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Zip压缩包密码忘记了,怎么办?

Zip压缩包设置了密码&#xff0c;解压的时候就需要输入正确对密码才能顺利解压出文件&#xff0c;正常当我们解压文件或者删除密码的时候&#xff0c;虽然方法多&#xff0c;但是都需要输入正确的密码才能完成。忘记密码就无法进行操作。 那么&#xff0c;忘记了zip压缩包的密…

HOT73-柱状图中最大的矩形

leetcode原题链接&#xff1a;柱状图中最大的矩形 题目描述 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&#xff1a;heights [2,1…

8.4一日总结

1.远程仓库的提交方式(免密提交) a.ssh:隧道加密传输协议,一般用来登录远程服务器 b.使用 git clone 仓库名 配置(生成公私钥对) ssh-Keygen [-t rsa -C 邮箱地址] 通过执行上述命令,全程回车,就会在~/.ssh/id_rsa(私钥)和id_rsa.pub(公钥),私钥是必须要保存好的,并不能…

解决Git下载失败太慢

解决Git下载失败太慢 Git 官网下载地址: https://git-scm.com/downloads Windows 下载地址: https://git-scm.com/download/win 用官网的地址下载, 需要从github上下载, 由于国内某些原因, 下载速度缓慢, 还经常失败. 国内用户, 可以通过镜像的方式, 提高下载速度. 阿里镜…

数据库与数据仓库的区别及关系

数据库与数据仓库的区别及关系 数据库数据仓库异同差异联系例子 数据库 数据库是结构化信息或数据的有序集合&#xff0c;一般以电子形式存储在计算机系统中。通常由数据库管理系统 (DBMS) 来控制。它是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集…

蒸散发与植被总初级生产力估算

目标 熟悉蒸散发ET及其组分&#xff08;植被蒸腾Ec、土壤蒸发Es、冠层截留Ei&#xff09;、植被总初级生产力GPP的概念和碳水耦合的基本原理&#xff1b;掌握利用Python与ArcGIS工具进行课程相关的操作&#xff1b;熟练掌握国际上流行的Penman-Monteith模型&#xff0c;并能够…

【雕爷学编程】Arduino动手做(182)---DRV8833双路电机驱动模块2

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

Python开发环境Spyder介绍

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 Spyder简介 Spyder (前身是 Pydee) 是一个强大的交互式 Python 语言开发环境&#xff0c; 提供高级的代码编辑、交互测试、调试等特性&#xff0c;支持包括 Windows、Linux 和 OS X 系统。 &#x1f447; &#x1f44…

Keil MDK环境下FreeModebus移植踩坑记录

Keil MDK环境下FreeModebus移植踩坑记录 文章目录 Keil MDK环境下FreeModebus移植踩坑记录armcc (arm compiler v5)环境实验一&#xff1a;实验二&#xff1a; armclang (arm compiler v6)环境实验一&#xff1a;实验二&#xff1a;实验三&#xff1a;实验四 总结 armcc (arm c…

探索OLED透明屏的参数及其在不同领域的应用

OLED透明屏作为一种创新的显示技术&#xff0c;具有高透明度、色彩鲜艳、观感独特等特点&#xff0c;正逐渐成为各个领域的热门选择。 为帮助您更好地了解和选择适合自己需求的OLED透明屏&#xff0c;尼伽便给您详细介绍一下OLED透明屏的参数&#xff0c;包括屏幕尺寸、分辨率…

JMeter压力测试记录

最近在学习redis解决高并发下导致数据库数据不准确的问题&#xff0c;使用到了一些工具&#xff0c;包括Jmeter&#xff0c;Redis-desktop-manager.。Jmeter用于模拟高并发情景&#xff0c;Redis-desktop-manager是redis数据库的GUI界面。 一、单元测试生成测试数据 1)插入20…

视频监控汇聚平台EasyCVR视频分享页面WebRTC流地址播放不了是什么原因?

开源EasyDarwin视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多…