《TCP/IP网络编程》阅读笔记--多线程服务器端的实现

目录

1--多线程的优点

2--进程和线程的差异

3--线程创建

4--线程使用

5--线程安全问题

6--互斥量

7--信号量

8--线程销毁

9--多线程并发聊天程序

9-1--服务器端

9-2--客户端

9-3--测试结果


1--多线程的优点

多进程服务器的缺点:

        ① 创建进程的过程会带来一定的开销;

        ② 为了完成进程间的数据交换,需要特殊的 IPC 技术;

        ③ 进程间的上下文切换是创建进程时的最大开销;

多线程的优点:

        ① 线程的创建和上下文切换比进程的创建和上下文切换更快;

        ② 线程间交换数据时无需特殊技术;

2--进程和线程的差异

        每个进程拥有独立的内存空间,拥有自己的数据区、堆区域和栈区域;

        每个线程只拥有自己的栈区域,线程间共享数据区和堆区域;因此线程间上下文切换时不需要切换数据区和堆,可以利用数据区和堆区域交换数据;

        

        进程:在操作系统构成单独执行流的单位;

        线程:在进程构成单独执行流的单位;

        进程是在操作系统内部生成多个执行流,线程就是在同一进程内部创建多条执行流;

3--线程创建

#include <pthread.h>
int pthread_create(pthread_t* restrict thread, const pthread_attr_t* restrict attr, void* (* start_routine)(void*), void* restrict arg);
// 成功时返回 0,失败时返回其他值
// thread 表示保存新创建线程 ID 的变量地址值
// attr 表示用于传递线程属性的参数,传递 NULL 时表示创建默认属性的线程
// start_routine 表示线程的入口函数
// arg 表示传递给线程入口函数的参数
// gcc thread1.c -o thread1 -lpthread
// ./thread1#include <stdio.h>
#include <pthread.h>
#include <unistd.h>void* thread_main(void* arg){ // 线程入口函数int i;int cnt = *((int*)arg);for(i = 0; i < cnt; i++){sleep(1);puts("running thread");}return NULL;
}int main(int argc, char* argv[]){pthread_t t_id;int thread_param = 5;if(pthread_create(&t_id, NULL, thread_main, (void*)&thread_param) != 0){ // 创建线程puts("pthread_create() error");return -1;}sleep(10);puts("end of main");return 0;
}

4--线程使用

        调用 pthread_join(ID) 可以使进程或线程进入等待状态,直到 ID 对应的线程终止为止;

#include <pthread.h>
int pthread_join(pthread_t thread, void** status);
// 成功时返回0,失败时返回其他值
// thread 表示线程ID,只有该线程终止后才会从函数返回
// status 表示保存线程返回值的指针变量地址值 
// gcc thread2.c -o thread2 -lpthread
// ./thread2#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void* thread_main(void *arg){int i;int cnt = *((int*)arg);char* msg = (char*)malloc(sizeof(char)*50);strcpy(msg, "Hello, I'm thread~ \n");for(i = 0; i < cnt; i++){sleep(1);puts("running thread");}return (void*)msg;
}int main(int argc, char* argv[]){pthread_t t_id;int thread_param = 5;void* thr_ret;// 创建线程if(pthread_create(&t_id, NULL, thread_main, (void*)&thread_param) != 0){puts("pthread_create() error");return -1;}// 阻塞,等待线程返回if(pthread_join(t_id, &thr_ret) != 0){puts("pthread_join() error");return -1;}// 打印线程返回值printf("Thread return message: %s \n", (char*)thr_ret);free(thr_ret);return 0;
}

5--线程安全问题

        多个线程同时调用函数执行临界区代码时,会出现问题;

        根据临界区是否引起问题,函数可分为:线程安全函数和非线程安全函数;

        线程安全函数被多个线程同时调用时不会引发问题,非线程安全函数被同时调用时会引发问题;

        以下代码展示了多个线程同时访问临界区代码操作全局变量时会出现意向不到的问题;

// gcc thread4.c -D_REENTRANT -o thread4 -lpthread
// ./thread4#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREAD 100long long num = 0;void* thread_inc(void* arg){int i;for(i = 0; i < 50000000; i++){num += 1;}return 0;
}void* thread_des(void* arg){int i;for(i = 0; i < 50000000; i++){num -= 1;}return 0;
}int main(int argc, char* argv[]){pthread_t thread_id[NUM_THREAD];int i;printf("sizeof long long: %ld \n", sizeof(long long));for(i = 0; i < NUM_THREAD; i++){// 各创建50个线程,分别执行对全局变量 num 的加减操作if(i%2){pthread_create(&(thread_id[i]), NULL, thread_inc, NULL);}else{pthread_create(&(thread_id[i]), NULL, thread_des, NULL);}}for(i = 0; i < NUM_THREAD; i++){pthread_join(thread_id[i], NULL);}printf("result: %lld \n", num);return 0;
}

        正常结果应为0,但实际结果并不是;这就是多线程同时访问临界区,会出现数据竞争等的问题;

        线程访问变量 num 时应阻止其他线程访问,直到一个线程完成运算,这就是同步(Synchronization);线程同步用于解决线程访问顺序引发的问题;

6--互斥量

        互斥量表示不允许多个线程同时访问,主要用于解决线程同步访问的问题;

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr);
int pthread_mutex_destroy(pthread_mutex_t* mutex);
// 成功时返回 0,失败时返回其他值
// mutex 表示创建和销毁互斥量时传递保存和销毁互斥量的变量地址值
// attr 传递即将创建的互斥量属性,没有特别需要指定的属性时传递 NULLint pthread_mutex_lock(pthread_mutex_t* mutex); // 上锁
int pthread_mutex_unlock(pthread_mutex_t* mutex); // 解锁
// 成功时返回 0,失败时返回其他值pthread_mutex_t mutex;
pthread_mutex_lock(&mutex);
// 临界区开始
// ...
// 临界区结束
pthread_mutex_unlock(&mutex);

7--信号量

        利用二进制信号量完成控制线程程序为中心的同步方法;

#include <semaphore.h>
int sem_init(sem_t* sem, int pshared, unsigned int value);
int sem_destroy(sem_t* sem);
// 成功时返回 0,失败时返回其他值
// sem 表示信号量的变量地址值
// pshared 传递其他值时,创建可由多个进程共享的信号量;传递 0 时,创建只允许一个进程内部使用的信号量
// value 表示指定新创建的信号量的初始值int sem_post(sem_t* sem); // 信号量增加1
int sem_wait(sem_t* sem); // 信号量减少1
// 成功时返回 0,失败时返回其他值sem_wait(&sem); // 信号量变为0
// 临界区的开始
// ...
// 临界区的结束
sem_post(&sem); // 信号量变为1

8--线程销毁

线程销毁的 3 种方法:

        ① 调用 main 函数的返回语句;

        ② 调用 pthread_join() 函数;

        ③ 调用 pthread_detach() 函数;

9--多线程并发聊天程序

9-1--服务器端

// gcc chat_server.c -D_REENTRANT -o chat_server -lpthread
// ./chat_server 9190#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>#define BUF_SIZE 100
#define MAX_CLNT 256int clnt_cnt = 0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutx;void send_msg(char* msg, int len){int i;pthread_mutex_lock(&mutx);for(i = 0; i < clnt_cnt; i++){write(clnt_socks[i], msg, len);}pthread_mutex_unlock(&mutx);
}void* handle_clnt(void* arg){int clnt_sock = *((int*)arg);int str_len = 0, i;char msg[BUF_SIZE];while((str_len = read(clnt_sock, msg, sizeof(msg))) != 0){send_msg(msg, str_len);}pthread_mutex_lock(&mutx);for(i = 0; i < clnt_cnt; i++){if(clnt_sock == clnt_socks[i]){while(i++ < clnt_cnt - 1)clnt_socks[i] = clnt_socks[i+1];break;}}clnt_cnt--;pthread_mutex_unlock(&mutx);close(clnt_sock);return NULL;
}void error_handling(char *msg){fputs(msg, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char* argv[]){int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;int clnt_adr_sz;pthread_t t_id;if(argc != 2){printf("Usage: %s <port>\n", argv[0]);exit(1);}pthread_mutex_init(&mutx, NULL);serv_sock = socket(PF_INET, SOCK_STREAM, 0);memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1){error_handling("bind() error");}if(listen(serv_sock, 5) == -1){error_handling("listen() error");}while(1){clnt_adr_sz = sizeof(clnt_adr);clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);pthread_mutex_lock(&mutx);clnt_socks[clnt_cnt++] = clnt_sock;pthread_mutex_unlock(&mutx);pthread_create(&t_id, NULL, handle_clnt, (void*)&clnt_sock);pthread_detach(t_id);printf("Connected client IP: %s \n", inet_ntoa(clnt_adr.sin_addr));}close(serv_sock);return 0;
}

9-2--客户端

// gcc chat_client.c -D_REENTRANT -o chat_client -lpthread
// ./chat_client 127.0.0.1 9190 Yoon#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>#define BUF_SIZE 100
#define NAME_SIZE 20char name[NAME_SIZE] = "[DEFAULT]";
char msg[BUF_SIZE];void* send_msg(void* arg){int sock = *((int*)arg);char name_msg[NAME_SIZE + BUF_SIZE];while(1){fgets(msg, BUF_SIZE, stdin);if(!strcmp(msg, "q\n") || !strcmp(msg, "Q\n")){close(sock);exit(0);}sprintf(name_msg, "%s %s", name, msg);write(sock, name_msg, strlen(name_msg));}return NULL;
}void* recv_msg(void* arg){int sock = *((int*)arg);char name_msg[NAME_SIZE+BUF_SIZE];int str_len;while(1){str_len = read(sock, name_msg, NAME_SIZE+BUF_SIZE-1);if(str_len == -1){return (void*)-1;}name_msg[str_len] = 0;fputs(name_msg, stdout);}return NULL;
}void error_handling(char *msg){fputs(msg, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char* argv[]){int sock;struct sockaddr_in serv_addr;pthread_t snd_thread, rcv_thread;void* thread_return;if(argc != 4){printf("Usage: %s <IP> <port> <name>\n", argv[0]);exit(1);}sprintf(name, "[%s]", argv[3]);sock = socket(PF_INET, SOCK_STREAM, 0);memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port = htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1){error_handling("connect() error!");}pthread_create(&snd_thread, NULL, send_msg, (void*)&sock);pthread_create(&rcv_thread, NULL, recv_msg, (void*)&sock);pthread_join(snd_thread, &thread_return);pthread_join(rcv_thread, &thread_return);close(sock);return 0;
}

9-3--测试结果

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

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

相关文章

Ubuntu 20.04中docker-compose部署Nightingale

lsb_release -r可以看到操作系统版本是20.04&#xff0c;uname -r可以看到内核版本是5.5.19。 sudo apt install -y docker-compose安装docker-compose。 完成之后如下图&#xff1a; cd /opt/n9e/docker/进入到/opt/n9e/docker/里边。 docker-compose up -d进行部署。 …

【LeetCode-中等题】150. 逆波兰表达式求值

文章目录 题目方法一&#xff1a;栈 题目 方法一&#xff1a;栈 class Solution {public int evalRPN(String[] tokens) {Deque<Integer> deque new LinkedList<>();String rpn "-*/";//符号集 用来判断扫描的是否为运算符int sum 0;for(int i 0 ; i…

华为云云耀云服务器L实例评测|centos7.9在线使用cloudShell下载rpm解压包安装mysql并开启远程访问

文章目录 ⭐前言⭐使用华为cloudShell连接远程服务器&#x1f496; 进入华为云耀服务器控制台&#x1f496; 选择cloudShell ⭐安装mysql压缩包&#x1f496; wget下载&#x1f496; tar解压&#x1f496; 安装步骤&#x1f496; 初始化数据库&#x1f496; 修改密码&#x1f4…

【zlm】 webrtc源码讲解

目录 前端WEB 服务器收到请求 服务端的处理 播放 拉流 参考文章 前端WEB 服务器收到请求 POST /index/api/webrtc?applive&streamtest&typeplay HTTP/1.1 HttpSession::onRecvHeaderHttpSession::Handle_Req_POSTHttpSession::Handle_Req_POSTif (totalConte…

科技评估分类

声明 本文是学习GB-T 42776-2023 科技评估分类. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件确立了科技评估的分类原则&#xff0c;描述了分类方法&#xff0c;规定了编码方法、分类与代码。 本文件适用于科技评估的规划、设计、委…

Ganache本地测试网+cpolar内网穿透实现公网访问内网

文章目录 前言1. 本地环境服务搭建2. 局域网测试访问3. 内网穿透3.1 ubuntu本地安装cpolar内网穿透3.2 创建隧道3.3 测试公网访问 4. 配置固定二级子域名4.1 保留一个二级子域名4.2 配置二级子域名4.3 测试访问公网固定二级子域名 前言 网&#xff1a;我们通常说的是互联网&am…

计算机毕设 大数据上海租房数据爬取与分析可视化 -python 数据分析 可视化

# 1 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通…

error:03000086:digital envelope routines::initialization error问题解决

目录 问题描述&#xff1a;error:03000086:digital envelope routines::initialization error 问题原因&#xff1a;nodejs V17 版本发布了 OpenSSL3.0 对算法和秘钥大小增加了更为严格的限制&#xff0c;nodeJs v17 之前版本没影响&#xff0c;但 V17 和之后版本会出现这个错…

短视频矩阵系统源代码开发搭建分享--代码开源SaaS

一、什么是短视频矩阵系统&#xff1f; 短视频矩阵系统是专门为企业号商家、普通号商家提供帐号运营从流量 到转化成交的一站式服务方案&#xff0c;具体包含&#xff1a;点赞关注评论主动私信 &#xff0c;评论区回复&#xff0c;自动潜客户挖掘&#xff0c;矩阵号营销&#x…

Codeforces Round 896 (Div. 1) C. Travel Plan(树形dp+组合数学)

题目 有一棵n(1<n<1e18)个点的树&#xff0c; 点i连着2*i和2*i1两个点&#xff0c;构成一棵完全二叉树 对于每个点i&#xff0c;记其值为a[i]&#xff0c;a[i]可以取[1,m](1<m<1e5)的整数 记i到j的简单路径上的最大值为s[i][j]&#xff0c; 则一棵权值确定的树…

邮件数据安全案例 | 有一种遇见,叫相见恨晚

Mr.赵回忆和联通相遇的时刻&#xff0c;他说&#xff0c;用一句诗来形容恰如其分&#xff0c;“众里寻他千百度&#xff0c;蓦然回首&#xff0c;那人却在&#xff0c;灯火阑珊处” 。 中国联合网络通信集团有限公司在国内31个省&#xff08;自治区、直辖市&#xff09;和境外…

排序算法:归并排序(递归和非递归)

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关排序算法的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通…

【多目标跟踪】 TrackFormer 耗时三天 单句翻译!!!

【多目标跟踪】 TrackFormer 耗时三天 单句翻译&#xff01;&#xff01;&#xff01; TrackFormer: Multi-Object Tracking with Transformers Abstract The challenging task of multi-object tracking (MOT) re-quires simultaneous reasoning about track initiali…

uniappAndroid平台签名证书(.keystore)生成

一、安装JRE环境 https://www.oracle.com/java/technologies/downloads/#java8 记住下载默认安装地址。ps&#xff1a;我都默认安装地址C:\Program Files\Java\jdk-1.8 二、安装成功后配置环境变量 系统变量配置 AVA_HOME 放到环境变量去 %JAVA_HOME%\bin 三、生成签名证书…

本地电脑搭建SFTP服务器,并实现公网访问

本地电脑搭建SFTP服务器&#xff0c;并实现公网访问 文章目录 本地电脑搭建SFTP服务器&#xff0c;并实现公网访问1. 搭建SFTP服务器1.1 下载 freesshd 服务器软件1.3 启动SFTP服务1.4 添加用户1.5 保存所有配置 2. 安装SFTP客户端FileZilla测试2.1 配置一个本地SFTP站点2.2 内…

苹果手机无法正常使用小程序和APP

小程序、APP 已使用了几年&#xff0c;突然大量反馈&#xff1a;苹果手机无法正常使用。但不是全部&#xff0c;只是部分手机。 因为同事苹果手机都能用&#xff0c;所以无法准确判断具体原因。 后来同事苹果手机也无法使用了&#xff0c;显示&#xff1a; 网上搜索结果&…

全国职业技能大赛云计算--高职组赛题卷④(私有云)

全国职业技能大赛云计算--高职组赛题卷④&#xff08;私有云&#xff09; 第一场次题目&#xff1a;OpenStack平台部署与运维任务1 基础运维任务&#xff08;5分&#xff09;任务3 OpenStack云平台运维&#xff08;15分&#xff09;任务4 OpenStack云平台运维开发&#xff08;1…

【xshell7】设置语法高亮(突出显示集)方式

设置高亮方式 增加文件路径 将下面内容起名 以.hls结尾&#xff0c;放入截图位置&#xff0c;然后按照步骤一选择即可 [Keyword_0] TermBackColor1 Underline1 Bold0 Strikeout0 Keyword[_a-z0-9-](.[_a-z0-9-])*[a-z0-9-](.[a-z0-9-])* DescriptionEmail address BackColorIn…

解决jupyter找不到虚拟环境的问题

解决jupyter找不到虚拟环境的问题 使用jupyter只能使用base环境&#xff0c;不能找到自己创建的虚拟环境。如下图&#xff0c;显示的默认的虚拟环境base的地址。 如何解决这个问题&#xff1f;需要两个步骤即可 1 . 在base环境中安装nb_conda_kernels这个库 activate base c…

第9章 【MySQL】InnoDB的表空间

表空间 是一个抽象的概念&#xff0c;对于系统表空间来说&#xff0c;对应着文件系统中一个或多个实际文件&#xff1b;对于每个独立表空间来说&#xff0c;对应着文件系统中一个名为 表名.ibd 的实际文件。大家可以把表空间想象成被切分为许许多多个 页 的池子&#xff0c;当我…