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…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...
沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
工厂方法模式和抽象工厂方法模式的battle
1.案例直接上手 在这个案例里面,我们会实现这个普通的工厂方法,并且对比这个普通工厂方法和我们直接创建对象的差别在哪里,为什么需要一个工厂: 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类: 两个发…...
