版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Function_Dou/article/details/89915310
如同TCP通信一样, UDP套接字也有专门的发送和接收数据的函数对应. TCP是recv
和send
两个函数负责收发, UDP是通过recvfrom
和sendto
两个函数负责收发数据.
recvfrom和sendto函数
函数原型:
#include <sys/socket.h>
ssize_t recvform(int sockfd, void *buff, size_t nbytes, int flags,
struct sockaddr* from, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,
struct sockaddr* to, socklen_t addrlen);
函数前4个参数和recv
,send
一样. 唯一不一样的就是最后两个参数.
参数 :
-
sockfd : 套接字
-
buff : 发送数据
-
nbytes : 发送字节数
-
flags :
flags值 描述 MSG_OOB 发送或接收外来数据(紧急数据) MSG_DONTROUTE 绕过路由表查找 MSG_DONTWAIT 仅操作非阻塞 MSG_PEEK 窥看外来数据 MSG_WAITALL 等待达到nbytes字节数后才返回
recvfrom :
- from : 对端UDP套接字信息. (无连接状态如果需要回复则不应该为空)
- addrlen : 对端UDP套接字大小. (注意 : addrlen 是指针)
sendto :
- to : 对端UDP套接字信息. (无连接状态如果需要发送不应该为空)
- addrlen : 发送的UDP套接字大小.
写一个长度为0的数据报也是可行的, 这样只发送8字节头部, recvfrom和sendto返回0也是可行的.
通信实现
UDP通信其实并不分客服端和服务端, 不过为了区分一般将发送方称为客服端, 接收方称为服务端.
服务端 : 因为UDP是无连接的, 所以服务端不需要listen
进行监听, 也不需要accept
.
服务端完整代码 : service.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#define error(s) {\
perror(s); \
exit(1); \
}
// 输入 : 端口号
int main(int argc, char *argv[]){
int fdServer;
struct sockaddr_in udpAddr, cliAddr;
memset(&udpAddr, 0, sizeof(udpAddr));
udpAddr.sin_family = AF_INET;
udpAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 使用本机地址
udpAddr.sin_port = htons(atoi(argv[1]));
fdServer = socket(AF_INET, SOCK_DGRAM, 0);
if(fdServer == -1)
error("socket");
if(bind(fdServer, (struct sockaddr *)&udpAddr, sizeof(udpAddr)) == -1)
error("bind");
int size;
char buf[1024];
while(1){
socklen_t socklen = sizeof(udpAddr);
// 接收并获取对端的UDP套接字信息
size = recvfrom(fdServer, buf, sizeof(buf), 0, (struct sockaddr *)&cliAddr, &socklen);
// 通过获取到的UDP套接字将数据回射回去
sendto(fdServer, buf, size, 0, (struct sockaddr *)&cliAddr, socklen);
}
close(fdServer);
return 0;
}
客服端 : 客服端如果没有使用connect
, 则UDP被称为无连接, 如果使用了则称为有连接(本节了解无连接).
完整代码 : client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#define error(s) { \
perror(s); \
exit(1); \
}
// 输入 : IP地址 端口号
int main(int argc, char *argv[]){
int fdClinet;
struct sockaddr_in serAddr, cliAddr;
serAddr.sin_family = AF_INET;
inet_aton(argv[1], &serAddr.sin_addr);
serAddr.sin_port = htons(atoi(argv[2]));
fdClinet = socket(AF_INET, SOCK_DGRAM, 0);
char buf[1024];
int size;
socklen_t clientlen;
while(1){
size = read(STDIN_FILENO, buf, sizeof(buf));
if(size <= 0) break;
// 向指定端口发送数据
sendto(fdClinet, buf, size, 0, (struct sockaddr *)&serAddr, sizeof(serAddr));
clientlen = sizeof(cliAddr);
// 接收并获取对端的UDP套接字信息
size = recvfrom(fdClinet, buf, sizeof(buf), 0, (struct sockaddr *)&cliAddr, &clientlen);
if(size < 0) break;
write(STDOUT_FILENO, buf, size);
}
close(fdClinet);
return 0;
}
服务端 :
./a.out 8080
客服端
./a.out 127.0.0.1 8080
可以看到UDP服务端直接可以建立多个连接并且与多个客服端进行通信, 并不用IO复用来实现.
无连接通信问题
如果你忘了启动服务端, 你会发现客服端依旧被阻塞了, 并且没有报错. 而且发送数据即使没有收到应答的数据也没有报错, 程序仍然处于阻塞状态.
那么这个问题怎么办啊, 虽然我们知道UDP是无连接的, 也不会像TCP那样给予RET
响应, 但是我们还是希望至少向TCP那样通知我们对端不存在之类的信息啊. 别急, 这个就是下节会解决的问题.
小结
recvfrom
和sendto
使用- UDP没有严格的客服端服务端划分, 都可以作为服务端.