C++ 网络套接字编程 tcp udp

news2024/9/20 14:47:42

文章目录

  • 网络通信的本质
    • tcp 和 udp 协议
    • 网络字节序
    • 网络主机数据转化接口
  • udp 通信
    • 服务端逻辑
    • 客户端逻辑
  • TCP 通信
    • 服务端程序编写步骤
    • 客户端程序编写步骤
  • 两种通信程序代码
    • udp服务端程序编写
    • udp 客户端程序编写
    • tcp 服务端程序编写
    • tcp 客户端程序编写

在这里插入图片描述

网络通信的本质

网络之间的通信,本质上就是进程间通信

对双方主机的两个进程而言,需要先将数据发送到对方的主机(ip地址),再找到指定的进程(port:端口号),就能实现通信

ip地址用来标识互联网中唯一的一台主机;port端口号用来标识该指定机器中进程的唯一性

那么(ip, port) 则可以用来表示互联网中唯一一个进程,ip + port 也叫网络套接字 socket

如何理解port:
一个端口号和一个进程相绑定,一个进程可以绑定多个端口号,反之则不可以。
那么为什么不用进程pid来表示网络中进程的唯一性呢?
为了其他的进程模块和网络进行解耦(万一pid的规则变化,网络部分也不受影响),port是专门用于网络通信的

tcp 和 udp 协议

tcp 协议常用于可靠通信,适用于对数据要求比较高的场景(如游戏,传输重要文件等),复杂
udp 协议用于不可靠通信,适用于允许数据偶尔出现差错的场景(如体育赛事直播等),简单,快

这两个协议没有好坏之分,只是应用场景不同,如果不确定使用哪个的时候,就要 tcp,毕竟复杂一点比丢包好

网络字节序

机器有大小端之分,大小端机器存储数据方式不同。大端是“正着存储”的,可读性较好,因此在网络传输时规定,所以到达网络的数据,必须时大端存储的,因此,如果是小端机,收发数据到网络时需要先转化为大端

网络主机数据转化接口

ip 地址为4个字节,使用 uint32_t,port 为2个字节,使用 uint_16

htonl、htons 是转网络,ntohl、ntohs 是转主机数据,使用这些接口可以自动识别机器的大小端,并将数据转化为需要的大小端数据。

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uintl6_t ntohs(uint16_t netshort);

socket 创建 udp 套接字接口

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器) 
int socket(int domain, int type, int protocol); 
int socket(AF_INET, SOCK_DGRAM, 0); // 创建网络套接字, 成功返回文件描述符, 失败返回 -1
// AF_INET 说明协议家族使用的是ipv4, SOCK_DGRAM表示当前创建的是 udp套接字,本质是创建文件
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

填充 sockaddr_in 结构(套接字信息)

struct sockaddr_in local;
bzero(&local, sizeof(local));
local.sin_family = AF_INEF; // 设置协议家族
local.sin_port = htons(_port); // 将端口号转化为网络字节序列并赋值给 sockaddr_in 参数
local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 使用 inet_addr 接口,变为4字节网络序列

服务器收发消息

recvform(); //收消息
sendto(); //发消息 

查看网络状态 可以查看启动的服务器的信息

sudo netstat -anup

udp 通信

首先,要有一个客户端,一个服务端。这两台机器的通信可以看作是两个在这台机器上的进程进行
通信,udp 是全双工通信,因此两个进程可以互发消息,不会相互影响。

服务端逻辑

服务端的主体逻辑可以封装成一个类对象,主函数中通过调用类中的 Init 函数和 Start 函数来实现服务器的初始化和启动。

首先,Init 类内函数中,先创建 socket 套接字,再填充 sockaddr_in 结构,sockaddr_in是网络通信相关的结构体,有 ip 地址,端口号,协议家族这几个字段,然后将 socket 套接字和 sockaddr_in 结构体 bind 绑定(注意,不是C++那个bind),绑定后,所谓的互联网中唯一的进程就出现了!

然后 Start 类内函数中,设置一个死循环,定义一个缓冲区,并定义一个 sockaddr_in 结构体变量,sockaddr_in 结构体变量用来存储收到的消息的网络地址,缓冲区用于存放收到的消息。通过 recvfrom 函数收到来自另一个互联网中唯一进程的消息后,如果 recvfrom 函数接受成功,就可以对消息做处理,然后通过 sendto 函数,将消息转发给对面的主机,对面主机的网络地址,就是 recvfrom 函数收到的存储在 sockaddr_in 结构体中的网络地址!

客户端逻辑

客户端的逻辑比较简单,就是拿到服务端的 ip 地址和端口号后,和服务端进行通信。如果通信的形式是收->发->收->发->收…这种收到消息后再发消息,发消息后才能收消息的情况,那么只需要定义一个死循环即可,先发消息,然后检测是否发送成功,成功了才收消息这种的

![[Pasted image 20240608100519.png]]

也可以使用多线程,一个线程只负责死循环发消息,一个线程只负责死循环收消息,即可实现 qq、微信式通信

![[Pasted image 20240608100201.png]]
这大概就是udp通信写程序的基本逻辑

TCP 通信

TCP 套接字

Tcp 通信与 Udp不同的是,Tcp要先建立连接才能通信,因此与 dup 服务端不同的是,tcp要有一个 listensock与本地网络信息绑定,还要有一个普通socket 来与 client 通信

服务端程序编写步骤

  • 创建listensocket, 它是 file fd, 本质是文件描述符
  • 填充本地网络信息(初始化 struct sockaddr_in)并bind listensocket 和本地网络信息
  • 设置socket为监听状态,tcp特有 listen(_listensock, default_backlog)
  • 获取连接,用 listensocket 来获取连接,定义一个 socket 用来接收客户端的信息,再定义一个网络套接字信息,accept 接收成功后,socket 将自动与网络信息绑定
  • 提供服务,建立连接成功后,用 read 读 socket,即可接收客户端信息,用 write 往 socket 中写,即可向客户端发送信息
  • 规定一个端口号,就能启动服务器
// 获取连接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sockfd = accept(_listensock, struct sockaddr*(&peer), &len);

// 填充网络信息时,ip地址的处理优化:
// p:process(进程), n(网络) -- 不太准确,但是好记忆
inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // 1. 字符串ip->4字节IP 2. 网络序列

客户端程序编写步骤

  • 先用服务端的ip 地址和它开放的端口号启动客户端
  • 创建 socket 并初始化
  • 填写 struct sockaddr_in 套接字信息
  • 用 sockfd 建立连接 connect
  • 收发信息用 read 和 write 向 sockfd 读写就行

如果要与多个客户端建立通信,则可使用多进程的方式:每检测到和一个客户端建立连接,就 fork 一个子进程,让子进程去与这个新建立的客户端通信(子进程继承父进程的文件描述符,但每个进程的文件描述符之间是独立的)

当然,也可以采用多线程,只需要将次级进程设置为分离即可!

两种通信程序代码

udp服务端程序编写

#pragma once
#include <iostream>
#include <cstring>
#include <string>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unordered_map>
#include <functional>
#include <pthread.h>
#include <strings.h>
#include <unistd.h>

#include "nocopy.hpp"
#include "Comm.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

using task_t = std::function<void()>;

const static int defaultport = 8888;
const static int defaultfd = -1;
// using func_t = std::function<std::string(std::string)>; // 定义了一个参数为 string, 返回值也为 string 的函数 
// 继承nocopy, 防止udpserver 被拷贝,因为基类中已经删除了拷贝构造和赋值
class UdpServer : public nocopy
{
public:
    UdpServer(u_int16_t port = defaultport)
        : _port(port), _sockfd(defaultfd)
    {
        pthread_mutex_init(&_user_mutex, nullptr);
    }
    void Init() // 初始化服务器
    {
        // 1. 创建 socket 文件细节 
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        std::cout << "sockfd: " << _sockfd << std::endl;
        if (_sockfd < 0)
        {
            lg.LogMessage(Fatal, "sock errr: %d %s\n", errno, strerror(errno));
            exit(Socket_Err);
        }

        lg.LogMessage(Info, "sock success, sockfd: %d\n", _sockfd);
        struct sockaddr_in local;
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY; // ip 设置为 0

        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // inet_addr 可以将点分十进制的ip地址转化为四字节网络字节序列
        int n = ::bind(_sockfd, (struct sockaddr*)(&local), (socklen_t)sizeof(local)); // 绑定到内核中, 成功就返回 0
        if (n != 0)
        {
            lg.LogMessage(Fatal, "bind errr: %d %s\n", errno, strerror(errno));
            exit(Bind_Err);
        }
        ThreadPool<task_t>::GetInstance()->Start();
    }
    void Map()
    {
        // 添加用户即可 ..
    }
    void AddOnlineUser(InetAddr addr) // 添加用户 
    {
        LockGuard lockguard(&_user_mutex);
        for (auto& user : _online_user)
        {
            if (addr == user) return;
        }
        _online_user.push_back(addr);
        lg.LogMessage(Debug, "%s : %d is add onlineusers!\n", addr.Ip().c_str(), addr.Port());
    }
    void Route(int sockfd, const std::string& message)
    {
        // 先加锁,然后转化给所有人
        LockGuard lockguard(&_user_mutex);
        for (auto& user : _online_user)
        {
            lg.LogMessage(Debug, "send to %s %d  success!\n", user.Ip().c_str(), user.Port());
            sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&user.GetAddr(), (socklen_t)sizeof(user.GetAddr()));
        }
    }
    void Start()
    {
        char buffer[1024];
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
            if (n > 0)
            {
                InetAddr addr(peer);
                buffer[n] = 0;
                AddOnlineUser(addr);
                std::string message;
                // 检测是否加入
                if (_hash.count(addr.Ip()) > 0) message = _hash[addr.Ip()] + buffer;
                else message = "[" + addr.Ip() + ":" + std::to_string(addr.Port()) + "]# " + buffer;
                std::cout << "message:" << message << std::endl;
                task_t task = std::bind(&UdpServer::Route, this, _sockfd, message);
                ThreadPool<task_t>::GetInstance()->Push(task);
            }
        }
    }
    ~UdpServer()
    {
        pthread_mutex_destroy(&_user_mutex);
    }

private:
    u_int16_t _port;
    int _sockfd;
    // func_t _OnMessage;
    std::vector<InetAddr> _online_user;
    pthread_mutex_t _user_mutex;

    std::unordered_map<std::string, std::string> _hash;
};

udp 客户端程序编写

#include <string>
#include <iostream>
#include <cstring>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "Comm.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "Pthread.hpp"

void Usage(std::string proc)
{
    std::cout << "Usage:" << proc << " server_ip server_port" << std::endl;
}

class ThreadData
{
public:
    ThreadData(int sock, struct sockaddr_in& server)
        :_sockfd(sock), _serveraddr(server)
    {}
    ~ThreadData()
    {}
public:
    int _sockfd;
    InetAddr _serveraddr;
};

// 负责收消息 
void RecverRoutine(ThreadData& td)
{
    while (true)
    {
        char buffer[4096];
        struct sockaddr_in tmp;
        socklen_t len = sizeof(tmp);
        ssize_t m = recvfrom(td._sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&tmp, &len);
        if (m > 0)
        {
            buffer[m] = 0;
            std::cout << buffer << std::endl;
        }
    }
}

// 负责发消息 
void SenderRoutine(ThreadData& td)
{
    while (true)
    {
        std::string inbuffer;
        std::getline(std::cin, inbuffer);
        auto server = td._serveraddr.GetAddr();
        ssize_t n = sendto(td._sockfd, inbuffer.c_str(), inbuffer.size(), 0, (struct sockaddr*)&server, sizeof server);
        if (n <= 0) std::cout << "send erro......" << std::endl;
        usleep(50);
    }
}

int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return Usage_Err;
    }
    std::string server_ip = argv[1];
    u_int16_t port = std::stoi(argv[2]);

    // 创建 socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        lg.LogMessage(Fatal, "sock errr: %d %s\n", errno, strerror(errno));
        return Socket_Err;
    }
    lg.LogMessage(Info, "sock success, sockfd: %d\n", sockfd);
    // lient 需要bind,但是不需要显示bind,让本地OS自动随机bind,选择随机端口号/

    // 填充 server 信息, 方便后面收发信息使用 
    struct sockaddr_in server;
    memset(&server, 0, sizeof server);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());
    server.sin_port = htons(port);


    ThreadData td(sockfd, server);
    Thread<ThreadData> recver("recver", RecverRoutine, td);
    Thread<ThreadData> sender("sender", SenderRoutine, td);

    recver.Start();
    sender.Start();

    recver.Join();
    sender.Join();
    close(sockfd);
    return 0;
}

tcp 服务端程序编写

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unordered_map>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

 class ThreadData
 {
 public:
     ThreadData(int sock, TcpServer *ptr, struct sockaddr_in &peer)
     :sockfd(sock),  svr_ptr(ptr), addr(peer)
     {}
     int SockFd() {return sockfd;}
     TcpServer *GetServer() { return svr_ptr;};
     ~ThreadData()
     {
         close(sockfd);
     }
 private:
     int sockfd;
     TcpServer* svr_ptr;
 public:
     InetAddr addr;
 };

class TcpServer : public nocopy
{
public:
    TcpServer(uint16_t port) : _port(port), _isrunning(false)
    {
    }
    // 都是固定套路
    void Init()
    {
        // 1. 创建socket, file fd, 本质是文件
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            lg.LogMessage(Fatal, "create socket error, errno code: %d, error string: %s\n", errno, strerror(errno));
            exit(Fatal);
        }
        lg.LogMessage(Debug, "create socket success, sockfd: %d\n", _listensock);

        // 固定写法,解决一些少量的bind失败的问题 -- TODO
        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        // 2. 填充本地网络信息并bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY);

        // 2.1 bind
        if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
        {
            lg.LogMessage(Fatal, "bind socket error, errno code: %d, error string: %s\n", errno, strerror(errno));
            exit(Bind_Err);
        }
        lg.LogMessage(Debug, "bind socket success, sockfd: %d\n", _listensock);

        // 3. 设置socket为监听状态,tcp特有的
        if (listen(_listensock, default_backlog) != 0)
        {
            lg.LogMessage(Fatal, "listen socket error, errno code: %d, error string: %s\n", errno, strerror(errno));
            exit(Listen_Err);
        }
        lg.LogMessage(Debug, "listen socket success, sockfd: %d\n", _listensock);

        ThreadNs::ThreadPool<task_t>::GetInstance()->Start();
        funcs.insert(std::make_pair("defaultService", std::bind(&TcpServer::DefaultService, this, std::placeholders::_1, std::placeholders::_2)));
    }
 
    void Service(int sockfd, InetAddr addr)
    {
        char buffer[1024];

        // 一直进行IO
        while (true)
        {
            ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << addr.PrintDebug() << "# " << buffer << std::endl;

                std::string echo_string = "server echo# ";
                echo_string += buffer;
                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!)
            {
                lg.LogMessage(Info, "client quit...\n");
                break;
            }
            else
            {
                lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));
                break;
            }
        }
    }
    // static void *HandlerRequest(void *args) // this
    // {
    //     pthread_detach(pthread_self());
    //     ThreadData *td = static_cast<ThreadData*>(args);
    //     td->GetServer()->Service(td->SockFd(), td->addr);
    //     delete td;
    //     return nullptr;
    // }
    void Start()
    {
        _isrunning = true;
        signal(SIGCHLD, SIG_IGN); // 在Linux环境中,如果对SIG_IGN进行忽略,子进程退出的时候,自动释放自己的资源
        while (_isrunning)
        {
            // 4. 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensock, CONV(&peer), &len);
            if (sockfd < 0)
            {
                lg.LogMessage(Warning, "accept socket error, errno code: %d, error string: %s\n", errno, strerror(errno));
                continue;
            }
            lg.LogMessage(Debug, "accept success, get n new sockfd: %d\n", sockfd);

            // 5. 提供服务啊, v1~v4
            // v1 单进程
            // Service(sockfd);
            // close(sockfd);

            // v2 多进程
            // pid_t id = fork();
            // if (id < 0)
            // {
            //     close(sockfd);
            //     continue;
            // }
            // else if (id == 0)
            // {
            //     // child
            //     close(_listensock);
            //     if (fork() > 0)
            //         exit(0);

            //     // 孙子进程,孤儿进程,被系统领养,正常处理
            //     Service(sockfd);
            //     close(sockfd);
            //     exit(0);
            // }
            // else
            // {
            //     close(sockfd);
            //     pid_t rid = waitpid(id, nullptr, 0);
            //     if (rid == id)
            //     {
            //         // do nothing
            //     }
            // }

            // v3 多进程 - 信号版
            // pid_t id = fork();
            // if (id < 0)
            // {
            //     close(sockfd);
            //     continue;
            // }
            // else if (id == 0)
            // {
            //     // child
            //     close(_listensock);
            //     Service(sockfd);
            //     close(sockfd);
            //     exit(0);
            // }
            // else
            // {
            //     close(sockfd);
            //     // 父进程不想等待呢?
            // }
            // v3 进程池 - 课堂不讲了 - 试一试 -- 问题?先创建子进程,然后才获取的连接,子进程看不到新获取的文件fd.

            // v4 多线程
            // ThreadData *td = new ThreadData(sockfd, this, peer);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, HandlerRequest, td);
            // 主线程和新线程,不需要关闭所谓文件描述符, 将线程设置为分离

            // v5 线程池 -- 不能让我们的服务器,提供长服务
            task_t t = std::bind(&TcpServer::Routine, this, sockfd, InetAddr(peer));
            ThreadNs::ThreadPool<task_t>::GetInstance()->Push(t);
        }
    }
    std::string Read(int sockfd)
    {
        char type[1024];
        ssize_t n = read(sockfd, type, sizeof(type) - 1);
        if (n > 0)
        {
            type[n] = 0;
        }
        else if (n == 0) // read如果返回值是0,表示读到了文件结尾(对端关闭了连接!)
        {
            lg.LogMessage(Info, "client quit...\n");
        }
        else
        {
            lg.LogMessage(Error, "read socket error, errno code: %d, error string: %s\n", errno, strerror(errno));
        }
        return type;
    }
    void Routine(int sockfd, InetAddr addr)
    {
        funcs["defaultService"](sockfd, addr);
        std::string type = Read(sockfd);
        lg.LogMessage(Debug, "%s select %s\n", addr.PrintDebug().c_str(), type.c_str());
        if (type == "ping")
            funcs[type](sockfd, addr);
        else if (type == "translate")
            funcs[type](sockfd, addr);
        else if (type == "transform")
            funcs[type](sockfd, addr);
        else
        {
        }

        close(sockfd);
    }
    void DefaultService(int sockfd, InetAddr& addr)
    {
        (void)addr;
        std::string service_list = " |";
        for (auto func : funcs)
        {
            service_list += func.first;
            service_list += "|";
        }

        write(sockfd, service_list.c_str(), service_list.size());
    }
    void RegisterFunc(const std::string& name, callback_t func)
    {
        funcs[name] = func;
    }
    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensock; // TODO
    bool _isrunning;

    // 构建业务逻辑
    std::unordered_map<std::string, callback_t> funcs;
};

tcp 客户端程序编写

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include "Comm.hpp"

using namespace std;

void handler(int signo)
{
    std::cout << "signo: " << signo << std::endl;
    exit(0);
}

#define Retry_Count 5

void Usage(const std::string &process)
{
    std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}

bool visitServer(std::string &serverip, uint16_t &serverport, int *cnt)
{
    // 1. 创建socket
    string inbuffer;
    char service_list[1024];
    ssize_t m = 0;
    ssize_t n = 0;
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        cerr << "socket error" << endl;
        return false;
    }
    bool ret = true;
    // 2. 要不要bind?必须要有Ip和Port, 需要bind,但是不需要用户显示的bind,client系统随机端口
    // 发起连接的时候,client会被OS自动进行本地绑定
    // 2. connect
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    // p:process(进程), n(网络) -- 不太准确,但是好记忆
    inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // 1. 字符串ip->4字节IP 2. 网络序列

    n = connect(sockfd, CONV(&server), sizeof(server)); // 自动进行bind哦!
    if (n < 0)
    {
        cerr << "connect error" << endl;
        ret = false;
        goto END;
    }
    *cnt = 0;

    m = read(sockfd, service_list, sizeof(service_list) - 1);
    if (m > 0)
    {
        service_list[m] = 0;
        cout << "服务器提供的服务列表是: " << service_list << endl;
    }

    // 并没有向server一样,产生新的sockfd.未来我们就用connect成功的sockfd进行通信即可.
    cout << "请你选择服务#  ";
    getline(cin, inbuffer);
    write(sockfd, inbuffer.c_str(), inbuffer.size());

    cout << "Enter> ";
    getline(cin, inbuffer);
    if (inbuffer == "quit")
        return true;

    // std::cout << "echo : " << inbuffer << std::endl;

    n = write(sockfd, inbuffer.c_str(), inbuffer.size());
    if (n > 0)
    {
        char buffer[1024];
        m = read(sockfd, buffer, sizeof(buffer) - 1);
        if (m > 0)
        {
            buffer[m] = 0;
            cout << buffer << endl;
        }
        else if (m == 0)
        {
            return true;
        }
        else
        {
            ret = false;
            goto END;
        }
    }
    else
    {
        std::cout << "hello write Error" << std::endl;
        ret = false;
        goto END;
    }

END:
    close(sockfd);
    return ret;
}

// ./tcp_client serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }
    std::string serverip = argv[1];
    uint16_t serverport = stoi(argv[2]);

    signal(SIGPIPE, SIG_IGN);

    // for (int i = 1; i <= 31; i++)
    // {
    //     signal(i, handler);
    // }

    int cnt = 1;
    while (cnt <= Retry_Count)
    {
        bool result = visitServer(serverip, serverport, &cnt);
        if (result)
        {
            break;
        }
        else
        {
            sleep(1);
            std::cout << "server offline, retrying..., count : " << cnt << std::endl;
            cnt++;
        }
    }

    if (cnt >= Retry_Count)
    {
    std:
        cout << "server offline" << std::endl;
    }
    return 0;
}

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

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

相关文章

香港电讯高可用网络助力企业变革金融计算

客户背景 客户是一家金融行业知名的量化私募对冲基金公司&#xff0c;专注于股票、期权、期货、债券等主要投资市场&#xff0c;在量化私募管理深耕多年&#xff0c;目前资管规模已达数百亿级&#xff0c;在国内多个城市均设有办公地点。 客户需求 由于客户业务倚重量化技术…

内网渗透-隧道搭建ssp隧道代理工具frp内网穿透技术

内网渗透-隧道搭建&ssp隧道代理工具&frp内网穿透 内网穿透&#xff1a;解决网络控制上线&网络通讯问题 隧道技术&#xff1a;解决不出网协议上线的问题&#xff08;利用出网协议进行封装出网&#xff09; 代理技术&#xff1a;解决网络通讯不通的问题&#xff0…

C++ —— unordered_set、unordered_map的介绍及使用

目录 unordered系列关联式容器 unordered_set的介绍 unordered_set的使用 unordered_set的定义方式 unordered_set接口的使用 unordered_multiset unordered_map的介绍 unordered_map的使用 unordered_map的定义方式 unordered_map接口的使用 unordered_multimap …

【两数之和】

两数之和 一、题目二、暴力解法三、哈希表四、map字典1.基本方法.set()添加键值对.get()通过键获取值.has()判断map是否有这个键 2.map和set的联系和区别共同点共同点MapSet 一、题目 二、暴力解法 三、哈希表 解题思路&#xff1a;将nums的元素依次以键值对的方式存储在map字典…

怎么看OV泛域名证书市场占有率提升

近年来&#xff0c;随着网络安全的逐渐普及&#xff0c;使用SSL证书的站点也逐渐增多。https证书也成为了当下最受欢迎的数字证书之一&#xff0c;主要是用于保护网站和应用程序的安全&#xff0c;并提升用户对网站的信任度&#xff0c;且只有企业或组织才可申请OV级别的SSL证书…

VMware虚拟机三种网络模式设置 - Bridged(桥接模式)

一、前言 由于linux目前很热门&#xff0c;越来越多的人在学习linux&#xff0c;但是买一台服务放家里来学习&#xff0c;实在是很浪费。那么如何解决这个问题&#xff1f;虚拟机软件是很好的选择&#xff0c;常用的虚拟机软件有vmware workstations和virtual box等。 在使用虚…

uniapp app一键登录

一键登录不需要单独写页面&#xff0c;uniapp 有原生的页面 第一步&#xff0c;登录Dcloud后台》我的应用》点击应用名称 填写完点击 uniCloud模块新建一个服务空间》选择免费 , 创建完点击一键登录&#xff0c;添加应用&#xff0c;这个需要审核&#xff0c;“大概一天左右”…

yolov10官方demo

python环境3.9 GitHub - THU-MIG/yolov10: YOLOv10: Real-Time End-to-End Object Detection 将模型移动到项目目录下 项目执行导入依赖 conda create -n yolov10 python3.9 conda activate yolov10 pip install -r requirements.txt pip install -e .

鸿萌的成功案例:数据“失而复得”的奇迹,奥睿科磁盘阵列恢复记

2024 年 6 月 3 日&#xff0c;鸿萌收到了来自大连某客户的一台奥睿科 ORICO 3548RUS3 四盘位 DAS 磁盘阵列。 故障背景 故障设备如下图所示&#xff0c;它曾经承载了客户满满的信赖与期待。 ORICO 3548RUS3 四盘位 DAS 磁盘阵列 有时&#xff0c;命运的转折让人猝不及防&am…

全新防关联技术出炉:亚马逊测评环境优化,下单成功率大提升

在竞争激烈的测评行业中&#xff0c;构建一个稳定且高效的环境系统成为了制胜的关键。然而&#xff0c;市场上现有的环境方案如虚拟机、模拟机、GCS、云手机、VPS等不仅成本高昂&#xff0c;而且面临着在风控严格的平台上如亚马逊难以逃脱检测的挑战&#xff0c;进而影响了测评…

决策树算法:揭示数据背后的决策逻辑

目录 一 决策树算法原理 特征选择 信息增益 信息增益比 基尼指数 树的构建 树的剪枝 预剪枝 后剪枝 二 决策树算法实现 一 使用决策树进行分类 数据预处理 构建决策树模型 二 使用决策树进行回归 数据预处理 构建决策树回归模型 三 决策树算法的优缺点 优点 …

发力采销,京东的“用户关系学”

作者 | 曾响铃 文 | 响铃说 40多岁打扮精致的城市女性&#xff0c;在西藏那曲的偏远农村&#xff0c;坐着藏民的摩托车&#xff0c;行驶在悬崖边的烂泥路上&#xff0c;只因为受顾客的“委托”&#xff0c;要寻找最原生态的藏区某款产品。 30多岁的憨厚中年男性&#xff0c;…

ROM修改进阶教程------小米机型不解锁bl 刷写国际版固件 rom修改解析

众所周知,小米机型要刷写国际版固件需要解锁bl先。否则开机会进不去系统。报错。究其原因是因为有区域限制。目前。在国外安卓论坛上已经有作者分享了一些国内版无需解锁bl刷写国际版固件的资源。 本人下载了一些固件通过与官方固件的对比了解了其中的一些区别,可能有网友询…

基于Baichuan2的新冠流感中医自我诊断治疗(大模型微调+Gradio)

一、项目说明 项目使用paddleNLP提供的大模型套件对Baichuan2-7b/13b进行微调&#xff0c;使用《中医治疗新冠流感支原体感染等有效病历集》进行Lora训练&#xff0c;使大模型具备使用中医方案诊断和治疗新冠、流感等上呼吸道感染的能力。 二、PaddleNLP PaddleNLP提供的飞桨…

2024最新宝塔面板8.1.0企业版开心版

官方更新记录 【增加】增加【网站】-【HTML项目】 【优化】优化Docker模块使用体验 【优化】优化文件压缩和解压的速度 【修复】修复在上一版本中出现的所有已知问题 开心版更新记录 1.在 PHP切换页面&#xff0c;出现报错弹窗属于正常情况&#xff0c;是因爲没安装 企业…

Chrome插件开发入门:手把手教你创建第一个扩展

问题背景 最近&#xff0c;客户发布了一个新的任务 —— 开发一个Chrome插件。之前没有这方面的开发经验&#xff0c;准备想学习一下这块的内容&#xff0c;我发现网上的大多数视频都是几年前的&#xff0c;开发版本都是基于MV2&#xff0c;当前谷歌已经开始使用MV3&#xff0…

【嵌入式编程】-C语言结构体成员对齐、填充和数据打包

C语言结构体成员对齐、填充和数据打包 文章目录 C语言结构体成员对齐、填充和数据打包1、内存中的数据对齐2、C语言中的结构填充3、如何减少结构填充4、C语言中结构填充的常见问题解答 在 C 语言中&#xff0c;结构用作数据包。它们不提供任何数据封装或数据隐藏功能。在本文中…

HarmonyOS【ArkUI组件--TextInput】

1.文本输入框基本用法 2. 使用文本输入框组件&#xff08;如何实现输入数字改变图片大小&#xff09; 在此博客的基础上继续编写&#xff1a;HarmonyOS【ArkUI组件--Text】-CSDN博客 ①代码如下&#xff1a; import font from ohos.font Entry Component struct Index {State …

【吊打面试官系列-Mysql面试题】Myql 中的事务回滚机制概述 ?

大家好&#xff0c;我是锋哥。今天分享关于 【Myql 中的事务回滚机制概述 ?】面试题&#xff0c;希望对大家有帮助&#xff1b; Myql 中的事务回滚机制概述 ? 事务是用户定义的一个数据库操作序列&#xff0c;这些操作要么全做要么全不做&#xff0c;是一个不可分割的工作单位…

RK3568技术笔记十二 Android编译方法

Android源码说明 Android源码在SAIL-RK3568开发板光盘->Android->源代码中&#xff0c;由于android源码太大&#xff0c;在进行压缩时&#xff0c;进行分包压缩&#xff0c;因此有4部分&#xff0c;如图所示&#xff1a; 进行解压时&#xff0c;需将4部分压缩包放置同一…