当前位置:   article > 正文

基于UDP的网络客户端和服务端模型IO函数

基于UDP的网络客户端和服务端模型IO函数

服务器端

udp_server.c

  1. #include <stdio.h> // 引入标准输入输出库
  2. #include <sys/types.h> // 引入基本系统数据类型
  3. #include <sys/socket.h> // 引入socket编程相关的库
  4. #include <netinet/in.h> // 引入网络地址相关的库
  5. #include <arpa/inet.h> // 引入网络地址转换等函数
  6. #include <unistd.h> // 引入UNIX标准函数定义
  7. #include <string.h> // 引入字符串操作函数
  8. #define BUF_SIZE 20 // 定义缓冲区大小为20字节
  9. int main(int argc, const char *argv[])
  10. {
  11. int iServer = socket(AF_INET, SOCK_DGRAM, 0); // 创建一个IPv4的UDP socket
  12. if(-1 == iServer){
  13. puts("----------1、create socket error!"); // 如果socket创建失败,打印错误消息
  14. return -1; // 并返回-1退出程序
  15. }
  16. puts("----------1、create socket ok!"); // 如果socket创建成功,打印成功消息
  17. struct sockaddr_in stServer; // 定义一个服务器地址结构体
  18. stServer.sin_family = AF_INET; // 设置地址族为IPv4
  19. stServer.sin_port = htons(6666); // 设置服务器端口为6666,使用htons将主机字节序转换为网络字节序
  20. stServer.sin_addr.s_addr = inet_addr("0.0.0.0"); // 设置服务器IP地址为0.0.0.0,表示监听所有可用的网络接口
  21. int ret = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr)); // 绑定socket到指定的地址和端口
  22. if(-1 == ret){
  23. puts("----------2、bind error!"); // 如果绑定失败,打印错误消息
  24. return -1; // 并返回-1退出程序
  25. }
  26. puts("----------2、bind ok!"); // 如果绑定成功,打印成功消息
  27. // 开始接收和发送数据
  28. char buf[BUF_SIZE] = {0}; // 定义一个缓冲区,并初始化为0
  29. struct sockaddr_in stClient; // 定义一个客户端地址结构体,用于接收客户端的地址信息
  30. socklen_t len = sizeof(struct sockaddr); // 定义socklen_t类型的变量,用于存储地址结构的长度
  31. while(1){ // 无限循环,持续接收和发送数据
  32. memset(buf, 0, BUF_SIZE); // 每次循环开始时,清空缓冲区
  33. // 从客户端接收数据
  34. if(recvfrom(iServer, buf, BUF_SIZE, 0, (struct sockaddr *)&stClient, &len) > 0){
  35. // 如果接收成功(返回值大于0),则打印接收到的数据和客户端地址信息
  36. printf("recvfrom Client ok! data:%s from %s:%d\r\n", buf, inet_ntoa(stClient.sin_addr), ntohs(stClient.sin_port));
  37. // 将接收到的数据发送回客户端
  38. sendto(iServer, buf, strlen(buf), 0, (struct sockaddr *)&stClient, len);
  39. // 注意:这里使用strlen(buf)可能不安全,因为如果接收到的数据包含'\0',strlen会提前结束。
  40. // 更安全的做法是使用recvfrom的返回值作为要发送的数据长度。
  41. }
  42. }
  43. // 注意:由于程序使用了无限循环,所以正常情况下不会执行到这里。
  44. // 如果要正常退出,可以在接收数据时加入某种退出条件,如接收到特定的命令或信号。
  45. return 0; // 程序正常退出,但由于上面的无限循环,这里实际上不会被执行到。
  46. }
  47. 注意:
  48. 在使用recvfrom和sendto时,应该检查接收到的数据长度,并使用这个长度来发送数据,而不是简单地使用strlen(buf)。因为recvfrom可能会接收到包含'\0'字符的数据,这会导致strlen(buf)提前结束。
  49. 在无限循环中,应该加入一种机制来允许程序在特定条件下退出,例如接收到特定的命令或信号。
  50. 使用inet_ntoa函数将客户端的IP地址从网络字节序转换为点分十进制格式,并打印出来,这样可以看到是哪个客户端发送了数据。
  51. socklen_t类型用于存储地址结构的长度,这是必须的,因为不同的系统可能有不同大小的地址结构。

客户端

udp_client.c

  1. 这是一个简单的UDP客户端程序,用于向指定的服务器发送数据,并尝试从同一地址接收数据(但通常UDP不是面向连接的,所以接收数据的操作可能不是从同一个服务器或端口来的)。
  2. #include <stdio.h> // 标准输入输出库
  3. #include <stdlib.h> // 标准库,包含exit等函数
  4. #include <string.h> // 字符串处理库
  5. #include <unistd.h> // Unix标准库,包含fgets等函数
  6. #include <arpa/inet.h> // 网络地址转换库
  7. #include <sys/types.h> // 数据类型定义
  8. #include <sys/socket.h> // 套接字库
  9. #include <netinet/in.h> // 网络接口,地址转换等函数
  10. #define SERVER_IP "0.0.0.0" // 服务器IP地址,但"0.0.0.0"通常用于监听所有网络接口,这里作为客户端可能不合适
  11. #define SERVER_PORT 6666 // 服务器端口号
  12. #define BUF_SIZE 1024 // 缓冲区大小
  13. int main() {
  14. int sockfd; // 套接字文件描述符
  15. struct sockaddr_in server_addr; // 服务器地址结构体
  16. char message[BUF_SIZE] = {0}; // 用于存储要发送的消息的缓冲区
  17. // 创建一个UDP套接字
  18. if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
  19. perror("socket creation failed"); // 如果套接字创建失败,打印错误消息
  20. return -1; // 返回-1表示程序异常退出
  21. }
  22. // 初始化服务器地址结构体
  23. memset(&server_addr, 0, sizeof(server_addr)); // 将结构体内容全部设置为0
  24. server_addr.sin_family = AF_INET; // 设置地址族为IPv4
  25. server_addr.sin_port = htons(SERVER_PORT); // 端口号需要转换为网络字节序
  26. socklen_t server_len = sizeof(server_addr); // 定义一个socklen_t类型的变量,用于存储地址结构体的长度
  27. // 将点分十进制的IP地址转换为网络字节序的二进制形式
  28. if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
  29. perror("invalid address/ Address not supported"); // 如果IP地址无效或不支持,打印错误消息
  30. exit(EXIT_FAILURE); // 退出程序
  31. }
  32. // 发送数据到服务器
  33. while(1){ // 无限循环,持续读取输入并发送
  34. fgets(message, BUF_SIZE, stdin); // 从标准输入读取一行文本
  35. ssize_t bytes_sent = sendto(sockfd, message, strlen(message), 0, (const struct sockaddr *)&server_addr, sizeof(server_addr)); // 发送数据
  36. if (bytes_sent == -1) {
  37. perror("sendto failed"); // 如果发送失败,打印错误消息
  38. return -1; // 返回-1表示程序异常退出
  39. }
  40. // 注意:这里尝试从同一个地址接收数据可能不是UDP的典型用法
  41. // 因为UDP是无连接的,接收的数据可能来自任何源
  42. recvfrom(sockfd, message, BUF_SIZE, 0, (struct sockaddr *)&server_addr, &server_len); // 尝试接收数据
  43. // 注意:这里没有处理接收到的数据,通常你会想要检查recvfrom的返回值并处理接收到的数据
  44. // 由于UDP是无连接的,你可能想要使用另一个变量来保存接收地址,或者只是忽略接收地址并处理数据
  45. }
  46. // 注意:由于上面的while循环是无限循环,所以下面的代码永远不会被执行
  47. //printf("Sent %zd bytes\n", bytes_sent);
  48. // 关闭套接字(但上面的代码永远不会到达这里)
  49. close(sockfd);
  50. return 0; // 程序正常退出(但上面的代码永远不会到达这里)
  51. }
  52. 注意:
  53. SERVER_IP 设置为 "0.0.0.0" 是不合适的,因为 "0.0.0.0" 通常用于服务器监听所有网络接口。作为客户端,你应该指定一个具体的服务器IP地址。
  54. UDP是无连接的,所以尝试从同一个地址接收数据可能不是UDP的典型用法。你可能想要接收来自任何源的数据,并相应地处理它们。
  55. 上面的 recvfrom 调用没有处理接收到的数据。在实际应用中,你应该检查 recvfrom 的返回值,并处理接收到的数据。
  56. 4

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/585026
推荐阅读
相关标签
  

闽ICP备14008679号