当前位置: 首页 > news >正文

【Linux网络】UDP套接字【实现英汉转化】

------实现一个简单的英译汉的功能------

一:Udp Server服务端

先看Udp Server服务端的整体框架:

Log lg;enum{SOCKET_ERR = 1,BIND_ERR
};class UdpServer
{
public:UdpServer(uint16_t port):port_(port){}~UdpServer(){}
public:
private:int sockfd_;    // 网络文件描述符std::string ip_;    // IPuint16_t port_;     // 表明服务器进程的端口号bool isrunning;     // 服务器是否运行
};

1.1、socket—创建套接字

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int socket(int domain, int type, int protocol);
  • 第一个参数 domain:协议域,决定了 socket 套接字的地址类型,在通信时必须采用相应的地址。例如 AF_INET 决定了采用 IPv4 地址(32位)与端口号(16位)的组合;AF_INET6 决定了采用 IPv6 地址(128位)与端口号(16位)的组合;AF_UNIX 决定了采用一个绝对路径名作为地址。
  • 第二个参数 type:指定 socket 套接字的类型,是 SOCK_STREAM(流式套接字)还是 SOCK_DGRAM(数据报式套接字)等。
  • 第三个参数 protocol: 协议字段,表明要指定的协议,如 IPPROTO_TCP(TCP传输协议)、PPTOTO_UDP(UDP 传输协议)等等。
  • 返回值:返回一个文件描述符,因为创建套接字的本质就是打开一个文件。
    void Init(){// 1.创建udp socket套接字sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd_ < 0){lg(Fatal, "socket create error, errno: %d, error message: %s", errno, strerror(errno));exit(SOCKET_ERR);}lg(Info, "socket create success, sockfd: %d", sockfd_);}

1.2、bind—将套接字与特定IP和特定端口号port进行绑定

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

第一个参数 sockfd:socket 函数的返回值,一个文件描述符。

第二个参数 addr:要传输的套接字种类,取地址之后需要强转成统一套接字(struct sosckaddr*)

第三个参数 addrlen:传输的套接字的大小 

    void Init(){// 1.创建udp socket套接字sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd_ < 0){lg(Fatal, "socket create error, errno: %d, error message: %s", errno, strerror(errno));exit(SOCKET_ERR);}lg(Info, "socket create success, sockfd: %d", sockfd_);// 2.绑定端口号struct sockaddr_in local;bzero(&local, sizeof(local));   // 将local内部清零// 填充 socket套接字相关字段local.sin_family = AF_INET;     // 当前结构体的地址类型local.sin_port = htons(port_);  // 当前服务器的端口号,须将主机序列转换成网络序列local.sin_addr.s_addr = inet_addr(ip_.c_str());     // 网络中的ip序列是4字节传输的,所以将字符串风格的ip地址转换成可以在网络中传输的4字节Ip网络序列// 开始绑定,bind本质就是将上面一系列参数设置进内核,到指定的套接字中int n = bind(sockfd_, (struct sockaddr*)&local, sizeof(local));if(n < 0){lg(Fatal, "bind error, error: %d, err message: %s", errno, strerror(errno));exit(BIND_ERR);}lg(Info, "bind success, errno: %d, err message: %s", errno, strerror(errno));}

要网络通信, 就要使用到 socket套接字,那么首先就必须定义一个 struct sockaddr_in 类型的结构体对象,该对象中有4个字段:sin_family 字段表示当前结构体的地址类型,sin_port 字段表示端口号,sin_addr 字段表示 IP 地址,sin_zero 字段表示填充。其中 sin_zero 字段一般我们不做处理,只处理其他三个字段即可。首先 sin_family 字段必须和 socket 函数中的 domain 字段保持一致,因为是在网络中通信传输的,所以 sin_port 和 sin_addr 必须将我们当前的 port 和 ip 转换成可以在网络中通信的序列,即使用 htons 函数将端口号从主机转成网络序列,使用 inet_addr 函数将 我们平常方便查看的字符串 ip 情况转成可以在网络中传输的 4 字节的 ip 情况。而 sin_addr 中只有 s_addr 字段,最总就是将该字段填充即可。

所以,所谓 bind,本质就是将我们的套接字与一个特定的 ip 和一个特定的端口号 port 相关联,这样就能与世界上的特定主机上的某一个进程相互连通。

1.3、recvfrom—从服务器的套接字中读取数据

#include <sys/types.h>
#include <sys/socket.h>ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
  • 第一个参数 sockfd:表示当前被读取数据的服务器的套接字 
  • 第二个参数 buf:表示要接收数据的缓冲区
  • 第三个参数 len:要接收的缓冲区的大小
  • 第四个参数 flags:默认设为0,表示阻塞
  • 第五个参数 src_addr:输出型参数,表示获取客户端的套接字信息,因为我们是从服务器的套接字读取数据的,读取的数据给谁呢?毫无疑问是给指定的客户端,所以就要获取到客户端的 ip 和端口号。因为是 UDP 网络通信,所以传的是 struct sockaddr_in 类型的对象地址,并将其强转。
  • 第六个参数 addrlen:struct sockaddr_in 对象的大小
  • 返回值:成功返回获取到数据的字节数,失败返回 -1
    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), 0, (struct sockaddr*)&client, &len);if(n < 0){lg(Warning, "recvfrom error, errno: %d, err message: %s", errno, strerror(errno));continue;}// 此时服务端的数据,我们已经拿到了并将其存至了 inbuffer里面inbuffer[n] = 0;    //在结尾添加'\0'std::string info = inbuffer;   std::string echo_string = "server say@ " + info;}}

1.4、sendto—向指定套接字中发送数据

#include <sys/types.h>
#include <sys/socket.h>ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
  • 第一个参数 sockfd:表示当前被读取数据的服务器的套接字  
  • 第二个参数 buf:要发送的数据缓冲区
  • 第三个参数 len:发送的数据缓冲区的大小
  • 第四个参数 flags:默认设为0
  • 第五个参数 src_addr:接收方的套接字信息。这里的接收方是客户端
  • 第六个参数 addrlen:struct sockaddr_in 对象的大小
  • 返回值:成功返回获取到数据的字节数,失败返回 -1
    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), 0, (struct sockaddr*)&client, &len);if(n < 0){lg(Warning, "recvfrom error, errno: %d, err message: %s", errno, strerror(errno));continue;}// 此时服务端的数据,我们已经拿到了并将其存至了 inbuffer里面inbuffer[n] = 0;    //在结尾添加'\0'std::string info = inbuffer;   std::string echo_string = "server say@ " + info;// 向客户端发送信息ssize_t r = sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (const sockaddr*)&client, len);if(r < 0){lg(Warning, "sendto error, errno: %d, err message: %s", errno, strerror(errno));continue;}}}

netstat -nlup 【net表示网络,stat表示状态,n 表示将所有能显示成数字的信息都以数字的形式显示出来,u表示udp,p 表示PID信息】测试一下:

注意:对于网络信息的发送和接收,我们不需要自己来进行主机转网络,网络转主机来转换的。数据内容会自动由 recvfrom 和 sendto 函数来转换解决。

1.5、绑定 IP 与端口号

云服务器禁止直接 bind 绑定公网 IP

一般建议服务端代码在 ip 绑定时使用 0,0,0,0,表示任意地址绑定。因为这样只要是发送到这台主机的数据信息,我们的这个服务器进程都能收到,在根据端口号向上交付。且一个服务器可能含有多个 ip,所以如果服务端的进程只绑定一个固定的 ip 的话,那么通过其他 ip 发送到这个服务器的数据,这个进程就无法收到该信息。所以,我们一般在设置服务器 ip 时通常设置服务端套接字的 local.sin_addr 字段:

local.sin_addr.s_addr = 0
// 或者
local.sin_addr.s_addr = INADDR_ANY

本地环回地址 IP(通常用来cs)

本地环回地址:127.0.0.1。

任何服务器都可以绑定 127.0.0.1 这个 ip 地址,绑定了这个地址后,该进程不会向网络中发送数据,但还是会通过网络协议栈,通常用来进程本主机的测试。

端口号的绑定

服务端的端口在被绑定时,不能想绑定哪个就绑定哪个。一般端口号 [0,1023] 是系统内定的端口号,有其自己固定的应用层协议使用。例如:http 的端口号是80,https 的端口号是443等等。端口号的选择一般范围在 [1024,65535] 之间。

1.6、Udp Server服务端总代码

// UdpServer.hpp
#pragma once#include <iostream>
#include <string>
#include <string.h>
#include <strings.h>    // bzero
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>#include "Log.hpp"
Log lg;uint16_t defaultport = 8888;
std::string defaultip = "0.0.0.0";
using func_t = std::function<std::string(const std::string&)>;enum{SOCKET_ERR = 1,BIND_ERR
};class UdpServer
{
public:UdpServer(uint16_t &port = defaultport, const std::string &ip = defaultip):sockfd_(0), port_(port), ip_(ip), isrunning(false){}~UdpServer(){if(sockfd_ > 0)close(sockfd_);}
public:void Init(){// 1.创建udp socket套接字sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd_ < 0){lg(Fatal, "socket create error, errno: %d, error message: %s", errno, strerror(errno));exit(SOCKET_ERR);}lg(Info, "socket create success, sockfd: %d", sockfd_);// 2.绑定端口号struct sockaddr_in local;bzero(&local, sizeof(local));   // 将local内部清零// 填充 socket套接字相关字段local.sin_family = AF_INET;     // 当前结构体的地址类型local.sin_port = htons(port_);  // 当前服务器的端口号,须将主机序列转换成网络序列local.sin_addr.s_addr = inet_addr(ip_.c_str());     // 网络中的ip序列是4字节传输的,所以将字符串风格的ip地址转换成可以在网络中传输的4字节Ip网络序列// 开始绑定,bind本质就是将上面一系列参数设置进内核,到指定的套接字中int n = bind(sockfd_, (struct sockaddr*)&local, sizeof(local));if(n < 0){lg(Fatal, "bind error, error: %d, err message: %s", errno, strerror(errno));exit(BIND_ERR);}lg(Info, "bind success, errno: %d, err message: %s", errno, strerror(errno));}void Run(func_t func)   // 让服务器执行特定的函数功能{isrunning = true;char inbuffer[SIZE];while (isrunning){struct sockaddr_in client;socklen_t len = sizeof(client);ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer), 0, (struct sockaddr*)&client, &len);if(n < 0){lg(Warning, "recvfrom error, errno: %d, err message: %s", errno, strerror(errno));continue;}// 此时服务端的数据,我们已经拿到了并将其存至了 inbuffer里面inbuffer[n] = 0;    //在结尾添加'\0'std::string info = inbuffer;   // std::string echo_string = "server say@ " + info;std::string echo_string = func(info);// 向客户端发送信息ssize_t r = sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (const sockaddr*)&client, len);if(r < 0){lg(Warning, "sendto error, errno: %d, err message: %s", errno, strerror(errno));continue;}}}
private:int sockfd_;    // 网络文件描述符std::string ip_;    // IPuint16_t port_;     // 表明服务器进程的端口号bool isrunning;     // 服务器是否运行
};
// UdpServer.cc#include <iostream>
#include <string>
#include "UdpServer.hpp"void Usage(std::string proc)
{std::cout << "proc: " << proc << ", port[1024+]" << std::endl;
}std::string Handler(const std::string& str)
{   std::string res = "Server a message: ";res += str;std::cout << res << std::endl;return res;
}// ./udpserver port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(1);}uint16_t port = std::stoi(argv[1]);UdpServer* usvr = new UdpServer(port);usvr->Init();usvr->Run(Handler);return 0;
}

二:Udp Client客户端

一个端口号只能被一个进程bind,对 server 是如此,对 client 也是。所以客户端也要绑定端口号,只不过不需要用户显示的绑定,而是由操作系统自由随机选择。这样可以避免端口号发生冲突。其次其实对客户端来讲,端口号是多少并不重要,只要能够保证该进程在主机上的唯一性就可以。因为一般都是客户端主动的向服务端发送信息数据。所以客户端一定要能知道服务端的端口号。相反服务端的端口号是确定的。所以在编写客户端代码时,无需进行绑定端口号,直接往服务器中发送数据。在 UDP 首次发送数据的时候,系统会自动地为我们动态bind绑定。

// UdpClient.cc#include "Log.hpp"
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <cstring>
#include <string>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>using namespace std;
Log lg;void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << ", serverip serverport" << std::endl;
}// ./udpclient serverip serverport
int main(int argc, char* argv[])
{if(argc != 3){Usage(argv[0]);exit(0);}// 获取 serverip 和 serverportstd::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 建立套接字,向服务器当中发信息struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);    server.sin_addr.s_addr = inet_addr(serverip.c_str());socklen_t len = sizeof(server);int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){std::cout << "socket error" << std::endl;return 1;}// 套接字创建完毕,直接就可以发信息了std::string message;char buffer[1024];while(true){std::cout << "Please Enter@ ";getline(cin, message);      // 要发送给服务端的数据// 数据有了,给谁发?已经有了服务端的结构体信息了,那就给它发sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, len);// 只要给服务器发了数据,服务器就会很快的识别到并作出处理// 接收服务器数据struct sockaddr_in temp;    //从服务器获取下来给我们应答的数据socklen_t tlen = sizeof(temp);ssize_t s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &tlen);if(s > 0){buffer[s] = 0;std::cout << buffer << std::endl;}}close(sockfd);return 0;
}

三:指令处理

因为服务端收到来自客户端的数据后,会对数据进行加工处理,之后再返回给客户端。那么我们就可以将对数据处理的方法独立出来,作为一个函数来传给服务端的 Run 方法。这样就大大提高了代码的维护性。例如,客户端输入命令,服务端接收到该命令,并将执行结果返还给客户端。

看一个函数 popen:

#include <stdio.h>FILE *popen(const char *command, const char *type);int pclose(FILE *stream);

因为对于指令的执行,需要单独新起一个进程,即需要 fork 创建子进程。而 popen 函数不仅仅能将一个文件打开,还可以调用 fork 函数创建子进程,让子进程进行程序替换执行对应的命令。

  • 参数 command:表示要执行的命令
  • 参数 type:" r "表示读取数据," w "表示写入数据。
  •  返回值:成功返回文件指针,失败返回 NULL,错误原因存至 errno 全局变量中。

所以,只需要将要执行的方法函数做为参数传进 Run 中即可。

// 指令处理
bool SafeCheck(const std::string &cmd)
{std::vector<std::string> v = {"mv", "cp", "git", "su", "top"};for(auto& e : v){if(cmd.find(e) != std::string::npos)    // 找到啦对应的指令{return false;}}return true;
}std::string ExcuteCommand(const std::string& cmd)
{if(!SafeCheck(cmd))     {return "The command is prohibited";}FILE* fp = popen(cmd.c_str(), "r");if(fp == nullptr){perror("popen");return "error";}// 读取执行的结果std::string result;char buffer[4096];while (true){char* ok = fgets(buffer, sizeof(buffer), fp);if(ok == nullptr)break;result += buffer;}pclose(fp);return result;
}// ./udpserver port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(1);}uint16_t port = std::stoi(argv[1]);// UdpServer* usvr = new UdpServer(port);std::unique_ptr<UdpServer> usvr(new UdpServer(port));usvr->Init();// usvr->Run(Handler);usvr->Run(ExcuteCommand);return 0;
}

四:基于 Udp 套接字的群聊

4.1、Server 服务端

因为是群聊,那么聊天的人就不只是一两个人。即再群聊中发送的信息是会被群聊中的所有用户所看到的,所以就需要在客户端维持一个在线用户列表,这里采用 unordered_map 结构,结构的 key 值填入用户的 ip 值,结构的 value 填入用户端的套接字。即当用户进入群聊的时候,客户端进行用户检查,该用户是否在群聊中,若不在群聊中,就将其添加到在线群聊列表中,之后客户端将该用户发送的消息在转发给群聊中的所有用户。

#pragma once#include <iostream>
#include <string>
#include <string.h>
#include <strings.h>    // bzero
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>#include "Log.hpp"
Log lg;uint16_t defaultport = 8888;
std::string defaultip = "0.0.0.0";
using func_t = std::function<std::string(const std::string&)>;enum{SOCKET_ERR = 1,BIND_ERR
};class UdpServer
{
public:UdpServer(uint16_t &port = defaultport, const std::string &ip = defaultip):sockfd_(0), port_(port), ip_(ip), isrunning(false){}~UdpServer(){if(sockfd_ > 0)close(sockfd_);}
public:void Init(){// 1.创建udp socket套接字sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd_ < 0){lg(Fatal, "socket create error, errno: %d, error message: %s", errno, strerror(errno));exit(SOCKET_ERR);}lg(Info, "socket create success, sockfd: %d", sockfd_);// 2.绑定端口号struct sockaddr_in local;bzero(&local, sizeof(local));   // 将local内部清零// 填充 socket套接字相关字段local.sin_family = AF_INET;     // 当前结构体的地址类型local.sin_port = htons(port_);  // 当前服务器的端口号,须将主机序列转换成网络序列local.sin_addr.s_addr = inet_addr(ip_.c_str());     // 网络中的ip序列是4字节传输的,所以将字符串风格的ip地址转换成可以在网络中传输的4字节Ip网络序列// 开始绑定,bind本质就是将上面一系列参数设置进内核,到指定的套接字中int n = bind(sockfd_, (struct sockaddr*)&local, sizeof(local));if(n < 0){lg(Fatal, "bind error, error: %d, err message: %s", errno, strerror(errno));exit(BIND_ERR);}lg(Info, "bind success, errno: %d, err message: %s", errno, strerror(errno));}void CheckUser(const struct sockaddr_in& client, const std::string clientip, const 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, const uint16_t clientport){for(const auto& user : online_user_){std::string message = "[" + clientip + ":" + std::to_string(clientport) + "]#";message += info;socklen_t len = sizeof(user.second);sendto(sockfd_, message.c_str(), message.size(), 0, (const sockaddr*)(&user.second), len);}}// 让服务器执行特定的函数功能// void Run(func_t func)   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), 0, (struct sockaddr*)&client, &len);if(n < 0){lg(Warning, "recvfrom error, errno: %d, err message: %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);      // 给每一个群聊中的用户都发送消息// // 此时服务端的数据,我们已经拿到了并将其存至了 inbuffer里面// inbuffer[n] = 0;    //在结尾添加'\0'// std::string info = inbuffer;   // // std::string echo_string = "server say@ " + info;// std::string echo_string = func(info);// // 向客户端发送信息// ssize_t r = sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (const sockaddr*)&client, len);// if(r < 0)// {//     lg(Warning, "sendto error, errno: %d, err message: %s", errno, strerror(errno));//     continue;// }}}
private:int sockfd_;    // 网络文件描述符std::string ip_;    // IPuint16_t port_;     // 表明服务器进程的端口号bool isrunning;     // 服务器是否运行std::unordered_map<std::string, struct sockaddr_in> online_user_;
};
// Main.cc
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include "UdpServer.hpp"void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << ", port[1024+]" << std::endl;
}// ./udpserver port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(1);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<UdpServer> usvr(new UdpServer(port));usvr->Init();usvr->Run();return 0;
}

4.2、Client 客户端

群聊中的客户端共有两个功能,一是用户发送数据,二是获取群聊中其他用户发送的信息,即本质就是获取服务端中的信息,因为其他用户发送信息数据是往服务器上发送的。所以就需要两个线程来执行两个不同的功能。一线程来发送数据给服务器,二线程是获取服务器上其他用户发送的信息。

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <cstring>
#include <string>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>using namespace std;
Log lg;void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << ", serverip serverport" << std::endl;
}struct ThreadData
{struct sockaddr_in server;int sockfd;std::string serverip;
};// 接收服务端其他用户发送的消息
void* recv_message(void* args)
{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;std::cerr << buffer << std::endl;}}}// 给服务端发送消息
void* sender_message(void* args)
{ThreadData* td = static_cast<ThreadData*>(args);std::string message;socklen_t len = sizeof(td->server);while(true){std::cout << "Please Enter@ ";getline(cin, message);// 给谁发sendto(td->sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)(&td->server), len);}
}int main(int argc, char* argv[])
{if(argc != 3){Usage(argv[0]);exit(0);}// 获取 serverip 和 serverportstd::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());socklen_t len = sizeof(td.server);td.sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(td.sockfd < 0){std::cout << "socket error" << std::endl;return 1;}td.serverip = serverip;pthread_t recvr, sender;    // 两个线程pthread_create(&recvr, nullptr, recv_message, &td);pthread_create(&sender, nullptr, sender_message, &td);pthread_join(recvr, nullptr);pthread_join(sender, nullptr);close(td.sockfd);return 0;
}

五:结语

今天的分享到这里就结束了,今日给大家分享了关于进程的终止情况。如果各位看官觉得还不错的话,可以三连支持一下欧。各位的支持就是捣蛋鬼前进的最大动力!

相关文章:

  • 探索容器技术:Docker与Kubernetes的实践指南
  • ​​IIS文件上传漏洞绕过:深入解析与高效防御​
  • 关于PHP的详细介绍,结合其核心特点、应用场景及2025年的技术发展趋势,以清晰的结构呈现:
  • TCP 的三次握手
  • 构造题(Constructive Problem)
  • 历年福州大学保研上机真题
  • 【论文阅读】KIMI-VL TECHNICAL REPORT
  • C语言中的寄存器:理解与应用
  • 2025年渗透测试面试题总结-匿名[实习]安全工程师(大厂) (2)(题目+回答)
  • OpenGL Chan视频学习-6 How Shaders Work in OpenGL
  • JVM——JNI 的运行机制
  • 【Linux】进程问题--僵尸进程
  • 神经网络加上注意力机制,精度反而下降,为什么会这样呢?注意力机制的本质是什么?如何正确使用注意力机制?注意力机制 | 深度学习
  • xml双引号可以不转义
  • 购物车系统的模块化设计:从加载到结算的全流程拆解
  • SpringBoot返回xml
  • HttpServletRequest 对象包含了哪些信息?
  • 计算机网络总结(物理层,链路层)
  • MongoDB | 零基础学习与Springboot整合ODM实现增删改查
  • docker部署XTdrone
  • 网站代运营服务/接广告推广
  • 电子商务网站开发技术解决方案/谷歌收录提交入口
  • 织梦网站后台默认登陆路径/百度推广方式有哪些
  • 门户网站建设投标书/网站后台管理系统
  • 外贸网站建设费用一般要多少/百度搜索推广平台
  • 网站 空间 是什么/广州网站设计