Linux --TCP协议实现简单的网络通信(中英翻译)

一、什么是TCP协议

1.1 、TCP是传输层的协议,TCP需要连接,TCP是一种可靠性传输协议,TCP是面向字节流的传输协议;

二、TCPserver端的搭建

2.1、我们最终好实现的效果是

客户端在任何时候都能连接到服务端,然后向服务端发送请求,服务端回应客户端的请求;且当客户端向服务端发送请求连接失败时(断网或者服务器故障),会有一个重新连接的状态,在某个时间段内可以不停尝试重新连接;在这里我们把请求和回应请求设计为一个英文翻译,即客户端发送英文,服务端翻译英文并把结果返回给客户端;

2.2 TCPserver第一步创建套接字
2.3、 准备数据

2.4、绑定

2.5、监听

以上初始化工作完成,接下来服务器运行: 

2.6、获取连接

2.7、让线程池取获取并处理任务

自己实现的简单版的线程池:

#pragma once
// 线程池类实现
#include <iostream>
#include <vector>
#include <queue>
#include <unistd.h>
const static int defaultNum = 5;
struct ThreadInfo
{pthread_t tid_;std::string threadname_;
};
template <class T>
class threadpool
{
public:void lock(){pthread_mutex_lock(&mutex_);}void unlock(){pthread_mutex_unlock(&mutex_);}void wakeup() // 唤醒{pthread_cond_signal(&cond_);}void sleep_t() // 休眠(到条件变量的等待队列里面等){pthread_cond_wait(&cond_, &mutex_);}bool isempty(){return task_queue.empty();}std::string getThreadname(pthread_t tid){for (auto &e : threads_){if (e.tid_ == tid)return e.threadname_;}return "None";}public:void push(const T &task){lock();task_queue.push(task);wakeup();unlock();}static void *HandlerTask(void *args) // 类内成员函数有this指针,会参数不匹配,加static修饰就没有了{threadpool<T> *tp = static_cast<threadpool<T> *>(args);std::string name=tp->getThreadname(pthread_self());while (true){tp->lock();// 1.获取任务while (tp->isempty()) // 防止伪唤醒{tp->sleep_t();}T t =tp->pop();tp->unlock();// 2.消费任务t();}}void start(){int threadcout = threads_.size();for (int i = 0; i < threadcout; i++){// 创建的同时把线程数组里的数据初始化好threads_[i].threadname_ = "thread-" + std::to_string(i + 1);pthread_create(&(threads_[i].tid_), nullptr, HandlerTask, this); // static成员函数不能访问成员变量,只能通过类对象访问}}T pop(){T out = task_queue.front();task_queue.pop();return out;}static threadpool<T> *GetInstacne() // 获取实例{                                   // 如果有多个线程同时进来获取实例呢?如果不上锁会导致对象被实例化多份出来if (tp_ == nullptr)             // 后面进来的大部分线程都会判断失败,不需要继续往获取锁{// 走到这里的只有一小部分线程pthread_mutex_lock(&lock_); // 上锁if (tp_ == nullptr)         // 只有第一次获取单例的线程需要进行条件判断,后续线程进来判断都失败{tp_ = new threadpool<T>();}pthread_mutex_unlock(&lock_); // 解锁}return tp_;}private:std::vector<ThreadInfo> threads_; // 存放线程信息(通过里面的tid找到线程)std::queue<T> task_queue;              // 存放任务的队列pthread_mutex_t mutex_;           // 访问队列的时候需要上锁pthread_cond_t cond_;             // 如果没有任务就去里面等threadpool(const T &) = delete;   // 把一切可构造出第二个对象的成员函数禁掉const threadpool<T> &operator=(const threadpool<T> &) = delete;threadpool(int threadnum = defaultNum): threads_(threadnum){// 初始化锁跟条件变量pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&cond_, nullptr);}~threadpool(){// 销毁锁和条件变量pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&cond_);}static threadpool<T> *tp_;static pthread_mutex_t lock_;
};template <class T> // 静态类成员类外定义
threadpool<T> *threadpool<T>::tp_ = nullptr;template <class T>
pthread_mutex_t threadpool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER; // 如果用全局锁需要对锁进行初始化跟销毁
2.8 把Task任务编写一下,即收发任务
#pragma once
#include <string>
#include"Init.hpp"
Init init;
const int buffersize = 1024;
class Task
{
private:int sock_fd;std::string clientip_;uint16_t clientport_;public:Task(const int &fd, const std::string &ip, const uint16_t &port): sock_fd(fd), clientip_(ip), clientport_(port){}void operator()(){Run();}void Run(){char buffer[buffersize];ssize_t n = read(sock_fd, buffer, sizeof(buffer));if (n > 0){buffer[n] = '\0';std::string message = buffer; // 获取client的信息std::string echo_message = init.translation(message);// 2.写// 把获取到的信息进行转换,输出client想要的信息ssize_t w = write(sock_fd, echo_message.c_str(), echo_message.size());if (w < 0){lg(WARNING, "Write fail!!");}else if (w == 0){lg(WARNING, "Read close!!");}}else if (n < 0){lg(INFO, "Read fail!! client ip is:%s, clien port is:%d",clientip_.c_str(),clientport_);}else{lg(WARNING, "Client close!! fd:%d, client ip: %s, client port: %d",sock_fd,clientip_.c_str(),clientport_);}close(sock_fd);}~Task(){}
};

 

2.9、server的main入口函数
#include "TcpServer.hpp"
#include <memory>
void Usage(std::string proc)
{std::cout<<"\n\rUsage:"<<proc<<" serverport[1024+]"<<std::endl;
}
//server serverport
int main(int argc,char * argv[])
{if(argc!=2){Usage(argv[0]);exit(1);}uint16_t port=std::stoi(argv[1]);lg.Enable(CLASSFILE);std::unique_ptr<TcpServer> ptr(new TcpServer(port));ptr->Init();ptr->Start();return 0;
}

三、客户端的编写

3.1
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#define SIZE 1024
void Usage(std::string proc)
{std::cout << "\n\rUsage:" << proc << " serverip serverport" << std::endl;
}
// client serverip serverport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}uint16_t serverport = std::stoi(argv[2]);std::string serverip = argv[1];// 2.准备数据struct sockaddr_in server;memset(&server, 0, sizeof(server)); // 初始化结构体server.sin_family = AF_INET;server.sin_port = htons(serverport);inet_pton(AF_INET, serverip.c_str(), &server.sin_addr);// 3.OS自动绑定在第一次连接成功时// 4.发起连接while (true){// 1.创建套接字int sock_fd = socket(AF_INET, SOCK_STREAM, 0);if (sock_fd < 0){return -1;}int cnt = 5;bool isconnect = false;do{ssize_t c = connect(sock_fd, (sockaddr *)&server, sizeof(server));if (c<0){cnt--;isconnect = true;std::cout << "Is connect!! time: " << cnt << std::endl;sleep(2);}else{break;}} while (cnt && isconnect);if (cnt == 0){std::cout << "user offline!!" << std::endl;break;}// 走到这里连接成功// 1.写std::cout << "Please enter@ " << std::endl;std::string line;std::getline(std::cin, line);ssize_t w = write(sock_fd, line.c_str(), line.size());if(w<0){std::cout<<"Write fail!!"<<std::endl;}// 2.读char buffer[SIZE];ssize_t r = read(sock_fd, buffer, sizeof(buffer));if (r > 0){buffer[r] = '\0';std::cout << buffer << std::endl;}close(sock_fd);}return 0;
}
3.2、在server中加入守护进程

         

守护进程的实现:

#pragma once
#include <unistd.h>
#include<cstdlib>
#include<signal.h>
#include<string>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>std:: string nullfile="/dev/null";
void Deamon(const std::string& cwd="")
{//1.忽略其他异常信号signal(SIGSTOP,SIG_IGN);signal(SIGPIPE,SIG_IGN);signal(SIGCLD,SIG_IGN);//2.自成会话if(fork()>0)exit(0);setsid();//3.更改调用进程的工作目录if(cwd.c_str()!=""){chdir(cwd.c_str());}//4.关闭标准输入输出错误流//打开垃圾桶int fd=open(nullfile.c_str(),O_RDONLY);//只读方式打开if(fd>0){//重定向到垃圾桶dup2(fd,0);dup2(fd,1);dup2(fd,2);}}
3.3、翻译服务的实现

        

#pragma oce
#include<unordered_map>
#include<fstream>
#include"log.hpp"
extern Log lg;
std::string filename="dict.txt";
std::string separator=":";
static bool split(const std::string&line,std::string*ptr1,std::string*ptr2)
{auto pos=line.find(separator);if(pos!=std::string::npos){*ptr1=line.substr(0,pos);*ptr2=line.substr(pos+1);return true;}return false;
}
class Init
{private:std::unordered_map<std::string,std::string> dict_;public:Init(){std::ifstream in(filename);if(!in.is_open()){lg(FATAL,"Ifstream open fail!!");exit(1);}std::string line;while(getline(in,line)){std::string ptr1,ptr2;split(line,&ptr1,&ptr2);dict_.insert({ptr1,ptr2});}in.close();}std::string translation(std::string&word){auto iter=dict_.find(word);if(iter!=dict_.end()){return iter->second;}return "Unknow";}~Init(){}
};

其中这个字典集随便找的一些内容:

3.4makefile 编译运行
.PHONY:all
all: tcpclient tcpserver
tcpclient:TcpClient.cppg++ -o $@ $^ -std=c++11
tcpserver:Main.cppg++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f tcpserver tcpclient

服务运行只要运行一次起来后有就会7*24小时在后台跑着:

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

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

相关文章

pc端小卡片功能-原生JavaScript金融信息与节日日历

代码如下 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>金融信息与节日日历</title><…

C语言——获取变量所在地址(uint8和uint32的区别)

前言&#xff1a; 1.使用uint8 *的原因 在C语言中&#xff0c;获取或操作一个4字节地址&#xff08;指针&#xff09;时使用uint8_t*&#xff08;即unsigned char*&#xff09;而不是uint32_t*&#xff0c;主要基于以下关键原因&#xff1a; 1.1. 避免违反严格别名规则&…

Python----目标检测(《YOLOv3:AnIncrementalImprovement》和YOLO-V3的原理与网络结构)

一、《YOLOv3:AnIncrementalImprovement》 1.1、基本信息 标题&#xff1a;YOLOv3: An Incremental Improvement 作者&#xff1a;Joseph Redmon, Ali Farhadi 机构&#xff1a;华盛顿大学&#xff08;University of Washington&#xff09; 发表时间&#xff1a;2018年 代…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Form Wave(表单label波动效果)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— FormWave组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ &#x1f3af; 组件目标 构建一个美观、动态的登录表单&#xff0…

【数据结构】--二叉树--堆(上)

一、树的概念和结构 概念&#xff1a; 树是一种非线性的数据结构&#xff0c;他是由n(n>0)个有限结点组成一个具有层次关系的集合。其叫做树&#xff0c;是因为他倒过来看就和一棵树差不多&#xff0c;其实际上是根在上&#xff0c;树枝在下的。 树的特点&#xff1a; 1…

linux有效裁剪视频的方式(基于ffmpeg,不改变分辨率,帧率,视频质量,不需要三方软件)

就是在Linux上使用OBS Studio录制一个讲座或者其他视频&#xff0c;可能总有些时候会多录制一段时间&#xff0c;但是如果使用剪映或者PR这样的工具在导出的时候总需要烦恼导出的格式和参数&#xff0c;比如剪映就不支持mkv格式的导出&#xff0c;导出成mp4格式的视频就会变得很…

SystemVerilog—Interface语法(一)

SystemVerilog中的接口&#xff08;interface&#xff09;是一种用于封装多模块间通信信号和协议的复合结构&#xff0c;可显著提升代码复用性和维护效率。其核心语法和功能如下&#xff1a; 一、接口的基本定义 1. 声明语法 接口通过interface关键字定义&#xff0c;支持信…

android binder(四)binder驱动详解

ref&#xff1a; Android10.0 Binder通信原理(五)-Binder驱动分析_binder: 1203:1453 ioctl 40046210 77004d93f4 return-CSDN博客 https://juejin.cn/post/7214342319347712057#heading-0 第6课第1节_Binder系统_驱动情景分析_数据结构_哔哩哔哩_bilibili

QT/c++航空返修数据智能分析系统

简介 1、区分普通用户和管理员 2、界面精美 3、功能丰富 4、使用cppjieba分词分析数据 5、支持数据导入导出 6、echarts展示图表 效果展示 演示链接 源码获取 int main(){ //非白嫖 printf("&#x1f4e1;:%S","joyfelic"); return 0; }

ToolsSet之:数值提取及批处理

ToolsSet是微软商店中的一款包含数十种实用工具数百种细分功能的工具集合应用&#xff0c;应用基本功能介绍可以查看以下文章&#xff1a; Windows应用ToolsSet介绍https://blog.csdn.net/BinField/article/details/145898264 ToolsSet中Number菜单下的Numeric Batch是一个数…

Ubuntu20.04 LTS 升级Ubuntu22.04LTS 依赖错误 系统崩溃重装 Ubuntu22.04 LTS

服务器系统为PowerEdge R740 BIOS Version 2.10.2 DELL EMC 1、关机 开机时连续按键盘F2 2、System Setup选择第一个 System BIOS 3、System BIOS Setting 选择 Boot Setting 4、System BIOS Setting-Boot Setting 选择 BIOS Boot Settings 5、重启 开启时连续按键盘F11 …

(javaSE)Java数组进阶:数组初始化 数组访问 数组中的jvm 空指针异常

数组的基础 什么是数组呢? 数组指的是一种容器,可以用来存储同种数据类型的多个值 数组的初始化 初始化&#xff1a;就是在内存中,为数组容器开辟空间,并将数据存入容器中的过程。 数组初始化的两种方式&#xff1a;静态初始化&#xff0c;动态初始化 数组的静态初始化 初始化…

支持向量机(SVM)例题

对于图中所示的线性可分的20个样本数据&#xff0c;利用支持向量机进行预测分类&#xff0c;有三个支持向量 A ( 0 , 2 ) A\left(0, 2\right) A(0,2)、 B ( 2 , 0 ) B\left(2, 0\right) B(2,0) 和 C ( − 1 , − 1 ) C\left(-1, -1\right) C(−1,−1)。 求支持向量机分类器的线…

UE特效Niagara性能分析

开启Niagara调试器 开启显示概览 界面显示 &#x1f7e9; 上方绿色面板&#xff1a;Niagara DebugHud 这是 HUD&#xff08;调试视图&#xff09; 模式下的性能统计显示&#xff0c;内容如下&#xff1a; 项目含义SystemFilter: ShockWave_01当前选中的 Niagara 粒子系统名称…

碳中和新路径:铁电液晶屏如何破解高性能与节能矛盾?

一、显示技术困局&#xff1a;当 “高刷” 遭遇 “高耗” 在元宇宙、电竞产业蓬勃发展的当下&#xff0c;显示设备的刷新率与能耗成为行业痛点。传统液晶受 “边缘场效应” 制约&#xff0c;刷新率长期停滞在 300Hz 以下&#xff0c;动态画面拖影问题显著&#xff1b;同时&…

Vue3+SpringBoot全栈开发:从零实现增删改查与分页功能

前言 在现代化Web应用开发中&#xff0c;前后端分离架构已成为主流。本文将详细介绍如何使用Vue3作为前端框架&#xff0c;SpringBoot作为后端框架&#xff0c;实现一套完整的增删改查(CRUD)功能&#xff0c;包含分页查询、条件筛选等企业级特性。 技术栈介绍 前端&#xff1…

IBM 与嘉士伯(Carlsberg)携手推进 SAP S/4HANA 数字化转型,打造啤酒行业新范式

在啤酒酿造拥有悠久传统的同时&#xff0c;嘉士伯也在积极拥抱前沿技术&#xff0c;迈出数字化转型的坚实步伐。2025年&#xff0c;嘉士伯宣布与 IBM 建立多年的合作伙伴关系&#xff0c;在其西欧业务中全面部署 SAP S/4HANA&#xff0c;旨在提升企业的运营效率、敏捷性和创新能…

深度解析 Nginx 配置:从性能优化到 HTTPS 安全实践

引言 Nginx 作为高性能的 Web 服务器和反向代理&#xff0c;其配置灵活性和强大功能备受开发者青睐。本文基于一份生产环境的 Nginx 配置文件&#xff0c;详细拆解其核心配置逻辑&#xff0c;涵盖性能优化、HTTPS 安全配置、反向代理及静态资源处理等关键环节&#xff0c;帮助…

传送文件利器wormhole的使用方法

传送文件利器wormhole的使用方法 wormhole文件传送工具是基于python的一个快捷的传送工具&#xff0c;在安装此工具之前首先要部署好python环境。 安装的过程如下&#xff1a; 1.部署好python 环境 LINUX系统自带PYTHON环境&#xff0c;直接安装即可。 WINDOWS系统需要安装py…

LangChain输出格式化实践:提升测试工程师LLM开发效率的完整指南

引言 在基于LangChain的LLM测试开发中&#xff0c;输出格式化是连接大模型推理能力与自动化测试系统的关键环节。通过结构化输出&#xff08;如JSON&#xff09;&#xff0c;测试工程师可快速将LLM生成的测试用例、缺陷报告等结果对接至CI/CD流水线。本文系统解析LangChain内置…