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

news2025/6/7 13:53:55

一、什么是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.cpp
	g++ -o $@ $^ -std=c++11
tcpserver:Main.cpp
	g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -f tcpserver tcpclient

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

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

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

相关文章

LlamaIndex 工作流简介以及基础工作流

什么是工作流&#xff1f; 工作流是一种由事件驱动、基于步骤的应用程序执行流程控制方式。 你的应用程序被划分为多个称为“步骤&#xff08;Steps&#xff09;”的部分&#xff0c;这些步骤由“事件&#xff08;Events&#xff09;”触发&#xff0c;并且它们自身也会发出事…

如何利用Elastic Stack(ELK)进行安全日志分析

在以下文章中&#xff0c;我将解释如何使用Elastic Stack&#xff08;ELK&#xff09;进行安全日志分析&#xff0c;以提高安全性和监控网络活动。ELK是一个功能强大的开源日志管理和分析平台&#xff0c;由Elasticsearch、Logstash和Kibana组成&#xff0c;适用于各种用例&…

创客匠人:以 AI 利器赋能创始人 IP 打造,加速知识变现新路径

在知识付费与个人 IP 崛起的时代&#xff0c;创客匠人作为行业领先的技术服务商&#xff0c;正通过 AI 工具重构创始人 IP 打造与知识变现的生态。其推出的三大 AI 利器 ——AI 销售信、免训数字人、AI 智能客服&#xff0c;精准解决 IP 运营中的核心痛点。 以 AI 销售信为例&…

Opencv中的copyto函数

一.OpenCV中copyto函数详解 copyto&#xff08;&#xff09;是 OpenCV 中用于图像复制和融合的核心函数&#xff0c;支持灵活的数据复制和掩模&#xff08;Mask&#xff09;操作&#xff0c;其功能和使用方法如下&#xff1a; 1. 核心功能 基础复制&#xff1a;将源图像&…

基于深度强化学习的Scrapy-Redis分布式爬虫动态调度策略研究

在大数据时代&#xff0c;网络数据的采集与分析变得至关重要&#xff0c;分布式爬虫作为高效获取海量数据的工具&#xff0c;被广泛应用于各类场景。然而&#xff0c;传统的爬虫调度策略在面对复杂多变的网络环境和动态的抓取需求时&#xff0c;往往存在效率低下、资源浪费等问…

在 Ubuntu 24.04 LTS 上安装 Jenkins 并配置全局工具(Git、JDK、Maven)

在 Ubuntu 24.04 LTS 上安装 Jenkins 并配置全局工具(Git、JDK、Maven) Jenkins 是一款开源的持续集成和持续交付(CI/CD)工具,在 DevOps 实践中有着广泛的应用。本文将手把手带你在 Ubuntu 24.04 LTS 系统中完成 Jenkins 的安装,并配置所需的全局工具:Git、JDK 和 Maven…

防爆型断链保护器的应用场景有哪些?

​ ​防爆型断链保护器是一种用于防止链条断裂导致设备损坏或安全事故的装置&#xff0c;尤其适用于存在爆炸风险的工业环境。以下是其主要应用场景&#xff1a; ​ ​1.石油化工行业 在石油化工厂、炼油厂等场所&#xff0c;防爆型断链保护器可用于保护输送设备&#xf…

OPenCV CUDA模块光流------高效地执行光流估计的类BroxOpticalFlow

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::BroxOpticalFlow 是 OpenCV CUDA 模块中实现Brox光流算法的类。该类用于在 GPU 上高效地计算两帧图像之间的稠密光流&#xff08;Dens…

K8S认证|CKS题库+答案| 3. 默认网络策略

目录 3. 默认网络策略 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、官网找模板 3&#xff09;、按照题目要求创建networkpolicy 4&#xff09;、应用networkpolicy 5&#xff09;、检查策略 3. 默认网络策略…

Linux编程:1、文件编程

一、Linux 文件编程与 C 语言文件编程的区别 特性C 语言 I/O 库函数Linux 文件编程&#xff08;系统调用&#xff09;实现层面用户空间&#xff08;glibc 库&#xff09;内核空间&#xff08;系统调用&#xff09;跨平台性跨平台&#xff08;Windows/Linux&#xff09;仅限 Li…

Kyosan K5BMC ELECTRONIC INTERLOCKING MANUAL 电子联锁

Kyosan K5BMC ELECTRONIC INTERLOCKING MANUAL 电子联锁

【Spark征服之路-2.3-Spark运行架构】

运行架构 Spark 框架的核心是一个计算引擎&#xff0c;整体来说&#xff0c;它采用了标准 master-slave 的结构。 如下图所示&#xff0c;它展示了一个 Spark 执行时的基本结构。图形中的 Driver 表示 master&#xff0c;负责管理整个集群中的作业任务调度。图形中的 Executor …

Cilium动手实验室: 精通之旅---4.Cilium Gateway API - Lab

Cilium动手实验室: 精通之旅---4.Cilium Gateway API - Lab 1. 环境准备2. API 网关--HTTP2.1 部署应用2.2 部署网关2.3 HTTP路径匹配2.4 HTTP头匹配 3. API网关--HTTPS3.1 创建TLS证书和私钥3.2 部署HTTPS网关3.3 HTTPS请求测试 4. API网关--TLS 路由4.1 部署应用4.2 部署网关…

Java转Go日记(六十):gin其他常用知识

1. 日志文件 package mainimport ("io""os""github.com/gin-gonic/gin" )func main() {gin.DisableConsoleColor()// Logging to a file.f, _ : os.Create("gin.log")gin.DefaultWriter io.MultiWriter(f)// 如果需要同时将日志写入…

89.实现添加收藏的功能的后端实现

实现完查看收藏列表之后&#xff0c;实现的是添加收藏的功能 我的设想是&#xff1a;在对话界面中&#xff0c;如果用户认为AI的回答非常好&#xff0c;可以通过点击该回答对应的气泡中的图标&#xff0c;对该内容进行添加 所以后端实现为&#xff1a; service类中添加&…

v1.0.1版本更新·2025年5月22日发布-优雅草星云物联网AI智控系统

v1.0.1版本更新2025年5月22日发布-优雅草星云物联网AI智控系统 开源地址 星云智控官网&#xff1a; 优雅草星云物联网AI智控软件-移动端vue: 优雅草星云物联网AI智控软件-移动端vue 星云智控PC端开源&#xff1a; 优雅草星云物联网AI智控软件-PC端vue: 优雅草星云物联网AI…

如何创造出一种不同于程序语言的人与机器自然交互语言?

人机交互自然语言通过模拟人类日常交流方式&#xff0c;使机器能够理解并响应人类的自然表达&#xff0c;从而打破编程语言的复杂性壁垒&#xff0c;极大地提升人机协同的效率和自然性&#xff0c;让机器更好地融入人类的工作与生活场景。创造一种通用的人与机器自然交互语言是…

宝塔think PHP8 安装使用FFmpeg 视频上传

宝塔think PHP8 安装使用FFmpeg 一、 安装think PHP8二、安装 FFmpeg1&#xff0c;登录到宝塔面板。2&#xff0c;进入“软件商店”。3&#xff0c;搜索“FFmpeg”。4&#xff0c;选择版本点击安装。5&#xff0c;检查 FFmpeg 是否安装成功6&#xff0c; 在 ThinkPHP 8 中使用 …

26.【新型数据架构】-零ETL架构

26.【新型数据架构】-零ETL架构:减少数据移动,原系统直接分析;典型实现(AWS Zero-ETL) 一、零ETL的本质:从“数据搬运工”到“数据翻译官” 传统ETL(Extract-Transform-Load)需要将数据从源系统抽取、清洗、转换后加载到目标系统,这一过程往往耗时费力,且面临数据延…

静态相机中的 CCD和CMOS的区别

文章目录 CCD处理方式CMOS处理方式两者区别 首先根据 成像原理&#xff0c;CCD和CMOS的作用是一致的&#xff0c;都是为了将光子转化为数字图像&#xff0c;只是 转换的方式出现差异。 CCD处理方式 获取光子&#xff1a; 在电荷耦合器件&#xff08;CCD&#xff09;传感器中…