C语言基于TCP的多线程服务器

news/2024/4/28 21:00:03/文章来源:https://blog.csdn.net/qq_43689451/article/details/137098540

核心思想:

1 在无限循环中 accpet()后 创建线程

2 预防多线程下的数据竞态:

accept()返回的client_sockfd 是否可以直接填入pthread_create()作为创建线程的参数?

我们观察到 while(1)中并没有阻塞的函数,假设accept()的速度足够快

他会不断地更新client_sockfd的值,而传递给pthread_create()是这个值的地址

也就是说 线程来没来得及启动(没获得cpu时间片) client_sockfd值就被更新了

我们的本意是用每个accept()返回的值,创建一条线程,很显然行为不符合预期

3 解决方案:

为了解决这个问题,最佳做法是为每个接受的连接分配一个新的内存空间来存储它的client_sockfd,并将这块内存的指针传递给新创建的线程。这样,每个线程都有自己独立的client_sockfd副本,不会受到主线程中client_sockfd值改变的影响。

4 扩展:

用同样的思想 扩展传递参数结构体

实现功能:

1 tcp多线程服务器

2 使用信号量计数的计数器

3 测试用客户端

运行注意事项:

1 先开启服务器 

2 根据实际情况修改ip地址及端口

3 手动运行客户端 可以无限次开启观察服务器现象

4 适用于unix-like 且 安装了 gnu_c 的系统 其他酌情改装

服务端:

#define _GNU_SOURCE
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
// 服务器地址
#define SERVER_IP "192.168.142.132"
// 服务器端口
#define SERVER_PORT 50010
// 给线程执行函数传递的参数
struct thread_args
{// 文件描述符int sockfd;// 端口号uint16_t port;// posix信号量sem_t *sem;
};
// 线程执行函数
void *start_routine(void *p)
{// 类型转换struct thread_args ta = *((struct thread_args *)p);// 赋值局部变量sem_t *sem = ta.sem;u_int16_t port = ta.port;int client_sockfd = ta.sockfd;ssize_t send_bytes, recv_bytes;char send_buf[1024] = "How can I help you today ?";char recv_buf[1024] = {0};// 连接数+1sem_post(sem);int sval;// 获取连接数并打印sem_getvalue(sem, &sval);printf("V [+] %u connected : %d\n", port, sval);// 接收数据recv_bytes = recv(client_sockfd, recv_buf, sizeof(recv_buf), 0);if (recv_bytes == -1){perror("recv");}if (recv_bytes == 0){printf("close by peer\n");}if (recv_bytes > 0){printf("Message : %s\n", recv_buf);}// 处理数据sleep(9);// 服务器响应send_bytes = send(client_sockfd, send_buf, strlen(send_buf), 0);if (send_bytes == -1){perror("send");}// 关闭socketif (close(client_sockfd) == -1){perror("close");}// 连接 -1sem_wait(sem);printf("P [-] %u disconnected \n", port);// 释放之前由malloc分配的指针free(p);// 线程退出pthread_exit(NULL);
}
int main()
{int server_sockfd, client_sockfd;struct sockaddr_in server_sockaddr, client_sockaddr;memset(&server_sockaddr, 0, sizeof(server_sockaddr));memset(&client_sockaddr, 0, sizeof(client_sockaddr));socklen_t client_sockaddr_len = sizeof(client_sockaddr);socklen_t server_sockaddr_len = sizeof(server_sockaddr);pthread_t tid;// 信号量 是否存在都先卸载sem_unlink("/sem1");// 新建信号量sem_t *sem = sem_open("/sem1", O_CREAT, 0700, 0);if (sem == SEM_FAILED){perror("sem_open");}// tcp标准流程 socketserver_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (server_sockfd == -1){perror("socket");}// 设置套接字选项:地址端口复用int optval = 1;if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1){perror("setsockopt");}// 绑定服务器地址,recv用server_sockaddr.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP, &server_sockaddr.sin_addr.s_addr);server_sockaddr.sin_family = AF_INET;if ((bind(server_sockfd, (struct sockaddr *)&server_sockaddr, server_sockaddr_len)) == -1){perror("bind");}// 第二个参数backlog:允许排队等待接受的连接数的最大值if ((listen(server_sockfd, 16)) == -1){perror("listen");}printf("listening on : %d\n", SERVER_PORT);// 因为在一个while循环中 主线程永远不会先行结束,设置分离是安全的while (1){// 接收连接并返回client_sockfdclient_sockfd = accept(server_sockfd, (struct sockaddr *)&client_sockaddr, &client_sockaddr_len);if (client_sockfd == -1){perror("accept");continue;}// 为每个参数结构体分配独立内存,预防多线程下的数据竞态struct thread_args *ta_p = (struct thread_args *)malloc(sizeof(struct thread_args));if (ta_p == NULL){return -1;}// 填充ta_p->port = ntohs(client_sockaddr.sin_port);ta_p->sem = sem;ta_p->sockfd = client_sockfd;// 创建线程if (pthread_create(&tid, NULL, start_routine, ta_p)){perror("pthread_create");}// 设置分离if (pthread_detach(tid)){perror("pthread_detach");}}return 0;
}

客户端:

#define _GNU_SOURCE
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>#define SERVER_IP "192.168.142.132"
#define SERVER_PORT 50010int main()
{int client_sockfd;struct sockaddr_in server_sockaddr;memset(&server_sockaddr, 0, sizeof(server_sockaddr));ssize_t send_bytes, recv_bytes;char send_buf[1024] = {"hello server !!!"};char recv_buf[1024] = {0};client_sockfd = socket(AF_INET, SOCK_STREAM, 0);inet_pton(AF_INET, SERVER_IP, &server_sockaddr.sin_addr.s_addr);server_sockaddr.sin_port = htons(SERVER_PORT);server_sockaddr.sin_family = AF_INET;connect(client_sockfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr));send_bytes = send(client_sockfd, send_buf, strlen(send_buf), 0);recv_bytes = recv(client_sockfd, recv_buf, sizeof(recv_buf), 0);printf("%s\n", recv_buf);close(client_sockfd);return 0;
}

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

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

相关文章

基于js css的瀑布流demo

要实现照片按照瀑布流展示&#xff0c;写个小demo&#xff0c;记录下。 瀑布流实现思路如下&#xff1a; CSS 弹性布局对 3 列按横向排列&#xff0c;对每一列内部按纵向排列 html代码&#xff1a; <div class"content"></div> css代码&#xff1a; …

【leetcode】环形链表的约瑟夫问题

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家刷题&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 点击查看题目 首先我们要明确一点&#xff0c;题目要求我们要用环形链表&#xff0c;所以用数组等是不被允…

网站为什么要选择使用安全加速SCDN?

安全加速SCDN&#xff08;安全内容交付网络&#xff09;是一种网络加速服务&#xff0c;旨在提高网站和应用程序的性能和安全性。它使用专门的技术和基础设施来加速内容传输并保护网站免受网络攻击。 安全加速SCDN可以通过内容缓存、快速传输和动态路由技术来加速网站和应用程…

FPGA时钟资源详解(3)——全局时钟资源

FPGA时钟系列文章总览&#xff1a;FPGA原理与结构&#xff08;14&#xff09;——时钟资源https://ztzhang.blog.csdn.net/article/details/132307564 一、概述 全局时钟是 FPGA 中的一种专用互连网络&#xff0c;旨在将时钟信号分配到 FPGA 内各种资源的时钟输入处。这种设计…

【黑马头条】-day04自媒体文章审核-阿里云接口-敏感词分析DFA-图像识别OCR-异步调用MQ

文章目录 day4学习内容自媒体文章自动审核今日内容 1 自媒体文章自动审核1.1 审核流程1.2 内容安全第三方接口1.3 引入阿里云内容安全接口1.3.1 添加依赖1.3.2 导入aliyun模块1.3.3 注入Bean测试 2 app端文章保存接口2.1 表结构说明2.2 分布式id2.2.1 分布式id-技术选型2.2.2 雪…

【全栈小5】我的创作纪念日

目录 前言机缘收获粉丝和原创个人成就六边形战士 回顾文章原代码代码优化 憧憬 前言 全栈小5 &#xff0c;有幸再次遇见你&#xff1a; 还记得 2019 年 03 月 29 日吗&#xff1f; 你撰写了第 1 篇技术博客&#xff1a; 《前端 - 仿动态效果 - 展开信息图标》 在这平凡的一天&…

Matlab之求直角坐标系下两直线的交点坐标

目的&#xff1a;在直角坐标系下&#xff0c;求两个直线的交点坐标 一、函数的参数说明 输入参数&#xff1a; PointA&#xff1a;直线A上的点坐标&#xff1b; AngleA&#xff1a;直线A的倾斜角&#xff0c;单位度&#xff1b; PointB&#xff1a;直线B上的点坐标&#xf…

Git命令及GUI基本操作

不习惯使用Git命令的可移步下面Git GUI基本操作 Git 常用命令 git branch 查看本地所有分支 git status 查看当前状态 git commit 提交 git branch -a 查看所有的分支 git branch -r 查看本地所有分支 git commit -am "init" 提交并且加注释 git remote add orig…

Linux根据时间删除文件或目录

《liunx根据时间删除文件》和 《Linux 根据时间删除文件或者目录》已经讲述了根据时间删除文件或目录的方法。 下面我做一些补充&#xff0c;讲述一个具体例子。以删除/home目录下的文件为例。 首先通过命令&#xff1a; ls -l --time-style"%Y-%m-%d %H:%M:%S"…

上位机图像处理和嵌入式模块部署(qmacvisual几何测量)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 几何测量是图像处理中经常遇到的一个问题&#xff0c;前面我们曾经讨论过点到直线的距离。不仅如此&#xff0c;qmacvisual还提供了另外三个常用的…

RTSP应用:实现视频流的实时推送

在实现实时视频流推送的项目中&#xff0c;RTSP&#xff08;Real Time Streaming Protocol&#xff09;协议扮演着核心角色。本文将指导你通过安装FFmpeg软件&#xff0c;下载并编译live555&#xff0c;以及配置ffmpeg进行视频流推送&#xff0c;来实现一个基本的RTSP流媒体服务…

Spring Boot 防护 XSS + SQL 注入攻击

XSS跨站脚本攻击 ① XSS漏洞介绍 跨站脚本攻击XSS是指攻击者往Web页面里插入恶意Script代码&#xff0c;当用户浏览该页之时&#xff0c;嵌入其中Web里面的Script代码会被解析执行&#xff0c;从而达到恶意攻击用户的目的。XSS攻击针对的是用户层面的攻击&#xff01; ② XSS…

iptables添加端口映射,k8s主机查询不到端口但能访问。

研究原因&#xff1a;k8s内一台主机使用命令查询没有80端口。但通过浏览器访问又能访问到服务。 查询了资料是使用了hostport方式暴露pod端口。cni调用iptables增加了DNAT规则。访问时流量先经过iptables直接被NAT到具体服务去了。 链接: K8s罪魁祸首之"HostPort劫持了我…

单例模式如何保证实例的唯一性

前言 什么是单例模式 指一个类只有一个实例&#xff0c;且该类能自行创建这个实例的一种创建型设计模式。使用目的&#xff1a;确保在整个系统中只能出现类的一个实例&#xff0c;即一个类只有一个对象。对于频繁使用的对象&#xff0c;“忽略”创建时的开销。特点&#xff1a…

快速上手Spring Cloud 七:事件驱动架构与Spring Cloud

快速上手Spring Cloud 一&#xff1a;Spring Cloud 简介 快速上手Spring Cloud 二&#xff1a;核心组件解析 快速上手Spring Cloud 三&#xff1a;API网关深入探索与实战应用 快速上手Spring Cloud 四&#xff1a;微服务治理与安全 快速上手Spring Cloud 五&#xff1a;Spring …

基于PaddleNLP的深度学习对文本自动添加标点符号(二)

前言 基于PaddleNLP的深度学习对文本自动添加标点符号的源码版来了&#xff0c;本篇文章主要讲解如何文本自动添加标点符号的原理和相关训练方法&#xff0c;前一篇文章讲解的是使用paddlepaddle已经训练好的一些模型&#xff0c;在一些简单场景下可以通过这些模型进行预测&…

【Unity】调整Player Settings的Resolution设置无效

【背景】 Build时修改了Player Settings下的Resolution设置&#xff0c;但是再次Building时仍然不生效。 【分析】 明显是沿用了之前的分辨率设定&#xff0c;所以盲猜解决办法是Build相关的缓存文件&#xff0c;或者修改打包名称。 【解决】 实测修改版本号无效&#xf…

指针数组的有趣程序【C语言】

文章目录 指针数组的有趣程序指针数组是什么&#xff1f;指针数组的魅力指针数组的应用示例&#xff1a;命令行计算器有趣的颜色打印 结语 指针数组的有趣程序 在C语言的世界里&#xff0c;指针是一种强大的工具&#xff0c;它不仅能够指向变量&#xff0c;还能指向数组&#…

[机缘参悟-162/管理者与领导者-151] :受害者心态与受害者思维模式,如何克服受害者思维模式,管理者如何管理这种思维模式的人?

目录 一、受害者心态概述 1.1 什么是受害者心态 1.2 受害者心态的表现形式 1.3 受害者心态在职场上的表现 1.4 受害者思维模式 1.5 受害者心态的危害 二、受害者心态的成因 2.1 概述 2.2 神经网络与受害者心态 三、如何克服受害者心态 3.1 概述 3.2 职场 3.3 家庭…

verilog 从入门到看得懂---verilog 的基本语法各种语句

本篇文章主要介绍verilog里面常用的语句&#xff0c; 包括条件语句、循环语句块语句和生成语句。出了块语句和生成语句&#xff0c;其他的基本和c语言或者m语言一致。 1&#xff0c;if 语句&#xff0c;在需要判断逻辑的时候可以使用if语句&#xff0c;如 从输入a&#xff0c;…