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

article/2025/7/4 20:44:07

一、什么是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.hkcw.cn/article/FBTeipbksp.shtml

相关文章

多群组部署

相关概念 星形拓扑和并行多组 如下图&#xff0c;星形组网拓扑和并行多组组网拓扑是区块链应用中使用较广泛的两种组网方式。 星形拓扑&#xff1a;中心机构节点同时属于多个群组&#xff0c;运行多家机构应用&#xff0c;其他每家机构属于不同群组&#xff0c;运行各自应用…

unidbg patch 初探 微博deviceId 案例

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向过程 看了b站迷人瑞信那个由于是…

如何自定义WordPress主题(5个分步教程)

如果您已经安装了一个 WordPress 主题&#xff0c;但它不太适合您&#xff0c;您可能会感到沮丧。在定制 WordPress 主题方面&#xff0c;您有很多选择。 挑战在于找到正确的方法。 在本篇文章中&#xff0c;我将引导您了解自定义 WordPress 主题的各种选项&#xff0c;帮助您…

【兽医处方专用软件】佳易王兽医电子处方软件:高效智能的宠物诊疗管理方案

一、软件概述与核心优势 &#xff08;一&#xff09;试用版获取方式 资源下载路径&#xff1a;进入博主头像主页第一篇文章末尾&#xff0c;点击卡片按钮&#xff1b;或访问左上角博客主页&#xff0c;通过右侧按钮获取详细资料。 说明&#xff1a;下载文件为压缩包&#xff…

【nssctf第三题】[NSSCTF 2022 Spring Recruit]easy C

这是题目&#xff0c;下载附件打开是个C文件 #include <stdio.h> #include <string.h>int main(){char a[]"wwwwwww";char b[]"dvxbQd";//try to find out the flagprintf("please input flag:");scanf(" %s",&a);if…

DAY41 CNN

可以看到即使在深度神经网络情况下&#xff0c;准确率仍旧较差&#xff0c;这是因为特征没有被有效提取----真正重要的是特征的提取和加工过程。MLP把所有的像素全部展平了&#xff08;这是全局的信息&#xff09;&#xff0c;无法布置到局部的信息&#xff0c;所以引入了卷积神…

助力活力生活的饮食营养指南

日常生活中&#xff0c;想要维持良好的身体状态&#xff0c;合理的营养补充至关重要。对于易受身体变化困扰的人群来说&#xff0c;更需要从饮食中摄取充足养分。​ 蛋白质是身体的重要 “建筑材料”&#xff0c;鱼肉、鸡肉、豆类制品富含优质蛋白&#xff0c;易于消化吸收&am…

CA-Net复现

复现结果–Dice&#xff1a;90.093802&#xff0c;Jaccard&#xff1a;82.077802&#xff0c;95HD&#xff1a;6.89387267&#xff0c;ASD&#xff1a;1.76263258&#xff0c;与原文一致 感想 第16篇完全复现的论文

【具身智能】【机械臂】各类机械臂对比

选购指标 选购指标 说明机械-负载1w以内通常200g负载&#xff08;一袋酸奶&#xff09;&#xff0c;1w-5w 1kg负载&#xff08;1L饮料&#xff09;&#xff0c;5w 3kg负载机械-精度越贵精度越高机械-夹爪是否支持更换夹爪等&#xff0c;能否支持力控夹爪机械-AGV扩展 …

云服务器无法远程连接怎么办?

当云服务器无法远程连接&#xff08;比如 SSH、RDP 连接不上&#xff09;时&#xff0c;可以按照以下步骤逐一排查和解决&#xff1a; ✅ 一、检查网络连通性 &#x1f539; 1. 确认公网 IP 是否正确 登录云服务商后台查看分配给服务器的公网 IP&#xff0c;确保你连接的目标地…

PolyGen:一个用于 3D 网格的自回归生成模型 论文阅读

[2002.10880] PolyGen&#xff1a;一个用于 3D 网格的自回归生成模型 --- [2002.10880] PolyGen: An Autoregressive Generative Model of 3D Meshes 图 2&#xff1a;PolyGen 首先生成网格顶点&#xff08;左侧&#xff09;&#xff0c;然后基于这些顶点生成网格面&#xff0…

从 LeetCode 到日志匹配:一行 Swift 实现规则识别

文章目录 摘要描述题解答案题解代码分析示例测试及结果时间复杂度空间复杂度总结 摘要 在开发中我们经常遇到“模式匹配”的问题&#xff0c;比如日志分类、用户意图识别、甚至是在一些权限系统中做规则映射判断。这类问题的本质是判断两个结构是否具有一致的对应关系。LeetCo…

基于Qt的app开发的过渡期

写在前面 这篇博客主要工作是解释和思考&#xff0c;不记录我做项目的过程&#xff0c;因为这篇博客是我要理解其他人的代码&#xff0c;其中涉及到tcp的服务器客户端交互、MySQL、多线程 这部分涉及到计算机网络&#xff0c;是笔者没学的部分&#xff0c;所以对我来说理解它们…

【笔记】如何卸载 MSYS2 中不同工具链的 numpy 包

&#x1f4dd; 笔记&#xff1a;如何卸载 MSYS2 中不同工具链的 numpy 包 &#x1f9f0; 目标说明 本笔记教你如何在 MSYS2 环境中彻底卸载 numpy 包&#xff0c;包括&#xff1a; MINGW64 工具链&#xff08;默认开发环境&#xff09;Clang-x86_64 工具链&#xff08;用于跨…

微型导轨在手术机器人领域中有哪些关键操作?

在微创手术领域&#xff0c;手术机器人凭借其高精度、高稳定性和远程操控能力&#xff0c;正逐步成为现代外科手术的重要工具。微型导轨作为一种专为高精度运动设计的线性导向系统&#xff0c;凭借其亚微米级定位精度、低摩擦运动特性及紧凑结构设计&#xff0c;已成为手术机器…

Qt实现的水波进度条和温度进度条

一.效果 二.原理 1.水波 要模拟波浪,就要首先画出一条波浪线,正弦余弦曲线就很适合。 y=A*sin(ω*x+φ)+k y=A*cos(ω*x+φ)+k 这是正弦余弦曲线的公式,要想实现水波效果,那需要两条曲线,一条曲线的波峰对着另外一条曲线的波谷,要实现这样的曲线效果,只有让正弦曲线前移…

并发编程的源头

1.1. 并发编程的全景图&#xff1a;三个核心问题 1. 分工 —— 提高并发性能的关键 含义&#xff1a;合理分配任务给多个线程&#xff0c;就像项目经理分配工作。目标&#xff1a;提升程序执行效率。实现工具和模式&#xff1a; Java SDK 并发包中的工具&#xff1a; Execut…

【Linux】Git原理与使用

编程不仅是解决问题的艺术&#xff0c;更是对复杂性进行优雅管理的哲学。 前言 这是我自己学习Git工具的笔记。后期我会继续把Git工具笔记开源至博客上。 版本控制器Git 1. Git是一款去中心化的分布式版本控制系统。 2. Git提交时&#xff0c;仅会记录并提交文件的变动部分。 G…

“等待-通知”机制优化(一次性申请)循环等待

1. “等待-通知”机制优化&#xff08;一次性申请&#xff09;循环等待 等待‑通知 释放锁 阻塞 唤醒 重新抢锁 所有 wait/notify 都属于 锁对象 的等待队列。 用 notifyAll()&#xff0c;写成 while(…) wait() —— 黄金法则。 面对自旋消耗 CPU 的场景&#xff0c;优…

麒麟信安安装谷歌浏览器

参考文档 麒麟信安系统Chrome离线安装包&#xff1a;高效便捷的浏览器解决方案-CSDN博客 项目文件预览 - 麒麟信安系统Chrome离线安装包:本仓库提供了一个适用于麒麟信安系统的Chrome浏览器离线安装包。该安装包包含了所有必要的依赖文件&#xff0c;并且已经对系统中已有的依…