linux实现网络程序
1️⃣ 在linux下,通过套接字实现服务器和客户端的通信。
2️⃣ 实现单线程、多线程通信。或者实现线程池来通信。
3️⃣ 优化通信,增加守护进程。
有情提醒,类里面默认的函数是内联。内联函数在调用的地方展开,没有函数地址,无法保持和传递。
一 、
1.1实现TCP服务器
创建socket文件描述符(TCP/UDP,客户端+服务端)
int socket(int domain, int type, int protocol);
绑定端口号(TCP/UDP,服务器)
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 (TCP, 客户端
int connect(int sockfd, const struct sockaddr*addr,socklen_t addrlen);
void init(){// 1.创建套接字// 1.1 协议族 1.2 SOCK_STREAM是一个有序、可靠、面向连接 的双向字节流 1.3建立一个流式套接字listenSock_ = socket(PF_INET, SOCK_STREAM, 0);if (listenSock_ < 0){logMessage(FATAL, "socket: %s", strerror(errno));exit(SOCKET_ERR);}logMessage(DEBUG, "socket: %s,%d", strerror(errno), listenSock_);// 2.bind绑定// 2.1填充服务器信息// sockaddr_in是系统封装的一个结构体,// 具体包含了成员变量:sin_family、sin_addr、sin_zerostruct sockaddr_in local; // 用户栈memset(&local, 0, sizeof local);local.sin_family = PF_INET;local.sin_port = htons(port_);// 绑定INADDR_ANY,管理一个套接字不管数据是从哪个网卡过来的,只要是绑定的端口号过来的数据,都可以接收到。// 将一个网络字节序的IP地址(也就是结构体in_addr类型变量)转化为点分十进制的IP地址(字符串)。ip_.empty() ? (local.sin_addr.s_addr = INADDR_ANY) : (inet_aton(ip_.c_str(), &local.sin_addr));// 2.2本地socket信息,写入sock_对应的内核区域if (bind(listenSock_, (const struct sockaddr *)&local, sizeof local) < 0) // 说明绑定错误{logMessage(FATAL, "bind :%s", strerror(errno));exit(BIND_ERR);}logMessage(DEBUG, "bind :%s,%d", strerror(errno), listenSock_);// 3.监听socketif (listen(listenSock_, 5) < 0){logMessage(FATAL, "listen :%s", strerror(errno));exit(LISTEN_ERR);}logMessage(DEBUG, "listen :%s,%d", strerror(errno), listenSock_);// 4.启动线程池tp_ = ThreadPool<Task>::getInstance();}void loop(){// 启动线程池tp_->start();logMessage(DEBUG, "thread pool start success ,thread number : %d", tp_->ThreadNum());// signal(SIGCHLD,SIG_IGN);//基类忽略子类信号while (true){struct sockaddr_in peer;socklen_t len = sizeof(peer);// 4.获取连接,accept的返回值是一个新的socketint serviceSock = accept(listenSock_, (struct sockaddr *)&peer, &len);if (serviceSock < 0){logMessage(WARINING, "accept :%s[%d]", strerror(errno), serviceSock);continue;}// 4.1获取客户端基本信息uint16_t peerPort = ntohs(peer.sin_port); // 将一个16位数由网络字节顺序转换为主机字节顺序std::string peerIp = inet_ntoa(peer.sin_addr);logMessage(DEBUG, "accept :%s | %s[%d],socket fd: %d", strerror(errno), peerIp.c_str(), peerPort, serviceSock);// 5.3 版本 --线程池版本// 5.3.1 构建任务// Task t(serviceSock, peerIp, peerPort, std::bind(&ServerTcp::transService, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// tp_->push(t);//将获取的任务给线程池,目前线程池只有5个线程// 5.3.2// 直接使用回调函数Task t(serviceSock, peerIp, peerPort, execCommand);// Task::callback_t call = execCommand;tp_->push(t);// popen传入命令字符串,让它能以文件的方式读到命令接口}}
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
sockfd,利用系统调用socket()建立的套接字描述符,通过bind()绑定到一个本地地址(一般为服务器的套接字),并且通过listen()一直在监听连接;
addr,指向struct sockaddr的指针,该结构用通讯层服务器对等套接字的地址(一般为客户端地址)填写,返回地址addr的确切格式由套接字的地址类别(比如TCP或UDP)决定;若addr为NULL,没有有效地址填写,这种情况下,addrlen也不使用,应该置为NULL;
addrlen,一个值结果参数,调用函数必须初始化为包含addr所指向结构大小的数值,函数返回时包含对等地址(一般为服务器地址)的实际数值;
上边绑定的时候需要将16位主机字节序转16位网络字节序。还有设置=cockaddr_in的结构体里面有域、协议家族该结构体是系统封装的。就是处理网络字节和主机字节的相互转换。获取连接的时候需要重新将网络字节序转化为主机字节序。accept返回的是一个新的socket
1.2实现UDP客户端
创建socket套接字
connect向服务器发起链接请求
int main(int argc,char *argv[])
{if(argc!=3){Usage(argv[0]);exit(USAGE_ERR);}// ./clientTcp serverIp serverPort//默认argc为1,argv[0]为程序名称。如果输入一个参数,则argc为2std::string serverIp=argv[1];uint16_t serverPort=atoi(argv[2]);//1.创建soketint sock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){std::cerr<<"socket:"<<strerror(errno)<<std::endl;exit(SOCKET_ERR);}//2.CONNECT向服务器发起链接请求//2.1先填充需要连接的远端主机的基本信息struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family=AF_INET;server.sin_port=htons(serverPort);//将一个16位主机字节顺序转换成网络字节顺序inet_aton(serverIp.c_str(),&server.sin_addr);//将一个网络字节序的IP地址(也就是结构体in_addr类型变量)转化为点分十进制的IP地址(字符串)。//2.2发起请求connect自动绑定bindif(connect(sock,(const struct sockaddr *)&server,sizeof(server))!=0){std::cerr<<"connect:"<<strerror(errno)<<std::endl;exit(CONN_ERR);}std::cout<<"inof : connect success: "<<sock<<std::endl;std::string message;while (!quit){message.clear();std::cout<<"请输入你的消息>>>";std::getline(std::cin,message);if(strcasecmp(message.c_str(),"quit")==0){quit=true;}size_t s=write(sock,message.c_str(),message.size());if(s>0){message.resize(1024);ssize_t s=read(sock,(char *)(message.c_str()),1024);if(s>0)message[s]=0;std::cout<<"Server Echo>>>"<<message<<std::endl;}else if(s<=0){break;}}close(sock);return 0;
}
以上代码主要是创建套接字,获取远端信息,端口和ip将主机字节顺序转换为网络字节顺序。使用connect请求连接。
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);参数:
第一个参数:int sockdf:socket文件描述符
第二个参数: const struct sockaddr *addr:传入参数,指定服务器端地址信息,含IP地址和端口号
第三个参数:socklen_t addrlen:传入参数,传入sizeof(addr)大小
返回值:成功: 0失败:-1,设置errno
connect()函数返回后并不进行数据交换。而是要等服务器端 accept 之后才能进行数据交换(read、write)。客户端端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。
二、
我们这边要注意既然是实现服务器与客户端互通。我们提供服务就需要考虑让多人连接单人连接等问题。
以下我们实现单线程和多线程版本。
提供服务:
void transService(int sock,const std::string &clientIp,uint16_t clientPort)
{
assert(sock>=0);
assert(!clientIp.empty());
assert(clientPort>=1024);
char inbuffer[BUFFER_SIZE];while (true){size_t s=read(sock,inbuffer,sizeof(inbuffer)-1);if(s>0){inbuffer[s]='\0';if(strcasecmp(inbuffer,"quit")==0){logMessage(DEBUG,"client quit -- %s[%d]",clientIp.c_str(),clientPort);break;}logMessage(DEBUG,"trans before: %s[%d]>>>%s",clientIp.c_str(),clientPort,inbuffer);//大小写转换for(int i = 0;i<s;i++){if(isalpha(inbuffer[i])&&islower(inbuffer[i])){inbuffer[i]=toupper(inbuffer[i]);}}logMessage(DEBUG,"trans after : %s[%d]>>> %s",clientIp.c_str(),clientPort,inbuffer);write(sock,inbuffer,strlen(inbuffer));}else if(s==0){logMessage(DEBUG,"client quit -- %s[%d]",clientIp.c_str(),clientPort);break;}else{logMessage(DEBUG,"%s[%d] - read:%s",clientIp.c_str(),clientPort,strerror(errno));break;}}close(sock);logMessage(DEBUG,"server close %d done",sock);}
单线程版本:
直接传套接字、ip、端口号
```cpptransService(serviceSock,peerIp,peerPort);
多线程版本:
//5.1 多线程版本pid_t id=fork();if(id==0){ //基类进程close(listenSock_);//又进行了一次fork,让if(fork()>0) exit(0);//子类进程 基类退出--子类还在被系统拿到--回收问题就交给系统回收transService(serviceSock,peerIp,peerPort);exit(0);//子进程退出后会进入僵尸状态}//基类退出close(serviceSock);//获取基类退出后的退出码pid_t ret=waitpid(id,nullptr,0);assert(ret>0);(void)ret;
//多线程会共享文件描述符表
ThreadData td=new ThreadData(peerPort,peerIp,serviceSock,this);
pthread_t tid;
pthread_create(&tid,nullptr,threadRoutine,(void)td);
pthread_detach的使用让每个线程结束后自动释放自己所用的资源。
int serviceSock=accept(listenSock_,(struct sockaddr*)&peer,&len);每个用户连接后需要执行5步。
- ThreadData *td=new ThreadData(peerPort,peerIp,serviceSock,this);这步是为了将主线程把链接信息封装好,然后创建子线程,让子线程去处理这份数据,然后等待下一个链接。
- pthread_create就是完成第一行的创建子进程是执行子进程对应的任务。
线程池版本:
pthread_mutex_init的使用是为了解决在多线程访问共享资源时,多个线程同时对共享资源操作产生的冲突而提出的一种解决方法,在执行时,哪个线程持有互斥锁,并对共享资源进行加锁后,才能对共享资源进行操作,此时其它线程不能对共享资源进行操作。
我们先启动线程池
tp_ = ThreadPool<Task>::getInstance();为每个线程池进行加锁
然后启动线程池
tp_->start();
来了任务就将任务放至任务队列里面,如果没有任务就等待。如果有任务就拿任务并且把任务队列的首部弹出该任务。返回任务。
Task t(serviceSock, peerIp, peerPort, execCommand);tp_->push(t);
调用程序执和被调用函数同时在执行。当被调函数执行完毕后,被调函数会反过来调用某个事先指定函数,以通知调用程序:函数调用结束。这个过程称为回调(Callback)。
void execCommand(int sock, const std::string& clientIp, uint16_t clientPort)
{assert(sock >= 0);assert(!clientIp.empty());assert(clientPort >= 1024);char command[BUFFER_SIZE];while (true){size_t s = read(sock, command, sizeof(command) - 1);if (s > 0){command[s] = '\0';// 这个用户执行command命令logMessage(DEBUG, "[%s:%d] exec [%s] ..done", clientIp.c_str(), clientPort, command);// 考虑安全std::string safe = command;if ((std::string::npos != safe.find("rm")) || (std::string::npos != safe.find("unlink"))){break;}FILE *fp = popen(command, "r");if (fp == nullptr){logMessage(WARINING, "exec %s failed,beacuse: %s", command, strerror(errno));break;}// 遍历目录char line[1024];while (fgets(line, sizeof(line) - 1, fp) != nullptr){write(sock, line, strlen(line));}// //本来要写入dile里但现在写入网络。// dup2(sock,fp->_fileno);// fflush(fp);pclose(fp);logMessage(DEBUG, "[%s:%d] exec [%s]...done", clientIp.c_str(), clientPort, command);}else if (s == 0){logMessage(DEBUG, "client quit -- %s[%d]", clientIp.c_str(), clientPort);break;}else{logMessage(DEBUG, "%s[%d] - read:%s", clientIp.c_str(), clientPort, strerror(errno));break;}}close(sock);logMessage(DEBUG, "server close %d done", sock);
}
对于线程池我的理解还是不行。希望能多多与大家一块交流。
相关文章:

linux实现网络程序
1️⃣ 在linux下,通过套接字实现服务器和客户端的通信。 2️⃣ 实现单线程、多线程通信。或者实现线程池来通信。 3️⃣ 优化通信,增加守护进程。 有情提醒,类里面默认的函数是内联。内联函数在调用的地方展开,没有函数地址&…...

FreeRTOS 队列(二)
文章目录 一、向队列发送消息1. 函数原型(1)函数 xQueueOverwrite()(2)函数 xQueueGenericSend()(3)函数 xQueueSendFromISR()、xQueueSendToBackFromISR()、xQueueSendToFrontFromISR()(4&…...

用python获取当前目录下的创建时间超过3天的所有python文件
直接上代码: import os import datetime print(os.getcwd()) # 获取当前目录下所有的html文件 html_files [] for filename in os.listdir(): if filename.endswith(.py): html_files.append(os.path.join(., filename)) now date…...

第五章 Linux实际操作——用户管理
第五章 Linux实际操作——用户管理 5.1 基本介绍5.2 添加用户5.3 指定、修改密码5.4 删除用户5.5 查询用户信息指令5.6 切换用户5.7 查看当前用户、登录用户5.8 用户组5.9 用户和组相关文件8.9.1/etc/passwd 文件8.9.2/etc/shadow文件8.9.3/etc/group文件 5.1 基本介绍 Linux系…...

悲观锁和乐观锁详细
悲观锁和乐观锁详细 悲观锁 悲观锁就是悲观的思想,他认为数据每一次被访问的时候都会被上锁,所以每次获得锁的时候都会上锁,这样其他线程想要获取这个锁的时候就会被堵塞,要等待上一个线程锁的释放。也就是说这个线程只一次只…...

三谈ChatGPT(ChatGPT可以解决问题的90%)
这是我第三次谈ChatGPT,前两篇主要谈了ChatGPT的概念,之所以火的原因和对人们的影响,以及ChatGPT可能存在的安全风险和将面临的监管问题。这一篇主要讲讲ChatGPT的场景和处理问题的逻辑。 这一次我特意使用了ChatGPT中文网页版体验了一番。并…...

Qt QSet 详解:从底层原理到高级用法
目录标题 引言:QSet的重要性与简介QSet 的常用接口迭代器:遍历Qset 中的元素(Iterators: Traversing Elements in Qset )高级用法:QSet 中的算法与功能(Advanced Usage: Algorithms and Functions in QList…...

Mac Doxygen的使用
Doxygen的使用 安装着Doxygen和Graphviz这两个东西 在源码目录先使用doxygen -g生成一个叫 ‘Doxyfile’ 的Doxygen的配置文件修改配置文件,里面都有介绍各个选项的功能,这里主要修改一下几个: HAVE_DOT YES EXTRACT_ALL YES EXTRACT_PRIVATE YES E…...

FPGA基础代码复用
一、verilog中有关代码复用的语法 1、连接符“{}” {4{1b1}} 或者 {5d6, 5d8} 2、参数(Parameter)型常量定义 parameter 参数名=表达式; 或者 localparam 参数名=表达式; parameter DATA_WIDTH 20; 3、function函数定义 …...

Hbase简介
HBase简介 一、HBase简介 1. HBase简介 (1) apache的顶级项目,hadoop的数据库,分布式、大规模的大数据存储。 HBase是Google的BigTable的开源java版本,建立在hdfs之上的,分布式、列存储、非关系(nosql、key-value&a…...

科海思除COD树脂,大孔树脂,除COD专用树脂
一、产品介绍 Tulsimer A-722 MP具有控制孔径的大孔强碱性Ⅰ型阴离子交换树脂 Tulsimer A-722 MP 是一款具有便于颜色和有机物去除的控制孔径的,专门开发的大孔强碱性Ⅰ型阴离子交换树脂。 Tulsimer A-722 MP(氯型)专门应用于去除COD…...

Qt 多线程 QThread、QThreadPool使用场景
QThread 和 QRunnable 都是 Qt 框架中用于多线程编程的类,它们之间有以下不同点: 继承关系不同 QThread 继承自 QObject 类,而 QRunnable 没有父类。 实现方式不同 QThread 是一个完整的线程实现,包含了线程的创建、启动、停止、…...

如何一招搞定PCB阻焊过孔问题?
PCB阻焊油墨根据固化方式,阻焊油墨有感光显影型的油墨,有热固化的热固油墨,还有UV光固化的UV油墨。而根据板材分类,又有PCB硬板阻焊油墨,FPC软板阻焊油墨,还有铝基板阻焊油墨,铝基板油墨也可以用…...

【代码随想录】刷题Day2
1.左右指针比大小 977. 有序数组的平方 class Solution { public:vector<int> sortedSquares(vector<int>& nums) {vector<int> ret nums;int left 0;int right nums.size()-1;int end nums.size();while(left<right){if(abs(nums[left])>abs…...

Python机器学习、深度学习技术提升气象、海洋、水文领域实践应用
Python是功能强大、免费、开源,实现面向对象的编程语言,在数据处理、科学计算、数学建模、数据挖掘和数据可视化方面具备优异的性能,这些优势使得Python在气象、海洋、地理、气候、水文和生态等地学领域的科研和工程项目中得到广泛应用。可以…...

计及调度经济性的光热电站储热容量配置方法【IEEE30节点】(Matlab代码实现)
💥 💥 💞 💞 欢迎来到本博客 ❤️ ❤️ 💥 💥 🏆 博主优势: 🌞 🌞 🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 …...

“不要放过这个春天”解锁品牌春日宣传新玩法
在万物复苏的春天,人们换新装、踏青等需求蓄势待发,出现了全民消费热情高涨的趋势,让品牌「贩卖春天」的宣传此起彼伏。 品牌洞察到用户的消费需求,打造具有品牌特色的浪漫宣传,如采用春日限定元素、创新春天宣传场景…...

利用GPT2 预测 福彩3d预测
使用GPT2预测福彩3D项目 个人总结彩票数据是随机的,可以预测到1-2个数字,但是有一两位总是随机的 该项目紧做模型学习用,通过该项目熟练模型训练调用生成过程. 福彩3D数据下载 https://www.17500.cn/getData/3d.TXT data数据格式 处理后数据格式 每行 2023 03 08 9 7 3 训…...

类加载过程
基本说明 反射机制是Java实现动态语言的关键,也就是通过反射实现类动态加载。 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强动态加载:运行时加载需要的类,如果运行时不用该类,即使…...

【C/C++】C++11 无序关联容器的诞生背景
文章目录 背景无序关联容器适用场景有序关联容器适用场景 背景 C11 引入了无序关联容器(unordered_map、unordered_set、unordered_multimap 和 unordered_multiset)是为了提供一种高效的元素存储和查找方式。相比于有序关联容器(map、set、…...

h264编码原理
在介绍编码器原理之前首先了解三个制定编码标准的组织: 1.国际电信联盟(ITU-T),这是一个音视频领域非常强的组织,规定了很多标准如h261,h262,h263,h263。h263也就是h264的前身。 2.国际标准化组织(ISO)&…...

网络工程师经常搞混的路由策略和策略路由,两者到底有啥区别?
当涉及到网络路由时,两个术语经常被混淆:策略路由和路由策略。虽然这些术语听起来很相似,但它们实际上有着不同的含义和用途。在本文中,我们将详细介绍这两个术语的区别和应用。 一、路由策略 路由策略是指一组规则,用…...

高精度气象模拟软件WRF实践技术
【原文链接】:高精度气象模拟软件WRF(Weather Research Forecasting)实践技术及案例应用https://mp.weixin.qq.com/s?__bizMzU5NTkyMzcxNw&mid2247538149&idx3&sn3890c3b29f34bcb07678a9dd4b9947b2&chksmfe68938fc91f1a99bbced2113b09cad822711e7f…...

总结827
学习目标: 4月(复习完高数18讲内容,背诵21篇短文,熟词僻义300词基础词) 学习内容: 高等数学:刷1800,做了26道计算题,记录两道错题,搞懂了,但并不…...

还在发愁项目去哪找?软件测试企业级Web自动化测试实战项目
今天给大家分享一个简单易操作的实战项目(已开源) 项目名称 ET开源商场系统 项目描述 ETshop是一个电子商务B2C电商平台系统,功能强大,安全便捷。适合企业及个人快速构建个性化网上商城。 包含PCIOS客户端Adroid客户端微商城…...

总结下Spring boot异步执行逻辑的几种方式
文章目录 概念实现方式Thread说明 Async注解说明 线程池CompletableFuture(Future及FutureTask)创建CompletableFuture异步执行 消息队列 概念 异步执行模式:是指语句在异步执行模式下,各语句执行结束的顺序与语句执行开始的顺序…...

【开发日志】2023.04 ZENO----Composite----CompNormalMap
NormalMap-Online (cpetry.github.io)https://cpetry.github.io/NormalMap-Online/ CompNormalMap 将灰度图像转换为法线贴图 将灰度图像转换为法线贴图是一种常见的技术,用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图&…...

春秋云境:CVE-2022-28525 (文件上传漏洞)
目录 一、题目 1.登录 2.burp抓包改包 3.蚁剑获取flag 一、题目 ED01CMSv20180505存在任意文件上传漏洞 英语不够 翻译来凑: 点击其他页面会Not Found 找不到: 先登录看看吧: 试试万能密码:admin:123 发现错误…...

【软件测试二】开发模型和测试模型,BUG概念篇
目录 1.软件的生命周期 2.瀑布模型 3.螺旋模型 4.增量,迭代 5.敏捷---scrum 1. 敏捷宣言 2.角色 6. 软件测试v模型 7.软件测试w模型 8.软件测试的生命周期 9.如何描述一个BUG 10.如何定义BUG的级别 11.BUG的生命周期 12.产生争执怎么办 1.软件的生命周期…...

短视频app开发:如何实现视频直播功能
短视频源码的实现 在短视频app开发中,实现视频直播功能需要借助短视频源码。短视频源码可以提供一个完整的视频直播功能模块,包括视频采集、编码、推流等。因此,我们可以选择一些开源的短视频源码,例如LFLiveKit、ijkplayer等&am…...