Linux网络编程——tcp套接字

文章目录

    • 主要代码
    • 关于构造
    • listen监听
    • accept
    • telnet测试
    • 读取信息
    • 掉线重连
    • 翻译服务器演示

本章Gitee仓库:tcp套接字

主要代码

客户端:

#pragma once

#include"Log.hpp"

#include<iostream>
#include<cstring>

#include<sys/wait.h>
#include<unistd.h>
#include<signal.h>
#include<pthread.h>

#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>

#include"threadPool.hpp"
#include"Task.hpp"

const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5;  //不要设置太大
Log log;


enum{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR,
    LITSEN_ERR
};
class TcpServer;

class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *t)
    :t_sockfd_(fd), t_clientip_(ip), t_clientport_(port), t_tsvr_(t)
    {}
public:
    int t_sockfd_;
    std::string t_clientip_;
    uint16_t t_clientport_;
    TcpServer *t_tsvr_; //需要this指针
};

class TcpServer
{

public:
    TcpServer(const uint16_t &port, const std::string &ip = defaultip)
    :listensockfd_(defaultfd)
    ,port_(port)
    ,ip_(ip)
    {}

    //初始化服务器
    void Init()
    {
        //创建套接字
        listensockfd_ = socket(AF_INET, SOCK_STREAM, 0); //sock_stream提供字节流服务--tcp
        if(listensockfd_ < 0)
        {
            log(Fatal, "create socket, errno: %d, errstring: %s",errno, strerror(errno));
            exit(SOCKET_ERR);
        }

        log(Info, "create socket success, sockfd: %d",listensockfd_);

        int opt = 1;
        setsockopt(listensockfd_, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));  //防止偶发性服务器无法进行立即重启

        //本地套接字信息
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        //填充网络信息
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &(local.sin_addr));

        //bind
        int bd = bind(listensockfd_, (struct sockaddr*)&local, sizeof(local));
        if(bd < 0)
        {
            log(Fatal, "bind error, errno: %d, errstring: %s",errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(Info, "bind success");


        //tcp面向连接, 通信之前要建立连接
        //监听
        if(listen(listensockfd_, backlog) < 0)
        {
            log(Fatal, "listen error, errno: %d, errstring: %s",errno, strerror(errno));
            exit(LITSEN_ERR);
        }
        log(Info, "listen success");

    }

    static void *Routine(void *args)
    {
        pthread_detach(pthread_self());
        //子线程无需关闭文件描述符
        ThreadData *td = static_cast<ThreadData*>(args);

        td->t_tsvr_->Service(td->t_sockfd_, td->t_clientip_, td->t_clientport_);

        delete td;
    }

    void Start()
    {
        signal(SIGPIPE, SIG_IGN);
        threadPool<Task>::GetInstance()->Start();
        //signal(SIGCHLD, SIG_IGN);  //直接忽略进程等待 V2
        log(Info, "server is running...");
        while(true)
        {
            //获取新链接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensockfd_, (struct sockaddr*)&client, &len);
            if(sockfd < 0)
            {
                log(Warning, "accpet error, errno: %d, errstring: %s",errno, strerror(errno));
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
            log(Info, "get a new link..., sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip, clientport);
            //根据新链接进行通信
            
            //V1--单进程
            //Service(sockfd, clientip, clientport);
            //close(sockfd);
            
            //V2--多进程
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     //child
            //     close(listensockfd_);  //子进程可以看到父进程的文件描述符表,关闭不需要的 
            //     if(fork() > 0)  exit(0);//父进程创建的子进程直接退出
            //     Service(sockfd, clientip, clientport);  //孙子进程执行, 由于孙子的父进程退出, 由系统领养
            //     close(sockfd);
            //     exit(0);
            // }
            // //father
            // close(sockfd);  //存在引用计数,不会这个关闭这个文件
            // pid_t tid = waitpid(id, nullptr, 0);
            // (void)tid;
            
            //V3--多线程(创建进程成本较高,换线程)
            // ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);
            //pthread_join(tid, nullptr);   //已进行线程分离,无需等待(并发执行)
            
            //V4--线程池
            Task t(sockfd, clientip, clientport);
            threadPool<Task>::GetInstance()->Push(t);

            //sleep(1); 
        }
    }
    void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
    {
        char buffer[4096];
        while(true)
        {
            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if(n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say# " << buffer << std::endl;
                std::string echo_str = "tcpserver echo# ";
                echo_str += buffer;

                write(sockfd, echo_str.c_str(), echo_str.size());
            }
            else if(n == 0)
            {
                log(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else
            {
                log(Warning, "read error, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip.c_str(), clientport);
                break;
            }
            memset(buffer, 0, sizeof(buffer));
        }
    }

    ~TcpServer(){}
private:
    int listensockfd_;
    uint16_t port_;
    std::string ip_;
};

客户端:

#include<iostream>
#include<cstring>
#include<unistd.h>

#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>

void Usage(const std::string &proc)
{
    std::cout << "\n\tUsage: " << proc << "serverip serverport" << std::endl;
}

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[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));



    //连接模块
    while (true)
    {
        int sockfd = 0;
        int cnt = 5;
        bool isreconnect = false;
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
        {
            std::cerr << "socket error" << std::endl;
            return 1;
        }
        do
        {
            // tcp客户端也无需显示bind
            // 向目标发起连接(connect的时候进行自动随机bind)
            int cn = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
            if (cn < 0)
            {
                isreconnect = true;
                cnt--;  //重连5秒
                std::cerr << "connet error, reconnect: " << cnt << std::endl;
                close(sockfd);
                sleep(1);   //每隔一秒重连一次
            }
            else
            {
                break;
            }
        }while(cnt && isreconnect);

        if(cnt == 0)
        {
            std::cerr << "server offline..." << std::endl;
            break;
        }

        //服务模块
        //while (true)
        {
            std::string message;
            std::cout << "Please Enter# ";
            std::getline(std::cin, message);
            if (message.empty())
                continue;

            int wn = write(sockfd, message.c_str(), message.size());
            if (wn < 0)
            {
                std::cerr << "write error" << std::endl;
                //break;
            }

            char inbuffer[4096];
            int rn = read(sockfd, inbuffer, sizeof(inbuffer));
            if (rn > 0)
            {
                inbuffer[rn] = 0;
                std::cout << inbuffer << std::endl;
            }
            else
            {
                //break;
            }
        }
        close(sockfd);
    }

    return 0;
}

关于构造

关于构造和初始化,可以直接在构造的时候,将服务器初始化,那为什么还要写到init初始化函数里面呢?

构造尽量简单一点,不要做一些“有风险”的操作。

listen监听

tcp是面向连接的,通信之前要建立连接,服务器处于等待连接到来的状态:

#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);

image-20240205161303727

accept

获取新链接:

#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

后两个参数输出型参数,获取对方的信息

这里返回也是一个套接字,这和最开始创建的listensockfd_有什么区别呢?

这就好比去吃饭,门口有一个拉客的,问“帅哥,来不来我们这里吃饭啊”,如果你去了,那就是“欢迎光临,几位呀”,你回答“我和我女朋友”,然后这个人大喊“两位客人,来一个服务员”,这时候就来了一个服务员招呼你坐下,然后给你菜单,点完菜之后给你上菜;在这个期间站在门口的拉客的人,一直在招揽客人,有人来就喊服务员招招待,人家不来就继续拉。

listensockdf_就是这个拉客的,真正给我们提供服务的是accept返回的

telnet测试

telnet可以对指定服务的远程连接

image-20240205192444936

127.0.0.1本地环回,再加上端口号就可以测试了

ctrl + ],然后回车

读取信息

tcp是面向字节流的,管道是面向字节流的、读取普通文件也是面向字节流的,所以可以采用read进行读取。

掉线重连

当读端关闭之后,系统会杀掉写端,采用signal(SIGPIPE, SIG_IGN)忽略这个信号

翻译服务器演示

GIF 2024-2-11 20-17-30

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

162基于matlab的多尺度和谱峭度算法对振动信号进行降噪处理

基于matlab的多尺度和谱峭度算法对振动信号进行降噪处理&#xff0c;选择信号峭度最大的频段进行滤波&#xff0c;输出多尺度谱峭度及降噪结果。程序已调通&#xff0c;可直接运行。 162 matlab 信号处理 多尺度谱峭度 (xiaohongshu.com)

Acwing---844.走迷宫

走迷宫 1.题目2.基本思想3.代码实现 1.题目 给定一个 nm 的二维整数数组&#xff0c;用来表示一个迷宫&#xff0c;数组中只包含 0 或 1&#xff0c;其中 0 表示可以走的路&#xff0c;1 表示不可通过的墙壁。最初&#xff0c;有 一个人位于左上角 (1,1)处&#xff0c;已知该…

实景剧本杀小程序:创新体验,沉浸式推理乐趣

随着科技的飞速发展&#xff0c;人们对于娱乐方式的追求也在不断升级。传统的桌面剧本杀游戏已经不能满足玩家的需求&#xff0c;他们渴望更加真实、刺激的游戏体验。正是这种需求推动下&#xff0c;实景剧本杀小程序应运而生&#xff0c;为玩家带来前所未有的推理乐趣。 实景…

图表自动化开篇

目录 前言&#xff1a; 使用 Canvas 或者 SVG 渲染 选择哪种渲染器 代码触发 ECharts 中组件的行为 前言&#xff1a; 图表自动化一直以来是自动化测试中的痛点&#xff0c;也是难点&#xff0c;痛点在于目前越来越多公司开始构建自己的BI报表平台但是没有合适的自动化测试…

docker 1:介绍

docker 1&#xff1a;介绍 docker解决哪些问题&#xff1a; 传统APP在安装到不同电脑的时候可能会遇到依赖问题&#xff0c;比如缺少VS 20xx&#xff0c;软件无法运行”的情况。docker使用容器技术将软件 依赖​打包为image包发布&#xff0c;解决了依赖问题。docker有一个官…

勒索攻击风起云涌,Sodinokibi深度分析

前言 Sodinokibi勒索病毒&#xff0c;又称为REvil勒索病毒&#xff0c;这款勒索病毒最早在国内被发现是2019年4月份&#xff0c;笔者在早期分析这款勒索病毒的时候就发现它与其他勒索病毒不同&#xff0c;于是被笔者称为GandCrab勒索病毒的“接班人”&#xff0c;为什么它是Ga…

在面试中如何回复擅长vue还是react

当面试官问及这个问题的时候&#xff0c;我们需要思考面试官是否是在乎你是掌握vue还是react吗&#xff1f;&#xff1f;&#xff1f; 在大前端的一个环境下&#xff0c;当前又有AI人工智能的加持辅助&#xff0c;我们是不是要去思考企业在进行前端岗位人员需求的时候&#xf…

C++ bfs再探迷宫游戏(五十五)【第二篇】

今天我们用bfs解决迷宫游戏。 1.再探迷宫游戏 前面我们已经接触过了迷宫游戏&#xff0c;并且学会了如何使用 DFS 来解决迷宫最短路问题。用 DFS 求解迷宫最短路有一个很大的缺点&#xff0c;需要枚举所有可能的路径&#xff0c;读入的地图一旦很大&#xff0c;可能的搜索方案…

[VulnHub靶机渗透] Nyx

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

C# CAD2016获取数据操作BlockTableRecord、Polyline、DBObject

一、数据操作说明 //DBObject 基础类 DBObject dbObj (DBObject)tr.GetObject(outerId, OpenMode.ForRead); //Polyline 线段类 Polyline outerPolyline (Polyline)tr.GetObject(outerId, OpenMode.ForRead); //BlockTableRecord 块表类 BlockTableRecord modelSpace (Bloc…

ChatGPT高效提问—prompt实践(视频制作)

ChatGPT高效提问—prompt实践&#xff08;视频制作&#xff09; 1.1 视频制作 ​ 制作视频对于什么都不懂的小白来说非常难。而随着AI技术的发展&#xff0c;这件事变得越来越简单&#xff0c;如今小白也可以轻松上手。如何借助ChatGPT来制作短视频。 ​ 其实方法非常简单&a…

ubuntu服务器部署gitlab docker并配置nginx反向代理https访问

拉取镜像 docker pull gitlab/gitlab-ce运行容器 docker run --detach \--publish 9080:80 --publish 9022:22 --publish 9443:443\--namegitlab \--restartalways \--volume /home/docker/gitlab/config:/etc/gitlab \--volume /home/docker/gitlab/logs:/var/log/gitlab \-…

docker 3.1 镜像

docker 3.1 镜像命令 拉取镜像 docker pull debian #从 Docker Hub 拉取名为 debian 的镜像docker pull hello-world #从 Docker Hub 拉入名为 hello-world 的镜像‍ 运行镜像/容器 docker run hello-world ‍ 查看本地所有的镜像 docker images​​ 容器生成镜像…

【算法随想录01】环形链表

题目&#xff1a;141. 环形链表 难度&#xff1a;EASY 代码 哈希表遍历求解&#xff0c;表中存储的是元素地址。 时间复杂度 O ( N ) O(N) O(N)&#xff0c;空间复杂度 O ( N ) O(N) O(N) /*** Definition for singly-linked list.* struct ListNode {* int val;* …

react【四】css

文章目录 1、css1.1 react和vue css的对比1.2 内联样式1.3 普通的css1.4 css modules1.5 在react中使用less1.6 CSS in JS1.6.1 模板字符串的基本使用1.6.2 styled-components的基本使用1.6.3 接受传参1.6.4 使用变量1.6.5 继承样式 避免代码冗余1.6.6 设置主题色 1.7 React中添…

【排序】归并排序

归并排序 动图演示&#xff1a; 基本思想&#xff1a;分治思想 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子…

【ES】--Elasticsearch的分词器深度研究

目录 一、问题描述及分析二、analyze分析器原理三、 multi-fields字段支持多场景搜索(如同时简繁体、拼音等)1、ts_match_analyzer配置分词2、ts_match_all_analyzer配置分词3、ts_match_1_analyzer配置分词4、ts_match_2_analyzer配置分词5、ts_match_3_analyzer配置分词6、ts…

2024年智能算法优化PID参数,ITAE、ISE、ITSE、IAE四种适应度函数随意切换,附MATLAB代码...

PID 参数整定就是确定比例系数&#xff08;Kp &#xff09;、积分系数&#xff08;Ki&#xff09;和微分系数&#xff08;Kd &#xff09;的过程&#xff0c;以便使 PID 控制器能够在系统中实现稳定、快速、准确的响应。 本期的主题 采用四种2024年的智能优化算法优化PID的三个…

《Java 简易速速上手小册》第6章:Java 并发编程(2024 最新版)

文章目录 6.1 线程的创建和管理 - 召唤你的士兵6.1.1 基础知识6.1.2 重点案例&#xff1a;实现一个简单的计数器6.1.3 拓展案例 1&#xff1a;定时器线程6.1.4 拓展案例 2&#xff1a;使用 Executor 框架管理线程 6.2 同步机制 - 维持军队的秩序6.2.1 基础知识6.2.2 重点案例&a…

pytorch花式索引提取topk的张量

文章目录 pytorch花式索引提取topk的张量问题设定代码实现索引方法gather方法验证 补充知识expand方法gather方法randint pytorch花式索引提取topk的张量 问题设定 或者说&#xff0c;有一个(bs, dim, L)的大张量&#xff0c;索引的index形状为(bs, X)&#xff0c;想得到一个(…