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

服务器端

udp_server.c

#include <stdio.h>            // 引入标准输入输出库  
#include <sys/types.h>        // 引入基本系统数据类型  
#include <sys/socket.h>       // 引入socket编程相关的库  
#include <netinet/in.h>       // 引入网络地址相关的库  
#include <arpa/inet.h>        // 引入网络地址转换等函数  
#include <unistd.h>           // 引入UNIX标准函数定义  
#include <string.h>           // 引入字符串操作函数  
  
#define BUF_SIZE 20           // 定义缓冲区大小为20字节  
  
int main(int argc, const char *argv[])  
{  
    int iServer = socket(AF_INET, SOCK_DGRAM, 0); // 创建一个IPv4的UDP socket  
    if(-1 == iServer){  
        puts("----------1、create socket error!"); // 如果socket创建失败,打印错误消息  
        return -1;                                // 并返回-1退出程序  
    }  
    puts("----------1、create socket ok!");        // 如果socket创建成功,打印成功消息  
  
    struct sockaddr_in stServer;                  // 定义一个服务器地址结构体  
    stServer.sin_family = AF_INET;                // 设置地址族为IPv4  
    stServer.sin_port = htons(6666);              // 设置服务器端口为6666,使用htons将主机字节序转换为网络字节序  
    stServer.sin_addr.s_addr = inet_addr("0.0.0.0"); // 设置服务器IP地址为0.0.0.0,表示监听所有可用的网络接口  
  
    int ret = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr)); // 绑定socket到指定的地址和端口  
    if(-1 == ret){  
        puts("----------2、bind error!"); // 如果绑定失败,打印错误消息  
        return -1;                        // 并返回-1退出程序  
    }  
    puts("----------2、bind ok!");        // 如果绑定成功,打印成功消息  
  
    // 开始接收和发送数据  
    char buf[BUF_SIZE] = {0};             // 定义一个缓冲区,并初始化为0  
    struct sockaddr_in stClient;          // 定义一个客户端地址结构体,用于接收客户端的地址信息  
    socklen_t len = sizeof(struct sockaddr); // 定义socklen_t类型的变量,用于存储地址结构的长度  
  
    while(1){ // 无限循环,持续接收和发送数据  
        memset(buf, 0, BUF_SIZE); // 每次循环开始时,清空缓冲区  
  
        // 从客户端接收数据  
        if(recvfrom(iServer, buf, BUF_SIZE, 0, (struct sockaddr *)&stClient, &len) > 0){  
            // 如果接收成功(返回值大于0),则打印接收到的数据和客户端地址信息  
            printf("recvfrom Client ok! data:%s from %s:%d\r\n", buf, inet_ntoa(stClient.sin_addr), ntohs(stClient.sin_port));  
  
            // 将接收到的数据发送回客户端  
            sendto(iServer, buf, strlen(buf), 0, (struct sockaddr *)&stClient, len);  
            // 注意:这里使用strlen(buf)可能不安全,因为如果接收到的数据包含'\0',strlen会提前结束。  
            // 更安全的做法是使用recvfrom的返回值作为要发送的数据长度。  
        }  
    }  
  
    // 注意:由于程序使用了无限循环,所以正常情况下不会执行到这里。  
    // 如果要正常退出,可以在接收数据时加入某种退出条件,如接收到特定的命令或信号。  
  
    return 0; // 程序正常退出,但由于上面的无限循环,这里实际上不会被执行到。  
}
注意:

在使用recvfrom和sendto时,应该检查接收到的数据长度,并使用这个长度来发送数据,而不是简单地使用strlen(buf)。因为recvfrom可能会接收到包含'\0'字符的数据,这会导致strlen(buf)提前结束。
在无限循环中,应该加入一种机制来允许程序在特定条件下退出,例如接收到特定的命令或信号。
使用inet_ntoa函数将客户端的IP地址从网络字节序转换为点分十进制格式,并打印出来,这样可以看到是哪个客户端发送了数据。
socklen_t类型用于存储地址结构的长度,这是必须的,因为不同的系统可能有不同大小的地址结构。

客户端

udp_client.c

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

SERVER_IP 设置为 "0.0.0.0" 是不合适的,因为 "0.0.0.0" 通常用于服务器监听所有网络接口。作为客户端,你应该指定一个具体的服务器IP地址。
UDP是无连接的,所以尝试从同一个地址接收数据可能不是UDP的典型用法。你可能想要接收来自任何源的数据,并相应地处理它们。
上面的 recvfrom 调用没有处理接收到的数据。在实际应用中,你应该检查 recvfrom 的返回值,并处理接收到的数据。
4

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值