Linux网络编程——tcp套接字

news2024/11/26 21:37:31

文章目录

    • 主要代码
    • 关于构造
    • 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

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

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

相关文章

C# CAD2016 判断多边形的方向正时针或逆时针旋转

方法一&#xff1a;基于相邻顶点相对位置判断顺时针排列 // 计算当前子序列是否为顺时针排列 for (int i 1; i < outerPoints.Count; i) {int index (startVertexIndex i) % outerPoints.Count;int prevIndex (startVertexIndex i - 1) % outerPoints.Count;Point2d c…

Acwing---873. 欧拉函数

欧拉函数 1.题目2.基本思想3.代码实现 1.题目 给定 n 个正整数 ai&#xff0c;请你求出每个数的欧拉函数。 欧拉函数的定义 输入格式 第一行包含整数 n n n。 接下来 n n n 行&#xff0c;每行包含一个正整数 a i ai ai。 输出格式 输出共 n n n 行&#xff0c;每行输出…

Fluke ADPT 连接器新增对福禄克万用 Fluke 101 的支持

所需设备&#xff1a; 1、Fluke ADPT连接器&#xff1b; 2、Fluke 101&#xff1b; Fluke 101 拆机图&#xff1a; 显示界面如下图&#xff1a; 并且可以将波形导出到EXCEL: 福禄克万用表需要自己动手改造&#xff01;&#xff01;&#xff01;

电商+支付双系统项目------设计数据库

这篇文章将详细介绍电商支付双系统项目的数据库设计。数据库在该项目中扮演着至关重要的角色&#xff0c;它负责存储和管理用户信息、商品数据、订单记录以及支付交易等关键数据。通过精心设计和优化数据库结构&#xff0c;可以实现高效的数据存储和检索&#xff0c;确保系统的…

【Linux】 Linux 小项目—— 进度条

进度条 基础知识1 \r && \n2 行缓冲区3 函数介绍 进度条实现版本 1代码实现运行效果 版本2 Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读&#xff01;&#xff01;&#xff01;下一篇文章见&#xff01;&#xff01;&#xff01; 基础知识 1 \r &&a…

利用修改邻接变量

资源下载 【免费】突破密码认证程序&#xff08;修改邻接变量&#xff09;资源-CSDN文库 资源内容 源码 /*****************************************************************************To be the apostrophe which changed "Impossible" into "Im possib…

java.lang.NoClassDefFoundError: org/springframework/core/GenericTypeResolver

前言 小编我将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识&#xff0c;有兴趣的小伙伴可以关注一下&#xff01; 也许一个人独行&#xff0c;可以走的很快&#xff0c;但是一群人结伴而行&#xff0c;才能走的更远&#xff01;让我们在成长的道路上互相学习&…

Codeforces Round 926 (Div. 2)(A~C)

A. Sasha and the Beautiful Array 分析&#xff1a;说实话&#xff0c;打比赛的时候看到这题没多想&#xff0c;过了一下样例发现将数组排序一下就行&#xff0c;交了就过了。刚刚写题解反应过来&#xff0c;a2-a1a3-a2.....an-a(n-1) an - a1&#xff0c;所以最后结果只取决…

35岁转行,是我人生中最正确的选择

前言 经常听到有人说&#xff0c;35岁是职场的分水岭&#xff0c;但我觉得我的35岁&#xff0c;人生才刚刚开始。 35岁前后&#xff0c;我生二胎&#xff0c;考研&#xff0c;跳槽&#xff0c;转行&#xff0c;从传统行业到服务业&#xff0c;从服务业到新能源行业&#xff0…

第7讲 SpringSecurity执行原理概述

SpringSecurity执行原理概述 spring security的简单原理&#xff1a; SpringSecurity有很多很多的拦截器&#xff0c;在执行流程里面主要有两个核心的拦截器 1&#xff0c;登陆验证拦截器AuthenticationProcessingFilter 2&#xff0c;资源管理拦截器AbstractSecurityInterc…

问题:在解决思想认识上存在的问题讲解过程中和大家分享谁的一段原话() #其他#媒体

问题&#xff1a;在解决思想认识上存在的问题讲解过程中和大家分享谁的一段原话&#xff08;&#xff09; A&#xff0e;鲁迅 B&#xff0e;稻盛和夫 C&#xff0e;戴尔卡耐基 D&#xff0e;奥格曼狄诺 参考答案如图所示

应急响应实战笔记02日志分析篇(3)

第3篇:Web日志分析 ox01 Web日志 Web访问日志记录了Web服务器接收处理请求及运行时错误等各种原始信息。通过对WEB日志进行的安全分析&#xff0c;不仅可以帮助我们定位攻击者&#xff0c;还可以帮助我们还原攻击路径&#xff0c;找到网站存在的安全漏洞并进行修复。 我们来…

【Spring源码解读 底层原理高级进阶】【上】探寻Spring内部:BeanFactory和ApplicationContext实现原理讲解

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;底层原理高级进阶》 &#x1f680…

java8并行数据处理与性能

块并用不同的线程分别处理每个数据块的流。这样一来&#xff0c;你就可以自动把给定操作的工作负荷分配给多核处理器的所有内核,让它们都忙起来。让我们用一个简单的例子来试验一下这个思想。假设你需要写一个方法&#xff0c;接受数字n作为参数&#xff0c;并返回从1到给定参数…

Web APIs -05

js执行机制 js是单线程&#xff0c;同一个时间只能做一件事情&#xff0c;所有任务需要排队所以有时候会渲染不连贯 同步任务 都在主线程上执行&#xff0c;形成一个执行栈 异步任务 js的异步是通过回调函数实现的分为三类&#xff1a;1.普通事件&#xff1a;click等&…

基于Python实现Midjourney集成到(个人/公司)平台中

目前Midjourney没有对外开放Api&#xff0c;想体验他们的服务只能在discord中进入他们的频道进行体验或者把他们的机器人拉入自己创建的服务器中&#xff1b;而且现在免费的也用不了了&#xff0c;想使用就得订阅。本教程使用midjourney-api这个开源项目&#xff0c;搭建Midjou…

【并发编程】AQS原理

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;并发编程 ⛺️稳中求进&#xff0c;晒太阳 1. 概述 全称是 AbstractQueuedSynchronizer&#xff0c;是阻塞式锁和相关的同步器工具的框架 特点&#xff1a; 用 state 属性来表示资源的状…

【牛客面试必刷TOP101】Day22.BM16 删除有序链表中重复的元素-II和BM21 旋转数组的最小数字

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;牛客面试必刷TOP101 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&…

【白话前端】一篇文章区分js库和js框架

假定你选择自助游&#xff0c;你需要找不同服务商帮你解决吃住行的问题&#xff0c;这些服务商就是js库。你也可以选择旅行社&#xff0c;给你全解决&#xff0c;这是js框架。 JavaScript库和框架都是用于简化Web开发的工具&#xff0c;但它们之间有一些区别。 JavaScript库&a…

Codeforces Round 926 (Div. 2)(A,B,C,D,E,F)

这场还是很有含金量的&#xff0c;B题开始就有难度了&#xff0c;B是个推结论的题&#xff0c;C要推结论然后递推&#xff0c;D题是有点难的树上DP&#xff08;主要是状态转移方程不好写&#xff09;&#xff0c;E题是个二进制预处理然后状压DP&#xff0c;F题是个数论&#xf…