Linux下的socket编程
概述
下面是一个通用的server端程序源码,用于实现两个client之间的通信。
功能
1、接收user的命令cmd消息,并将cmd消息发送到dev;
2、接收dev的应答ack消息,并将ack消息发送到user;
架构实现
通过6个线程实现。
thread_init_user
用于创建user socket,并创建接收user cmd的线程recv_cmd_from_user 和将ack发送给user的线程send_ack_to_user
recv_cmd_from_user
用于接收user客户端的cmd,并激活send_ack_to_user线程
send_ack_to_user
将user cmd发送到设备
thread_init_dev
用于创建dev socket,并创建将cmd发送给dev的线程send_cmd_to_dev 和接收dev ack的线程recv_ack_from_dev
send_cmd_to_dev
用于将user客户端的cmd 发送到dev客户端
recv_ack_from_dev
用于接收dev客户端的ack,并激活send_ack_to_user
注意要点
客户端断开后,server端相应的线程会收到长度为0的信息,收到此消息后,要退出这个线程,同时通知这个客户端的发送线程退出。
源码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <pthread.h>
#include <termios.h>
#include <string.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/time.h>
#include <dirent.h>
#include <signal.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/tcp.h>
#include <string.h>
#include <stdbool.h>
#include <linux/rtnetlink.h>
#include <netinet/ether.h>
#include <semaphore.h>#define FRAME_LEN_MAX 256static sem_t sem_recv_user;
static sem_t sem_send_dev;
static sem_t sem_recv_ack_start;
static sem_t sem_recv_ack_end;static unsigned char g_cmd_frame[FRAME_LEN_MAX];
static int g_cmd_frame_len;
static unsigned char g_ack_frame[FRAME_LEN_MAX];
static int g_ack_frame_len;static bool g_user_thread_exit;
static bool g_dev_thread_exit;static void *recv_cmd_from_user(void *arg)
{int fd = 0;int ret = 0;if (!arg) {printf("[%s] arg is NULL!\n", __FUNCTION__);return NULL;}fd = *(int *)arg;if (fd < 0) {printf("[%s] invailed arg!\n", __FUNCTION__);return NULL;}printf("[%s] thread run\n", __FUNCTION__);while (1) {ret = recv(fd, g_cmd_frame, FRAME_LEN_MAX, 0);printf("[%s] recv ret:%d!\n", __FUNCTION__, ret);if(ret <= 0) {//当用户客户端断开时,会走这里,本进程退出,同时发送信号量通知另个一线程退出g_user_thread_exit = 1;sem_post(&sem_recv_ack_start);break;}g_cmd_frame_len = ret;sem_post(&sem_recv_user);}close(fd);pthread_exit(NULL);
}static void *send_cmd_to_dev(void *arg)
{int fd = 0;int ret = 0;if (!arg) {printf("[%s] arg is NULL!\n", __FUNCTION__);return NULL;}fd = *(int *)arg;if (fd < 0) {printf("[%s] invailed arg!\n", __FUNCTION__);return NULL;}printf("[%s] thread run\n", __FUNCTION__);while (1) {sem_wait(&sem_recv_user);if(g_dev_thread_exit) {break;}ret = send(fd, g_cmd_frame, g_cmd_frame_len, 0);printf("[%s] send to client:%d ret:%d!\n", __FUNCTION__, fd, ret);if(ret <= 0) {break;}sem_post(&sem_send_dev);}close(fd);pthread_exit(NULL);
}static void *recv_ack_from_dev(void *arg)
{int fd = 0;int ret = 0;if (!arg) {printf("[%s] arg is NULL!\n", __FUNCTION__);return NULL;}fd = *(int *)arg;if (fd < 0) {printf("[%s] invailed arg!\n", __FUNCTION__);return NULL;}printf("[%s] thread run\n", __FUNCTION__);while (1) {sem_wait(&sem_send_dev);//dev客户端主动发送命令会被阻塞,直到user客户端发送命令sem_post(&sem_recv_ack_start);ret = recv(fd, g_ack_frame, FRAME_LEN_MAX);printf("[%s] recv ret:%d!\n", __FUNCTION__, ret);if(ret <= 0) {g_dev_thread_exit = 1;sem_post(&sem_recv_user);break;}g_ack_frame_len = ret;sem_post(&sem_recv_ack_end);}close(fd);pthread_exit(NULL);
}static void *send_ack_to_user(void *arg)
{int fd = 0;int ret = 0;if (!arg) {printf("[%s] arg is NULL!\n", __FUNCTION__);return NULL;}fd = *(int *)arg;if (fd < 0) {printf("[%s] invailed arg!\n", __FUNCTION__);return NULL;}printf("[%s] thread run\n", __FUNCTION__);while (1) {struct timespec ts;sem_wait(&sem_recv_ack_start);if(g_user_thread_exit) {break;}
#if 0//对应答时间有要求,可以打开这里if (clock_gettime(CLOCK_REALTIME, &ts) == -1)printf("[%s] ERROR:get current time failed\n",__FUNCTION__);ts.tv_nsec += 10 * MS_TO_NS;ret = sem_timedwait(&sem_recv_dev_end, &ts);
#elsesem_wait(&sem_recv_dev_end);
#endifret = send(fd, g_ack_frame, g_ack_frame_len);printf("[%s] send to client:%d ret:%d!\n", __FUNCTION__, fd, ret);if (ret <= 0) {break;}}close(fd);pthread_exit(NULL);
}void *thread_init_user(void *arg)
{struct sockaddr_in server_addr;struct sockaddr_in client_addr;pthread_t tid_rx;pthread_t tid_tx;int server_fd = 0;int client_fd = 0;int ret = 0;int addr_size = sizeof(client_addr);int port = *(int *)arg;server_fd = socket(AF_INET, SOCK_STREAM, 0);if(server_fd < 0){printf("[%s] socket failed! port:%d\n", __FUNCTION__, port);return NULL;}printf("[%s] socket sucess! server_fd:%d, port:%d\n", __FUNCTION__,server_fd, port);bzero( &server_addr, sizeof(struct sockaddr_in) );server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr((const char*)IP_ADDR_DFLT);server_addr.sin_port = htons(port);if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {printf("[%s] bind failed!\n", __FUNCTION__);return NULL;}printf("[%s] bind sucess! server_fd:%d\n", __FUNCTION__,server_fd);if (listen(server_fd, 10) == -1) {printf("[%s] listen failed!\n", __FUNCTION__);return NULL;}printf("[%s] listen sucess! server_fd:%d\n\n", __FUNCTION__,server_fd);set_socket_alive(server_fd);while (server_fd != -1) {client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_size);if (client_fd < 0) {printf("[%s] accept failed: %d!\n", __FUNCTION__, client_fd);sleep(1);continue;}g_user_thread_exit = 0;printf("[%s] client: %d connected!\n", __FUNCTION__, client_fd);ret = pthread_create(&tid_rx, NULL, recv_cmd_from_user, &client_fd);if(ret != 0) {continue;}pthread_detach(tid_rx);ret = pthread_create(&tid_tx, NULL, send_ack_to_user, &client_fd);if(ret != 0) {continue;}pthread_detach(tid_tx);}return NULL;
}void *thread_init_dev(void *arg)
{struct sockaddr_in server_addr;struct sockaddr_in client_addr;pthread_t tid_rx;pthread_t tid_tx;int server_fd = 0;int client_fd = 0;int ret = 0;int addr_size = sizeof(client_addr);int port = *(int *)arg;server_fd = socket(AF_INET, SOCK_STREAM, 0);if(server_fd < 0){printf("[%s] socket failed!\n", __FUNCTION__);return NULL;}printf("[%s] socket sucess! server_fd:%d, port:%d\n", __FUNCTION__,server_fd, port);bzero( &server_addr, sizeof(struct sockaddr_in) );server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr((const char*)IP_ADDR_DFLT);server_addr.sin_port = htons(port);if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {printf("[%s] bind failed!\n", __FUNCTION__);return NULL;}printf("[%s] bind sucess! server_fd:%d\n", __FUNCTION__,server_fd);if (listen(server_fd, 10) == -1) {printf("[%s] listen failed!\n", __FUNCTION__);return NULL;}printf("[%s] listen sucess! server_fd:%d\n\n", __FUNCTION__,server_fd);set_socket_alive(server_fd);while (server_fd != -1) {client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_size);if (client_fd < 0) {printf("[%s] accept failed: %d!\n", __FUNCTION__, client_fd);sleep(1);continue;}g_dev_thread_exit = 0;printf("[%s] client: %d connected!\n", __FUNCTION__, client_fd);ret = pthread_create(&tid_rx, NULL, recv_ack_from_dev, &client_fd);if(ret != 0) {continue;}pthread_detach(tid_rx);ret = pthread_create(&tid_tx, NULL, send_cmd_to_dev, &client_fd);if(ret != 0) {continue;}pthread_detach(tid_tx);}return NULL;
}int main(int argc, char *argv[])
{int user_port = PORT_USER;int dev_port = PORT_DEV;pthread_t thread_user;pthread_t thread_dev;pthread_attr_t attr;if(argc >= 2)user_port = auto_convert_0x(argv[1]);if(argc >= 3)dev_port = auto_convert_0x(argv[2]);printf("[%s]user_port:%d, dev_port:%d\n", __FUNCTION__, user_port, dev_port);sem_init(&sem_recv_user,0,0);sem_init(&sem_send_dev,0,0);sem_init(&sem_recv_ack_start,0,0);sem_init(&sem_recv_ack_end,0,0);pthread_attr_init (&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);if (0 != pthread_create(&thread_user, &attr, thread_init_user, (void *)&user_port)) {printf("[%s] thread_user init failed\n", __FUNCTION__);return 0;}sleep(1);if (0 != pthread_create(&thread_dev, &attr, thread_init_dev, (void *)&dev_port)) {printf("[%s] thread_dev init failed\n", __FUNCTION__);return 0;}while(1) {sleep(UINT32_MAX);}return 0;
}
参考文档
linux下的Socket网络编程教程_linux socket 编程示例-CSDN博客
相关文章:

Linux下的socket编程
概述 下面是一个通用的server端程序源码,用于实现两个client之间的通信。 功能 1、接收user的命令cmd消息,并将cmd消息发送到dev; 2、接收dev的应答ack消息,并将ack消息发送到user; 架构实现 通过6个线程实现。 …...

【算法】Floyd多源最短路径算法
目录 一、概念 二、思路 三、代码 一、概念 在前面的学习中,我们已经接触了Dijkstra、Bellman-Ford等单源最短路径算法。但首先我们要知道何为单源最短路径,何为多源最短路径 单源最短路径:从图中选取一点,求这个点到图中其他…...

iOS SmartCodable 替换 HandyJSON 适配记录
前言 HandyJSON群里说建议不要再使用HandyJSON,我最终选择了SmartCodable 来替换,原因如下: 首先按照 SmartCodable 官方教程替换 大概要替换的内容如图: 详细的替换教程请前往:使用SmartCodable 平替 HandyJSON …...

使用 axios 拦截器实现请求和响应的统一处理(附常见面试题)
在现代前端开发中,我们经常需要向服务器发送 HTTP 请求,并根据响应内容做不同的处理。axios 是一个流行的 HTTP 库,提供了 拦截器 功能,可以在请求和响应阶段插入自定义逻辑,这使得我们在处理认证、错误提示等场景时更…...

阿里 Sentinel
1、什么是sentinel? sentinel顾名思义:卫兵;在Redis中叫做哨兵,用于监控主从切换,但是在微服务中叫做流量防卫兵。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定…...

【点云网络】 pointnet 和 pointnet++
这两个网络都是斯坦福大学的一个团队提出的 我先先看一下pointnet的网络架构,这个网络比较经典,是2016年提出的: PointNet 是一个专门用于点云数据处理的神经网络。它的设计目的是直接操作不规则的点云数据,而无需将点云数据转换为规则网格或…...

.net core mvc 控制器中页面跳转
方式一: 在控制器的方法内部结尾使用 return View(); 来打开与方法同名的页面,如: public ActionResult Login() { return View(); } 该写法打开 Login 页面。 方式二: 可以添加参数来显式地指定要跳转的页面࿰…...

大学适合学C语言还是Python?
在大学学习编程时,选择C语言还是Python,这主要取决于你的学习目标、专业需求以及个人兴趣。以下是对两种语言的详细比较,帮助你做出更明智的选择: C语言 优点: 底层编程:C语言是一种底层编程语言&#x…...

跳表原理课堂笔记
课程地址 跳表是一种基于随机化的有序数据结构,它提出是为了赋予有序单链表以 O(logn) 的快速查找和插入的能力 创建 首先在头部创建一个 sentinel 节点,然后在 L1 层采用“抛硬币”的方式来决定 L0 层的指针是否增长到 L1 层 例如上图中,L…...

Windows系统使用OpenSSL生成自签名证书
Nginx服务器添加SSL证书。 要在Windows系统的Nginx Web服务器上使用OpenSSL生成证书,并确保该证书能在局域网内被计算机信任,你可以按照以下详细步骤进行操作: 一、生成证书 下载并安装OpenSSL: 从OpenSSL的官方网站下载适用于Wi…...

定位new的表达式
这里面会涉及内存池,所谓的内存池就是池化技术,让我们使用的更加方便,里面有1.线存池和连接池。 如果想要高频释放内存池,要针对系统有个堆,而堆事针对我们需要的生擒一个特例,和我们家庭里面妈妈给爸爸的…...

矩阵特殊打印方式
小伙伴们大家好,好几天没更新了,主要有个比赛。从今天起继续给大家更新,今天给大家带来一种新的题型:矩阵特殊打印方式。 螺旋打印矩阵 解题思路 首先给大家看一下什么是螺旋方式打印: 就像这样一直转圈圈。 我想大多…...

OCC 拟合的平面转换为有界平面
问题:针对导入的部分面无法获取大小,同时也无法判断点是否在面上。但是OBB可以获取大小 解决方法:通过面拟合转换gp_Pln,然后获取面的内外边,重新修剪生成新的TopoDS_Face 疑问:本人对OCC中各种面的特性不…...

Nginx性能优化的几个方法
文章目录 一 Nginx 配置优化二 缓存利用三 压缩策略四 安全性优化修改配置文件修改 Nginx 源码使用第三方模块 五 监控和日志优化六 系统层面优化七 故障转移优化 小伙伴们平时使用 Nginx 是否有进行过性能优化呢?还是软件装好了就直接使用呢? 今天松哥和…...

Unity性能优化5【物理篇】
1.刚体的碰撞检测属性首选离散型 离散碰撞的缺点是小物体快速移动时,有丢失碰撞的风险。此下拉菜单中,越下面的选项碰撞检测频率越高,性能消耗也显著增加。因此在选择碰撞检测类型时尽量选择离散型。 2.优化碰撞矩阵 合理标记碰撞矩阵可以减…...

我的工具列表
开发工具 名称备注Visual Studio微软开发工具集Visual Studio Code代码编辑器Qt CreatorQt IDEQt Design StudioQt 界面设计器linguistQt 国际化翻译PyCharmPython IDEVMware Workstation Pro虚拟机MATLAB数据计算和仿真Keil单片机 IDENavicat Premium数据库管理MobaXterm远程…...

985研一学习日记 - 2024.11.5
一个人内耗,说明他活在过去;一个人焦虑,说明他活在未来。只有当一个人平静时,他才活在现在。 日常 1、起床6:00 2、健身1.5h 今天练了胸,然后跑了会步,又吃多了,明天少吃点! 3、…...

Vue2 与 Vue3 的区别
Vue.js 作为流行的前端框架,已经经历了多次版本的更新迭代,从 Vue2 到 Vue3 的转变不仅带来了新的功能,也在性能、开发体验等方面作出了显著改进。无论是对于新手还是有经验的开发者,了解这两个版本之间的差异都至关重要。本文将讨…...

虚拟现实技术课程开发思路
文章目录 组队选题立项分工建模说明:场景说明:交互说明: 结语: 前言:最近学弟学妹们反馈水水老师课程开始上强度了。不仅有翻转课堂,还有理论课实验课都要做东西出来。听说理论课是做什么博物馆什么的&…...

triangle_area_calculators库发布
最近将在pip网站上发布triangle_area_calculators库(我编写的python第三方库) triangle_area_calculators库用于计算不同类型及不同已知量的三角形面积 在triangle_area_calculators库中,有一个名为TriangleAreaCalculators的类 可以通过f…...

ClickHouse数据库SSL配置和SSL连接测试
目录 1.Server SSL配置介绍 2.Client SSL访问配置的介绍 3.my测试环境上开启ClickHouse Server SSL配置 & 客户端SSL访问的配置流程 4.附录 1)SSL证书的几种类型 单域名SSL证书 通配符SSL证书 多域名SSL证书 多域名通配符SSL证书 2)单域名…...

云渲染与汽车CGI图像技术优势和劣势
在数字时代,云渲染技术以其独特的优势在汽车CGI图像制作中占据了重要地位。云渲染通过利用云计算的分布式处理能力,将渲染任务分配给云端的服务器集群进行计算,从而实现高效、高质量的渲染效果。 这种技术的优势主要体现在以下几个方面&#…...
信号与噪声分析——第二节:随机变量的统计特征
2.1 单个随机变量的统计特征 随机变量是什么? 当随机变量X的取值个数是有限个的时候,我们称它为离散随机变量。 当随机变量X的取值个数是无限个的时候,我们称它为连续随机变量。 1. 分布函数和概率密度 1.分布函数 分布函数 定义为随机变…...

PHP网络爬虫常见的反爬策略
PHP网络爬虫在抓取数据时,常常会遭遇各种反爬策略。这些策略是网站为了保护自身数据不被恶意爬取而设置的。以下是一些常见的PHP网络爬虫反爬策略: IP限制: 这是最常见的反爬虫技术。通过限制IP的访问,可以有效防止恶意的爬虫攻击…...

java java.util.Scanner设置编码
在Java中,可以通过设置Scanner对象的编码来读取特定编码的输入。 使用Scanner的构造方法时,可以传入一个InputStream对象作为参数来设置编码。例如,如果要设置编码为UTF-8,可以这样写: InputStream inputStream Syst…...

小菜家教平台(二):基于SpringBoot+Vue打造一站式学习管理系统
目录 前言 今日进度 详细过程 一、数据库重构 二、编写登录接口 相关知识点 前言 昨天我们重启了小菜家教平台的开发,创建了新项目并初步进行了配置,今天我们继续。大家要是有需要源码的话可以在评论区跟我说,博客中就不添加源码了~ 今…...

Android AndroidManifest 文件内标签及属性
以下是重新排版后的文章: AndroidManifest 1. <manifest> 它是AndroidManifest.xml文件的根标签,包含了整个应用程序的基本信息,如应用程序的包名、版本代码、版本名称等。所有其他标签几乎都是在manifest标签内部定义的。 示例&…...

修改sql server 数据库的排序规则Chinese_PRC_CI_AS(字符集+排序)
文章目录 引言I 解决方案案例II 知识扩展排序规则SQL SERVER支持的所有排序规则引言 新增sql server 数据库实例的默认排序规则不支持中文存储,导致乱码 解决方案: 修改排序规则为Chinese_PRC_CI_AS 或者 Chinese_PRC_Stroke_CI_AS_WS或者Chinese_PRC_CI_AI_KS_WS 仅对新增…...

【ChatGPT】让ChatGPT在回答中附带参考文献与来源
让ChatGPT在回答中附带参考文献与来源 在撰写内容时,引用参考文献和来源可以增强信息的可信度和权威性。通过引导ChatGPT生成带有参考文献的回答,用户能够获取更可靠的信息和背景资料。本文将探讨如何有效地引导ChatGPT在回答中附带参考文献与来源。 一…...

云计算 在esxi 如何创建磁盘存储
重启启动...