
 
 以下是对上述代码的详细解释:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义 sockaddr 结构体类型别名 sa_t
typedef  struct sockaddr       sa_t;
// 定义 sockaddr_in 结构体类型别名 sin_t
typedef  struct sockaddr_in    sin_t;
int main(int argc, char** argv)
{
    // 检查命令行参数是否足够
    if (argc < 3)
    {
        // 输出使用说明,如果命令行参数不足,程序无法正常运行
        fprintf(stderr, "Usage <%s servIP servPort>\n", argv[0]);
        return -1;
    }
    // 1. 创建数据报套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1)
    {
        // 如果套接字创建失败,输出错误信息并退出程序
        perror("socket");
        exit(-1);
    }
    // 2. 绑定服务器端地址信息到套接字上
    sin_t               server = {AF_INET};
    // 将服务器端口号转换为网络字节序
    server.sin_port            = htons( atoi(argv[2]));
    // 将服务器 IP 地址从点分十进制转换为网络字节序
    server.sin_addr.s_addr     = inet_addr(argv[1]);
    int  len  = sizeof(sin_t);
    if (-1 == bind(sockfd, (sa_t*)&server, len))
    {
        // 如果绑定失败,输出错误信息,关闭套接字并退出程序
        perror("bind");
        close(sockfd);
        exit(-1);
    }
    while (1)
    {    
        // 3. 接收客户端发来的网络数据
        char buf[64] = {0};
        sin_t   peer = {0};
        int n  = recvfrom(sockfd, buf, sizeof(buf)-1, 0, (sa_t*)&peer, &len);
        // 在接收的数据末尾添加字符串结束符
        buf[n] = '\0';
        // 打印客户端的 IP 地址、端口号和发送的数据
        printf("[%s:%d]发来数据:%s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), buf);
        // 4. 回复数据给客户端
        const char* resp[] = {"继续努力", "百尺杆头", "百舸争流", "干的漂亮"};
        int sz = sizeof resp / sizeof resp[0];
        // 随机选择一个回复信息
        int  i = rand() % sz;
        // 向客户端发送回复信息
        sendto(sockfd, resp[i], strlen(resp[i]), 0, (sa_t*)&peer, len);
    }
    // 关闭套接字
    close(sockfd);
    return 0;
}
代码解释
-  头文件部分: - <unistd.h>:提供了通用的文件、目录、进程等操作的接口,这里可能用于- close函数。
- <sys/types.h>:包含了基本的系统数据类型。
- <sys/socket.h>:包含了套接字相关的函数和结构体的声明,如- socket、- bind、- recvfrom、- sendto等。
- <netinet/in.h>:包含了- struct sockaddr_in结构体,用于存储 IPv4 地址信息。
- <arpa/inet.h>:包含了- inet_addr和- inet_ntoa等网络地址转换函数。
- <stdio.h>:用于输入输出操作,如- printf和- fprintf。
- <stdlib.h>:提供了一些标准的库函数,如- exit。
- <string.h>:提供了字符串操作函数,如- strlen。
 
-  类型别名部分: - typedef struct sockaddr sa_t;:将- struct sockaddr结构体类型定义为- sa_t,方便后续使用。
- typedef struct sockaddr_in sin_t;:将- struct sockaddr_in结构体类型定义为- sin_t,方便后续使用。
 
-  主函数部分: - 参数检查: 
    - if (argc < 3):检查命令行参数数量,如果小于 3 个,输出使用说明并返回 -1。程序需要两个参数,即服务器的 IP 地址和端口号。
 
- 套接字创建: 
    - int sockfd = socket(AF_INET, SOCK_DGRAM, 0);:创建一个 UDP 套接字。- AF_INET表示使用 IPv4 地址族,- SOCK_DGRAM表示使用数据报(UDP)协议,- 0表示使用默认的协议。
- if (sockfd == -1):检查套接字创建是否失败,如果失败使用- perror输出错误信息并退出程序。
 
- 地址绑定: 
    - sin_t server = {AF_INET};:创建并初始化- sin_t结构体,将地址族设置为- AF_INET。
- server.sin_port = htons(atoi(argv[2]));:将服务器端口号从主机字节序转换为网络字节序。
- server.sin_addr.s_addr = inet_addr(argv[1]);:将服务器 IP 地址从点分十进制表示转换为网络字节序。
- int len = sizeof(sin_t);:计算- sin_t结构体的长度。
- if (-1 == bind(sockfd, (sa_t*)&server, len)):将服务器地址绑定到套接字上,如果绑定失败,输出错误信息,关闭套接字并退出程序。
 
- 数据接收和回复循环: 
    - while (1):程序进入一个无限循环,持续处理客户端请求。
- char buf[64] = {0};:创建一个 64 字节的接收缓冲区,并初始化为 0。
- sin_t peer = {0};:创建一个- sin_t结构体来存储客户端的地址信息。
- int n = recvfrom(sockfd, buf, sizeof(buf)-1, 0, (sa_t*)&peer, &len);:从套接字接收数据,存储在- buf中,- sizeof(buf)-1为接收数据的最大长度,- 0表示无特殊标志,- (sa_t*)&peer存储发送者的地址,- &len存储地址长度。
- buf[n] = '\0';:在接收的数据末尾添加字符串结束符。
- printf("[%s:%d]发来数据:%s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), buf);:打印客户端的 IP 地址、端口号和发送的数据。
- const char* resp[] = {"继续努力", "百尺杆头", "百舸争流", "干的漂亮"};:定义一个回复信息数组。
- int sz = sizeof resp / sizeof resp[0];:计算回复信息数组的元素个数。
- int i = rand() % sz;:随机选择一个回复信息的索引。
- sendto(sockfd, resp[i], strlen(resp[i]), 0, (sa_t*)&peer, len);:向客户端发送回复信息,包括选择的回复信息、信息长度、无特殊标志、客户端地址和地址长度。
 
 
- 参数检查: 
    
-  套接字关闭: - close(sockfd);:关闭套接字,释放资源。
 
注意事项
- 此代码实现了一个简单的 UDP 服务器,可接收客户端发送的数据并随机回复一条信息。
- 代码没有处理 recvfrom或sendto可能出现的错误情况,可使用perror或其他方式处理错误。
- 代码使用了 rand()函数但没有调用srand()函数设置随机数种子,每次运行可能产生相同的随机数序列,可在main函数开始处调用srand(time(NULL));解决。
- 代码没有处理可能出现的缓冲区溢出问题,如客户端发送的数据超过 63 字节,会导致数据截断。
客户端完整代码
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef  struct sockaddr       sa_t;
typedef  struct sockaddr_in    sin_t;
int main(int argc,char** argv)
{
    if(argc < 3)
    {
         fprintf(stderr,"Usage <%s servIP servPort>\n",argv[0]);
         return -1;
    }
    /*1 创建数据报套接字*/
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd == -1)
    {
         perror("socket");
         exit(-1);
    }
    /*2 绑定服务器端地址信息到套接字上*/
    sin_t               server = {AF_INET};
    server.sin_port            = htons( atoi(argv[2]));
    server.sin_addr.s_addr     = inet_addr(argv[1]);
    int  len  = sizeof(sin_t);
    if(-1 == bind(sockfd,(sa_t*)&server,len))
    {
         perror("bind");
         close(sockfd);
         exit(-1);
    }
    while(1)
    {    
      /*3接收客户端发来的网络数据*/
      char buf[64] = {0};
      sin_t   peer = {0};
      int n  = recvfrom(sockfd,buf,sizeof(buf)-1,0,(sa_t*)&peer,&len);
      buf[n] = '\0';
      printf("[%s:%d]发来数据:%s\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port),buf);
      
      /*4.回复数据给客户端*/
      const char* resp[] = {"继续努力","百尺杆头","百舸争流","干的漂亮"};
      int sz = sizeof resp / sizeof resp[0];
      int  i = rand() % sz;
      sendto(sockfd,resp[i],strlen(resp[i]),0,(sa_t*)&peer,len);
    }
    close(sockfd);
    return 0;
}

 客户端代码
 以下是对这段代码的详细解释:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
typedef  struct sockaddr       sa_t;
typedef  struct sockaddr_in    sin_t;
int main(int argc, char** argv)
{
    // 检查命令行参数是否足够
    if (argc < 3)
    {
        // 输出使用说明,如果命令行参数不足,程序无法正常运行
        fprintf(stderr, "Usage <%s servIP servPort>\n", argv[0]);
        return -1;
    }
    // 定义缓冲区大小
    #define BUFFER_SIZE 1024
    // 1. 创建数据报套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1)
    {
        // 如果套接字创建失败,输出错误信息并退出程序
        perror("socket");
        exit(-1);
    }
    // 声明和初始化 server_address
    sin_t server_address = {0};
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(atoi(argv[2]));
    if (inet_pton(AF_INET, argv[1], &server_address.sin_addr) <= 0) 
    {
        // 如果 IP 地址转换失败,输出错误信息并退出程序
        perror("inet_pton");
        exit(EXIT_FAILURE);
    }
    // 声明 client_socket
    int client_socket;
    // 连接到服务器
    client_socket = socket(AF_INET, SOCK_DGRAM, 0);
    if (client_socket == -1) 
    {
        // 如果套接字创建失败,输出错误信息并退出程序
        perror("socket");
        exit(EXIT_FAILURE);
    }
    if (connect(client_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) 
    {
        // 如果连接失败,输出错误信息并退出程序
        perror("connect");
        exit(EXIT_FAILURE);
    }
    // 正确声明和初始化 len
    socklen_t len = sizeof(sin_t);
    while (1)
    {
        // 声明并初始化要发送的字符串
        char *hello = "席甜千到此一游";
        // 发送消息给服务端
        send(client_socket, hello, strlen(hello), 0);
        printf("Message sent to server: %s\n", hello);
        // 声明接收数据的缓冲区
        char buffer[BUFFER_SIZE];
        // 接收服务端的信息
        int valread = recv(client_socket, buffer, BUFFER_SIZE, 0);
        if (valread == -1)
        {
            // 如果接收失败,输出错误信息并继续下一次循环
            perror("recv");
            continue;
        }
        buffer[valread] = '\0';
        printf("Message received from server: %s\n", buffer);
        // 错误:此处不应关闭 sockfd,因为后续可能还需要使用
        // close(sockfd); 
    }
    // 关闭客户端套接字
    close(client_socket);
    return 0;
}

 

代码解释
-  头文件部分: - <stdio.h>:提供标准输入输出函数,如- printf和- fprintf。
- <stdlib.h>:提供了一些标准的库函数,如- exit。
- <string.h>:提供了字符串操作函数,如- strlen。
- <sys/socket.h>:包含了套接字相关的函数和结构体的声明,如- socket、- connect、- send、- recv等。
- <arpa/inet.h>:包含了- inet_pton等网络地址转换函数。
- <unistd.h>:提供了- close等系统调用函数。
 
-  类型别名部分: - typedef struct sockaddr sa_t;:将- struct sockaddr结构体类型定义为- sa_t,方便后续使用。
- typedef struct sockaddr_in sin_t;:将- struct sockaddr_in结构体类型定义为- sin_t,方便后续使用。
 
-  主函数部分: - 参数检查: 
    - if (argc < 3):检查命令行参数数量,如果小于 3 个,输出使用说明并返回 -1。程序需要两个参数,即服务器的 IP 地址和端口号。
 
- 套接字创建: 
    - int sockfd = socket(AF_INET, SOCK_DGRAM, 0);:创建一个 UDP 套接字。- AF_INET表示使用 IPv4 地址族,- SOCK_DGRAM表示使用数据报(UDP)协议,- 0表示使用默认的协议。
- if (sockfd == -1):检查套接字创建是否失败,如果失败使用- perror输出错误信息并退出程序。
 
- 服务器地址初始化: 
    - sin_t server_address = {0};:创建并初始化- sin_t结构体,将地址族设置为- AF_INET。
- server_address.sin_port = htons(atoi(argv[2]));:将服务器端口号从主机字节序转换为网络字节序。
- if (inet_pton(AF_INET, argv[1], &server_address.sin_addr) <= 0):将服务器 IP 地址从点分十进制表示转换为网络字节序,如果转换失败,输出错误信息并退出程序。
 
- 客户端套接字创建和连接: 
    - int client_socket;:声明一个客户端套接字变量。
- client_socket = socket(AF_INET, SOCK_DGRAM, 0);:创建另一个 UDP 套接字作为客户端套接字。
- if (client_socket == -1):检查套接字创建是否失败,如果失败使用- perror输出错误信息并退出程序。
- if (connect(client_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1):尝试将客户端套接字连接到服务器地址,如果连接失败,输出错误信息并退出程序。
 
- 数据传输和接收循环: 
    - socklen_t len = sizeof(sin_t);:计算- sin_t结构体的长度。
- while (1):程序进入一个无限循环,持续发送和接收数据。
- char *hello = "席甜千到此一游";:定义要发送的字符串。
- send(client_socket, hello, strlen(hello), 0);:使用- send函数将字符串发送给服务器。
- printf("Message sent to server: %s\n", hello);:打印发送的消息。
- char buffer[BUFFER_SIZE];:创建一个接收缓冲区。
- int valread = recv(client_socket, buffer, BUFFER_SIZE, 0);:接收服务器发送的数据。
- if (valread == -1):如果接收失败,输出错误信息并继续下一次循环。
- buffer[valread] = '\0';:在接收的数据末尾添加字符串结束符。
- printf("Message received from server: %s\n", buffer);:打印接收到的消息。
 
- 套接字关闭: 
    - close(client_socket);:关闭客户端套接字。
 
 
- 参数检查: 
    
问题和优化点
- 代码中创建了两个 UDP 套接字 sockfd和client_socket,但sockfd似乎没有被使用,可以考虑删除sockfd的创建。
- 在 while循环中错误地调用了close(sockfd);,应该将其移除,因为sockfd未被使用且可能导致后续操作失败。
- 代码没有处理 recv函数返回 0 的情况,可能需要根据具体情况进行处理。
- 可以使用 #define定义一些常量,如服务器端口号和 IP 地址,增强代码的可维护性。
- 可以添加更多的错误处理,如处理服务器不可达等情况。
在这里插入代码片
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
typedef  struct sockaddr       sa_t;
typedef  struct sockaddr_in    sin_t;
int main(int argc, char** argv)
{
    if (argc < 3)
    {
        fprintf(stderr, "Usage <%s servIP servPort>\n", argv[0]);
        return -1;
    }
    // 定义缓冲区大小
    #define BUFFER_SIZE 1024
    // 1. 创建数据报套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1)
    {
        perror("socket");
        exit(-1);
    }
    // 声明和初始化 server_address
    sin_t server_address = {0};
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(atoi(argv[2]));
    if (inet_pton(AF_INET, argv[1], &server_address.sin_addr) <= 0) 
    {
        perror("inet_pton");
        exit(EXIT_FAILURE);
    }
    // 声明 client_socket
    int client_socket;
    // 连接到服务器
    client_socket = socket(AF_INET, SOCK_DGRAM, 0);
    if (client_socket == -1) 
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    if (connect(client_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) 
    {
        perror("connect");
        exit(EXIT_FAILURE);
    }
    // 正确声明和初始化 len
    socklen_t len = sizeof(sin_t);
    while (1)
    {
        // 声明并初始化要发送的字符串
        char *hello = "席甜千到此一游";
        // 发送消息给服务端
        send(client_socket, hello, strlen(hello), 0);
        printf("Message sent to server: %s\n", hello);
        // 声明接收数据的缓冲区
        char buffer[BUFFER_SIZE];
        // 接收服务端的信息
        int valread = recv(client_socket, buffer, BUFFER_SIZE, 0);
        if (valread == -1)
        {
            perror("recv");
            continue;
        }
        buffer[valread] = '\0';
        printf("Message received from server: %s\n", buffer);
		close(sockfd);
    }
 /*    close(sockfd); */
    close(client_socket);
    return 0;
}

![[cg] android studio 无法调试cpp问题](https://i-blog.csdnimg.cn/direct/c9d052a430c94745929ad3cc97bb46a3.png)





![[Pro Git#2] 分支管理 | branch fix_bug , feature | 处理合并冲突](https://i-blog.csdnimg.cn/img_convert/fdaf8ba4cf4a03ba04dd70c5e84b7ffc.png)











