概述
UDP编程和TCP编程有着本质的差异:UDP是无连接不可靠的数据报协议,非常不同于TCP提供的面向连接的可靠字节流
点我获取本文源码
recvfrom和sendto函数
1 |
|
- recvfrom返回0是可以接受的(不像read函数返回0代表对端关闭),因为UDP是无连接的
- recvfrom调用的时候如果不关心发送端的地址,则后两个参数from和addrlen可以置为NULL。要注意的是,如果from传的是NULL,addrlen也必须传NULL(可能是由于recvfrom函数的内部实现导致的)
- recvfrom和sendto都可用于TCP,但通常不这么用
UDP实现简单的回射C/S程序
服务器程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void dg_echo(int sockfd, SA *cliaddr, socklen_t clilen);
int main(int argc, char **argv){
int sockfd;
struct sockaddr_in servaddr, cliaddr;
//第二个参数指定为SOCK_DGRAM,标识创建一个UDP套接字
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9748);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//为套接字绑定众所周知的端口号,以及指定接收哪些网络接口的请求
Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
}
void dg_echo(int sockfd, SA *cliaddr, socklen_t clilen){
int n;
socklen_t len;
char mesg[MAXLINE];
for( ; ; ){
len = clilen;
//接收客户号段发送的消息
n = Recvfrom(sockfd, mesg, MAXLINE, 0, cliaddr, &len);
//回射
Sendto(sockfd, mesg, n, 0, cliaddr, len);
}
}- 这段程序和TCP服务端回射程序的实现逻辑类似,不一样的是收发消息用的是Recvfrom和Sendto
- 需要注意的是:UDP是无连接的,所以如果要给客户回发消息,就必须记录下客户的地址信息,Recvfrom的后两个参数就是用来记录客户的地址信息的,回发消息给客户的时候,用这个记录的地址信息即可
- UDP层中隐含排队,所有收到的包都在队列中排队,recvfrom每次从队头取出消息处理,没有消息则阻塞(消息队列长度是有限制的,可通过SO_RCVBUF套接字选项修改)
客户端程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void dg_cli_my(FILE *fp, int sockfd, SA *servaddr, socklen_t len);
int main(int argc, char **argv){
int sockfd;
struct sockaddr_in servaddr;
if(argc != 2)
err_quit("usage: ./udpcli01 <IPAddress>\n");
//创建一个UDP套接字
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9748);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
dg_cli_my(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));
}
void dg_cli_my(FILE* fp, int sockfd, SA *servaddr, socklen_t len){
int n;
char sendline[MAXLINE], recvline[MAXLINE];
//从控制台接收用户输入,并发送给服务器
//接着接收服务器回射的消息,并输出到控制台
while( (Fgets(sendline, MAXLINE, fp)) != NULL){
Sendto(sockfd, sendline, strlen(sendline), 0, servaddr, len);
n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
recvline[n] = 0;
Fputs(recvline, stdout);
}
}- 本段程序和TCP回射客户端程序的实现逻辑类似,同样的区别就在于对网络数据的读写操作不同
- 需注意的是:UDP是无连接的,所以客户端在调用Sendto的时候并不要求一定要有一个服务器在监听。即客户端只是负责把数据发出去,不管是否有人收到
TCP和UDP回射程序对比
两个客户的TCP客户/服务器小节
- 服务器每收到一个客户端的连接,便派生一个子进程,并将连接交付给子进程维护,父进程接着回去监听客户端的请求。在子进程中处理特定客户端的回射服务
两个客户的UDP客户/服务器小节
- UDP服务器将收到的多个数据报置于套接字接收缓冲区中,再逐个取出,并根据Datagram报文中的发送端地址分别回复
对比
- UDP回射服务程序是永不终止的,不像TCP中类似EOF的东西。而TCP回射服务程序,可以通过客户关闭连接导致服务端子进程中读到EOF,进而子进程退出。
- 大多数TCP服务器是并发的,而大多数UDP服务器是迭代的(TCP需要用连接标识不同的客户,并与之通信,所以一般做成并发。而UDP不需要维持连接,对收到的任何一个包进行处理都可以知道是谁发出的,对应处理即可,所以一个进程里面可以处理所有的请求)
- TCP通信必须要一个长期在线的服务端,并且服务端要先于客户端启动,而UDP通信则不用
- 可以看出,利用UDP进行通信的时候,并不要求服务器在客户端之前启动(即即便客户端比服务器先启动也是不会报错的,指示发出去的消息没有响应而已),只是服务器在启动之前,客户端无法获取到服务,一旦服务器启动,客户端变可以获取到服务。
- 相比之下,TCP进行进行通信的的时候,必须服务端先于客户端启动。因为TCP是有连接的,其通信依赖于一个已经建立好的连接,一旦连接无法建立,变无法进行通信(虽然可以在连接失败时反复尝试,但服务器没启动之前,客户端的connect函数都是返回错误的)
UDP程序栗子小节