【网络】网络编程套接字——UDP、TCP、UDP接口使用、TCP接口使用、UDP程序实例、TCP程序实例

news2024/9/19 15:57:27

文章目录

  • Linux网络
    • 1. UDP
      • 1.1 UDP接口使用
      • 1.1 UDP程序实例
    • 2. TCP
      • 2.1 TCP接口使用
      • 2.2 TCP程序实例

Linux网络

1. UDP

  在使用我们的UDP和TCP函数的时候,我们需要理解一些预备的知识:

源 IP 地址和目的 IP 地址:

  在网络通信中,IP 数据包头部的源 IP 地址和目的 IP 地址起着至关重要的作用。源 IP 地址指明了数据包的发送方所在的网络位置,就像寄信时的发信人地址;目的 IP 地址则指明了数据包的接收方所在的网络位置,如同收信人的地址。

  例如,当您在网上浏览网页时,您的设备(如电脑或手机)发送的请求数据包中就包含了您设备的源 IP 地址,而您所请求的网站服务器的 IP 地址则作为目的 IP 地址。

  但仅有 IP 地址并不足以完成完整的通信。以发送 QQ 消息为例,虽然 IP 地址能将消息送达对方的机器,但机器上可能运行着多个程序,还需要其他标识来确定这个数据应由哪个程序来解析处理,这就引入了端口号的概念。

  

端口号:

  端口号是传输层协议的重要组成部分,它是一个 2 字节 16 位的整数。端口号的作用在于标识一个进程,告诉操作系统应该将接收到的数据交给哪一个进程来处理。

  比如说,您的电脑上同时运行着浏览器和即时通讯软件,当网络数据到达时,端口号就像一个“指示牌”,告诉操作系统把数据准确地传递给对应的程序。

  就像一个电话号码,IP 地址能让数据找到正确的主机,而端口号则能让数据找到主机上正确的进程。

  IP 地址和端口号的组合能够唯一标识网络上某一台主机的某一个进程。

  

端口号和进程 ID(pid):

  在系统编程中,pid 用于唯一标识一个进程。而端口号在某种程度上也类似于 pid 对进程的标识作用。

  然而,它们之间也存在一些区别。pid 是操作系统内部用于管理进程的标识符,而端口号则主要用于网络通信中标识进程。

  例如,在一个服务器系统中,操作系统通过 pid 来管理进程的资源分配和调度,而当网络数据到达时,服务器通过端口号来确定将数据传递给哪个网络服务进程。

  

源端口号和目的端口号:

  在传输层协议(如 TCP 和 UDP)的数据段中,源端口号和目的端口号分别描述了“数据是谁发的,要发给谁”。

  比如,您通过浏览器访问一个网站,您的浏览器使用一个随机分配的源端口号(通常是大于 1024 的临时端口)向网站服务器的特定目的端口号(如 HTTP 协议通常使用 80 端口)发送请求,服务器接收到请求后,根据源端口号和目的端口号进行回应,从而完成一次完整的网络通信过程。

  
在这里插入图片描述

  

1.1 UDP接口使用

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);

// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,
 socklen_t address_len);

  

创建 UDP 套接字:

 // 创建 UDP 套接字
  int sockfd;
  if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 
  {
      perror("Socket creation failed");
      exit(EXIT_FAILURE);
  }

  “socket(AF_INET, SOCK_DGRAM, 0)”这个函数调用是用来创建一个套接字。

  “AF_INET”意思是使用 IPv4 地址格式

  “SOCK_DGRAM”表示创建的UDP数据报套接字,数据以独立的包发送,可能无序、丢失或重复。

  “0”让系统自动选择适合前面设置的默认传输协议,这里就是 UDP 协议。

  函数成功会返回一个套接字描述符,用于后续操作;失败则返回 -1 。

  

绑定端口号:

  struct sockaddr_in servaddr, cliaddr;
  memset(&servaddr, 0, sizeof(servaddr));
  memset(&cliaddr, 0, sizeof(cliaddr));

  // 填充服务器地址信息
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = INADDR_ANY;
  servaddr.sin_port = htons(PORT);

  // 绑定套接字到指定端口
  if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
      perror("Bind failed");
      exit(EXIT_FAILURE);
  }

  我们首先定义了两个 struct sockaddr_in 类型的结构体 servaddr(服务器地址)和 cliaddr(客户端地址)。

  然后使用 memset 函数将这两个结构体的内存空间初始化为 0,以清除可能存在的垃圾数据。

  接下来为服务器地址结构体 servaddr 进行填充:

  servaddr.sin_family 设为 AF_INET,表示使用 IPv4 地址格式

  servaddr.sin_addr.s_addr 设为 INADDR_ANY,表示服务器可以接收来自任何本地 IPv4 地址的连接请求

  servaddr.sin_port 通过 htons 函数将指定的端口号(比如前面定义的 PORT)转换为网络字节序并进行设置。

  最后,使用 bind 函数将创建的套接字 sockfd 与填充好的服务器地址 servaddr 进行绑定。如果绑定失败,会打印错误信息并退出程序。

  

网络字节序:

  网络字节序是网络数据流的规定格式,采用大端字节序(低地址高字节)。

  TCP/IP 协议要求网络数据按这种格式传输。发送主机按内存地址从低到高发送数据,接收主机也按内存地址从低到高保存数据。

  不管主机是大端还是小端字节序,都要按网络字节序发送/接收数据。小端主机需转换数据,大端主机可直接发送。

  为让网络程序在不同主机都能正常运行,可调用库函数进行网络字节序和主机字节序转换,htons、htonl、ntohs、ntohl 。

  

sockaddr结构:

  Socket API 是适用于多种底层网络协议的抽象编程接口,如 IPv4、IPv6 及 UNIX Domain Socket 。IPv4 地址用 sockaddr_in 结构体表示,其包含地址类型、端口号和 IP 地址。

  IPv4、IPv6 地址类型分别定义为常数 AF_INET 、 AF_INET6 。Socket API 都能用 struct sockaddr * 类型表示,使用时需强制转化为 sockaddr_in ,好处是使程序具有通用性,能接收各种类型的 sockaddr 结构体指针作为参数。

  

  sockaddr_in 结构:

  虽然socket api的接口是sockaddr,但是我们真正在基于IPv4编程时,使用的数据结构是sockaddr_in,这个结构里主要有三部分信息:地址类型,端口号,IP地址。

  

  in_addr结构:

  in_addr用来表示一个IPv4的IP地址,其实就是一个32位的整数。

  

1.1 UDP程序实例

UdpServer.hpp

#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include "Log.hpp"

// using func_t = std::function<std::string(const std::string&)>;
typedef std::function<std::string(const std::string &, const std::string &, uint16_t)> func_t;

Log lg;

enum{
    SOCKET_ERR=1,
    BIND_ERR
};

uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";
const int size = 1024;

class UdpServer{
public:
    UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip):sockfd_(0), port_(port), ip_(ip),isrunning_(false)
    {}
    void Init()
    {
        // 1. 创建udp socket
        // 2. Udp 的socket是全双工的,允许被同时读写的
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // PF_INET
        if(sockfd_ < 0)
        {
            lg(Fatal, "socket create error, sockfd: %d", sockfd_);
            exit(SOCKET_ERR);
        }
        lg(Info, "socket create success, sockfd: %d", sockfd_);
        // 2. bind socket
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_); //需要保证我的端口号是网络字节序列,因为该端口号是要给对方发送的
        local.sin_addr.s_addr = inet_addr(ip_.c_str()); //1. string -> uint32_t 2. uint32_t必须是网络序列的 // ??
        // local.sin_addr.s_addr = htonl(INADDR_ANY);

        if(bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno: %d, err string: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));
    }
    void CheckUser(const struct sockaddr_in &client, const std::string clientip, uint16_t clientport)
    {
        auto iter = online_user_.find(clientip);
        if(iter == online_user_.end())
        {
            online_user_.insert({clientip, client});
            std::cout << "[" << clientip << ":" << clientport << "] add to online user." << std::endl;
        }
    }

    void Broadcast(const std::string &info, const std::string clientip, uint16_t clientport)
    {
        for(const auto &user : online_user_)
        {
            std::string message = "[";
            message += clientip;
            message += ":";
            message += std::to_string(clientport);
            message += "]# ";
            message += info;
            socklen_t len = sizeof(user.second);
            sendto(sockfd_, message.c_str(), message.size(), 0, (struct sockaddr*)(&user.second), len);
        }
    }

    void Run() // 对代码进行分层
    {
        isrunning_ = true;
        char inbuffer[size];
        while(isrunning_)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);
            if(n < 0)
            {
                lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));
                continue;
            }
            
            uint16_t clientport = ntohs(client.sin_port);
            std::string clientip = inet_ntoa(client.sin_addr);
            CheckUser(client, clientip, clientport);

            std::string info = inbuffer;
            Broadcast(info,clientip, clientport);
        }
    }
    ~UdpServer()
    {
        if(sockfd_>0) close(sockfd_);
    }
private:
    int sockfd_;     // 网路文件描述符
    std::string ip_; // 任意地址bind 0
    uint16_t port_;  // 表明服务器进程的端口号
    bool isrunning_;
    std::unordered_map<std::string, struct sockaddr_in> online_user_;
};

  

UdpClient.cc

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "Terminal.hpp"

using namespace std;

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

struct ThreadData
{
    struct sockaddr_in server;
    int sockfd;
    std::string serverip;
};

void *recv_message(void *args)
{
    // OpenTerminal();
    ThreadData *td = static_cast<ThreadData *>(args);
    char buffer[1024];
    while (true)
    {
        memset(buffer, 0, sizeof(buffer));
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);

        ssize_t s = recvfrom(td->sockfd, buffer, 1023, 0, (struct sockaddr *)&temp, &len);
        if (s > 0)
        {
            buffer[s] = 0;
            cerr << buffer << endl;
        }
    }
}

void *send_message(void *args)
{
    ThreadData *td = static_cast<ThreadData *>(args);
    string message;
    socklen_t len = sizeof(td->server);

    std::string welcome = td->serverip;
    welcome += " comming...";
    sendto(td->sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&(td->server), len);

    while (true)
    {
        cout << "Please Enter@ ";
        getline(cin, message);

        // std::cout << message << std::endl;
        // 1. 数据 2. 给谁发
        sendto(td->sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)&(td->server), len);
    }
}

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

    struct ThreadData td;
    bzero(&td.server, sizeof(td.server));
    td.server.sin_family = AF_INET;
    td.server.sin_port = htons(serverport); //?
    td.server.sin_addr.s_addr = inet_addr(serverip.c_str());

    td.sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (td.sockfd < 0)
    {
        cout << "socker error" << endl;
        return 1;
    }

    td.serverip = serverip;

    pthread_t recvr, sender;
    pthread_create(&recvr, nullptr, recv_message, &td);
    pthread_create(&sender, nullptr, send_message, &td);

    pthread_join(recvr, nullptr);
    pthread_join(sender, nullptr);

    close(td.sockfd);
    return 0;
}

  

2. TCP

TCPUDP
可靠传输不可靠传输
有连接无连接
字节流数据报

  

2.1 TCP接口使用

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);

// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,
 socklen_t address_len);

// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);

// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
 socklen_t* address_len);

// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);

  

创建TCP套接字:

 // 创建套接字
 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
     perror("Socket creation failed");
     return -1;
 }

  socket(AF_INET, SOCK_STREAM, 0) 这个函数来做创建套接字。

  AF_INET 是 IPv4 地址族

  SOCK_STREAM 表示要创建 TCP 套接字

  最后的 0 让系统选合适协议,这里就是 TCP 协议。

  要是创建失败(server_fd 为 0 ),就输出错误信息,程序返回 -1 结束。

  

绑定套接字:

 struct sockaddr_in address;
 int addrlen = sizeof(address);
 
 address.sin_family = AF_INET;
 address.sin_addr.s_addr = INADDR_ANY;
 address.sin_port = htons(PORT);

 // 绑定
 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
     perror("Bind failed");
     return -1;
 }

  我们先定义了一个 sockaddr_in 结构体 address 并获取其大小。然后为 address 结构体的成员赋值:

  sin_family 设为 AF_INET,表示使用 IPv4 地址格式。

  sin_addr.s_addr 设为 INADDR_ANY,表示服务器可以接收来自任何本地 IPv4 地址的连接请求。

  sin_port 通过 htons 函数将指定的端口号(比如前面定义的 PORT)转换为网络字节序并进行设置。

  接下来使用 bind 函数将创建的套接字 server_fd 与填充好的 address 结构体进行绑定。如果绑定失败,会输出错误信息并返回 -1 结束程序。

  

监听套接字:

 // 监听
 if (listen(server_fd, 3) < 0) {
     perror("Listen failed");
     return -1;
 }

  我们使用 listen 函数让服务器套接字 server_fd 进入监听状态。

  参数 3 表示等待连接队列的最大长度。

  如果 listen 函数执行失败(返回值小于 0 ),就会输出错误信息 “Listen failed” ,然后程序返回 -1 结束运行。

  

接收连接:

  // 接受连接
  if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
      perror("Accept failed");
      return -1;
  }

  我们通过 accept 函数来接受客户端的连接请求。

  如果成功,会返回一个新的套接字描述符 new_socket ,用于与客户端进行后续的通信。
同时,会将客户端的地址信息填充到 address 结构体中,addrlen 则用于记录客户端地址的长度。

  如果 accept 函数执行失败(返回值小于 0 ),就会输出错误信息 “Accept failed” ,然后程序返回 -1 结束。

  

接收数据:

 // 接收数据
 int received = recv(new_socket, buffer, sizeof(buffer), 0);
 if (received < 0) {
     perror("Receive failed");
     return -1;
 }

  我们使用 recv 函数从与客户端连接的新套接字 new_socket 中接收数据,并将接收到的数据存储到 buffer 数组中

  received 变量用于记录实际接收到的数据字节数。

  如果接收数据失败(received 的值小于 0 ),就会输出错误信息 “Receive failed” ,然后程序返回 -1 结束。

  

发送数据:

  // 发送数据
  char *response = "Hello client!";
  send(new_socket, response, strlen(response), 0);

  我们使用 send 函数通过新套接字 new_socket 向客户端发送这个数据

  strlen(response) 用于获取响应字符串的长度。

  最后一个参数 0 表示默认的发送选项。

  

关闭连接:

 close(new_socket);
 close(server_fd);

  close(new_socket) 关闭与客户端通信的套接字 new_socket 。

  close(server_fd) 关闭服务器端创建的监听套接字 server_fd 。

  关闭套接字可以释放相关的资源,并结束与客户端的连接和服务器的监听状态。

  

2.2 TCP程序实例

TcpServer.hpp

#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <signal.h>
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"

const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10; // 但是一般不要设置的太大
extern Log lg;

enum
{
    UsageError = 1,
    SocketError,
    BindError,
    ListenError,
};

class TcpServer;

class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &p, TcpServer *t): sockfd(fd), clientip(ip), clientport(p), tsvr(t)
    {}
public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer *tsvr;
};

class TcpServer
{
public:
    TcpServer(const uint16_t &port, const std::string &ip = defaultip) : listensock_(defaultfd), port_(port), ip_(ip)
    {
    }
    void InitServer()
    {
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            lg(Fatal, "create socket, errno: %d, errstring: %s", errno, strerror(errno));
            exit(SocketError);
        }
        lg(Info, "create socket success, listensock_: %d", listensock_);

        int opt = 1;
        setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt)); // 防止偶发性的服务器无法进行立即重启(tcp协议的时候再说)

        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));
        // local.sin_addr.s_addr = INADDR_ANY;

        if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(BindError);
        }

        lg(Info, "bind socket success, listensock_: %d", listensock_);

        // Tcp是面向连接的,服务器一般是比较“被动的”,服务器一直处于一种,一直在等待连接到来的状态
        if (listen(listensock_, backlog) < 0)
        {
            lg(Fatal, "listen error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(ListenError);
        }

        lg(Info, "listen socket success, listensock_: %d", listensock_);
    }

    // static void *Routine(void *args)
    // {
    //     pthread_detach(pthread_self());
    //     ThreadData *td = static_cast<ThreadData *>(args);
    //     td->tsvr->Service(td->sockfd, td->clientip, td->clientport);//???
    //     delete td;
    //     return nullptr;
    // }

    void Start()
    {
        Daemon();
        ThreadPool<Task>::GetInstance()->Start();
        // for fork();
        // signal(SIGCHLD, SIG_IGN);
        lg(Info, "tcpServer is running....");
        for (;;)
        {
            // 1. 获取新连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                lg(Warning, "accept 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));

            // 2. 根据新连接来进行通信
            lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);
            // std::cout << "hello world" << std::endl;
            // version 1 -- 单进程版
            // Service(sockfd, clientip, clientport);
            // close(sockfd);

            // version 2 -- 多进程版
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     // child
            //     close(listensock_);
            //     if(fork() > 0) exit(0);
            //     Service(sockfd, clientip, clientport); //孙子进程, system 领养
            //     close(sockfd);
            //     exit(0);
            // }
            // close(sockfd);
            // // father
            // pid_t rid = waitpid(id, nullptr, 0);
            // (void)rid;

            // version 3 -- 多线程版本
            // ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);

            // version 4 --- 线程池版本
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }
    // 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_string = "tcpserver echo# ";
    //             echo_string += buffer;

    //             write(sockfd, echo_string.c_str(), echo_string.size());
    //         }
    //         else if (n == 0)
    //         {
    //             lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
    //             break;
    //         }
    //         else
    //         {
    //             lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
    //             break;
    //         }
    //     }
    // }
    ~TcpServer() {}

private:
    int listensock_;
    uint16_t port_;
    std::string ip_;
};

  

TcpClient.cc

#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\rUsage: " << proc << " serverip serverport\n"
              << std::endl;
}

// ./tcpclient serverip serverport
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 cnt = 5;
        int isreconnect = false;
        int sockfd = 0;
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
        {
            std::cerr << "socket error" << std::endl;
            return 1;
        }
        do
        {
            // tcp客户端要不要bind?1 要不要显示的bind?0 系统进行bind,随机端口
            // 客户端发起connect的时候,进行自动随机bind
            int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));
            if (n < 0)
            {
                isreconnect = true;
                cnt--;
                std::cerr << "connect error..., reconnect: " << cnt << std::endl;
                sleep(2);
            }
            else
            {
                break;
            }
        } while (cnt && isreconnect);

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

        // while (true)
        // {
            std::string message;
            std::cout << "Please Enter# ";
            std::getline(std::cin, message);

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

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

    return 0;
}

            

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

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

相关文章

Chrome谷歌浏览器Console(控制台)显示文件名及行数

有没有这样的困扰&#xff1f;Chrome谷歌浏览器console(控制台)不显示编译文件名及行数? 设置&#xff08;Settings&#xff09;- > 忽略列表&#xff08;lgnore List&#xff09;-> 自定义排除规则&#xff08;Custom exclusion rules&#xff09; 将自定义排除规则…

Golang实现Word模板内容填充导出

这里我们使用一个广泛使用且免费处理 .docx 文件的库&#xff0c;github.com/nguyenthenguyen/docx. 安装 github.com/nguyenthenguyen/docx 库 首先&#xff0c;确保你已经安装了 docx 库&#xff1a; go get github.com/nguyenthenguyen/docx使用 docx 库处理 Word 模板 …

逆向案例二十九——某品威客登录,请求头参数加密,简单webpack

网址&#xff1a;登录- 一品威客网,创新型知识技能共享服务平台 抓到登陆包分析&#xff0c;发现请求头有参数加密&#xff0c;直接搜索 定位到加密位置&#xff0c;打上断点&#xff0c;很明显是对象f的a方法进行了加密。 往上找f&#xff0c;可以发现f被定义了&#xff0c;是…

Superset二次开发之筛选器native Filters 水平布局

引言 Apache Superset作为一个功能强大的开源数据探索和可视化平台&#xff0c;提供了丰富的配置选项来定制化用户体验。其中&#xff0c;HORIZONTAL_FILTER_BAR 是一个重要的配置项&#xff0c;专注于优化和改进Superset中的筛选器条布局与交互。 什么是HORIZONTAL_FILTER_B…

day4 vue2以及ElementUI

创建vue2项目 可能用到的命令行们 vue create 项目名称 // 创建项目 cd 项目名称 // 只有进入项目下&#xff0c;才能运行 npm run serve // 运行项目 D: //切换盘符 更改 Vue项目的端口配置 基础语法 项目创建完成之后&#xff0c;会有一个组件HelloWorld.vue&#xff0c;…

前后端项目打包对比——关于Spring Boot Maven Plugin配置的问题

Spring Boot Maven Plugin 配置详解 Spring Boot Maven Plugin 配置详解1. 添加插件到 pom.xml2. 插件配置2.1 基本配置2.2 配置参数详解默认行为说明简单配置示例为什么这样的配置能工作&#xff1f;进一步说明 2.3 高级配置 3. 使用插件打包应用程序3.1 打包成 JAR 文件3.2 打…

使用 XRDP 远程linux主机

一、简介 XRDP是一个开源的远程桌面协议&#xff08;Remote Desktop Protocol,RDP&#xff09;服务器&#xff0c;采用的是标准的RDP。 官网地址&#xff1a;https://www.xrdp.org/ github地址&#xff1a; https://github.com/neutrinolabs/xrdp/releases XRDP也是C/S架构&…

主成分分析算法

一、算法介绍 主成分分析&#xff1a;用较少的综合变量尽可能多地反映原来信息地统计方法 原理: PCA的主要目标是将特征维度变小&#xff0c;同时尽量减少信息损失。就是对一个样本矩阵&#xff0c;一是换特征&#xff0c;找一组新的特征重新标识&#xff1b;二是减少特征&a…

探索Perl的奇妙世界:入门学习与实战指南

一、Perl语言概述 1.1 Perl的起源与发展 Perl&#xff08;Practical Extraction and Reporting Language&#xff09;是一种高级、解释型、动态编程语言&#xff0c;由Larry Wall于1987年发明。Perl的初衷是作为一种文本处理工具&#xff0c;帮助系统管理员在Unix系统中处理报…

grep命令搜索部分命令

首先 然后可以输入&#xff5c;以及grep命令 比如 bjobs| grep "3075*"bjobs| grep "3075"这个结果是这样的&#xff0c;

「12月·长沙」第四届机器人、自动化与智能控制国际会议(ICRAIC 2024)

随着科技的飞速发展&#xff0c;智能机器人在当今社会的重要性愈发凸显。从制造业的自动化生产线&#xff0c;到医疗领域的手术机器人&#xff0c;再到家庭生活中的智能助手&#xff0c;机器人与人工智能的融合正在改变着我们的生产和生活方式。第四届机器人、自动化与智能控制…

反转链表java实现

力扣206 反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 class Solution {public ListNode reverseList(ListNode head) {ListNode pre null;while(head!null){ListNode next head.next;head.nextpre;pre head;head next;}…

matlab仿真 数字信号载波传输(上)

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第七 章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; clear all nsymbol100000;%每种信噪比下的发送符号数 T1;%符号周期 fs100;%每个符号的采样点数 ts1/fs;%采样时间间隔 t0:ts:…

分类常用的评价指标-二分类/多分类

二分类常用的性能度量指标 精确率、召回率、F1、TPR、FPR、AUC、PR曲线、ROC曲线、混淆矩阵 「精确率」查准率 PrecisionTP/(TPFP) 「召回率」查全率RecallTP/(TPFN) 「真正例率」即为正例被判断为正例的概率TPRTP/(TPFN) 「假正例率」即为反例被判断为正例的概率FPRFP/(TNFP)…

GPT-4o Mini:探索最具成本效益的小模型在软件开发中的应用

随着人工智能技术的迅猛发展&#xff0c;自然语言处理&#xff08;NLP&#xff09;领域也取得了显著的进步。OpenAI 最新发布的 GPT-4o Mini 模型&#xff0c;以其卓越的性能和极具竞争力的价格&#xff0c;成为了广大开发者关注的焦点。作为一名长期关注人工智能及其在软件开发…

基于JSP、java、Tomcat三者的项目实战--校园交易网(1)-项目搭建(前期准备工作)

这是项目的初始页面 接下来我先写下我的初始项目搭建 技术支持&#xff1a;JAVA、JSP 服务器&#xff1a;TOMCAT 7.0.86 编程软件&#xff1a;IntelliJ IDEA 2021.1.3 x64 首先我们打开页面&#xff0c;准备搭建项目的初始准备 1.New Project 2.随后点击Next&#xff0c;勾…

自定义 RAG 工作流:在 IDE 中结合 RAG 编排,构建可信的编码智能体

构建编码智能体并非一件容易的事。结合我们在 AutoDev、ArchGuard Co-mate、ChocoBuilder 等智能体项目的经验&#xff0c;我们开始思考在 Shire 语言中提供一种新的 RAG 工作流。结合我们先前构建的 IDE 基础设施&#xff08;代码生成、代码校验、代码执行等接口&#xff09;&…

【SQL 新手教程 1/20】SQL语言MySQL数据库 简介

&#x1f497; 什么是SQL&#xff1f;⭐ (Structured Query Language) 结构化查询语言&#xff0c;是访问和处理关系数据库的计算机标准语言 无论用什么编程语言&#xff08;Java、Python、C……&#xff09;编写程序&#xff0c;只要涉及到操作关系数据库都必须通过SQL来完成 …

蚂蚁集团Android一面凉经(2024)

蚂蚁集团Android一面凉经(2024) 笔者作为一名双非二本毕业7年老Android, 最近面试了不少公司, 目前已告一段落, 整理一下各家的面试问题, 打算陆续发布出来, 供有缘人参考。今天给大家带来的是《蚂蚁集团Android一面凉经(2024)》。 面试职位: 蚂蚁集团-Android/iOS开发工程师-支…

优秀需求缺陷管理工具推荐及选购技巧

国内外主流的10款需求缺陷管理工具对比&#xff1a;PingCode、Worktile、禅道、Teambition、TAPD、Trello、简道云、Jira 、Bugzilla、Asana。 在选择需求缺陷管理工具时&#xff0c;面对众多选项&#xff0c;许多人可能感到困惑。不管是初创公司还是大型企业&#xff0c;都需要…