一、概述
使用UDP编写的一些常见的应用程序有:DNS(域名系统)、NFS(网络文件系统)和SNMP(简单网络管理协议)。
二、recvfrom和sendto函数
这两个函数类似于标准的read和write函数,不过需要三个额外的参数
#include <sys/socket.h>
ssize_t recvfrom(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, const struct sockaddr* to, socklen_t addrlen);
- 前三个参数,等同于read和write的三个参数,描述符、读写缓冲区指针、读写字节数
- flags14章介绍,当前总是设置为0
- recvfrom的from参数存放发送者的套接字地址结构,由函数返回时填写;sendto的to参数指向一个数据报接受者的协议地址的套接字结构。
- recvfrom的addrlen是指针,同样由函数返回给调用者;sendto中的addrlen是整数,指定地址结构的大小
recvfrom返回0是可以接受的,不像read返回0表示对端关闭连接。因为UDP是无连接的,也就没有所谓的关闭。
三、UDP回射服务器和客户端程序
3.1 服务端程序
#include "unp.h"
void dg_echo(int sockfd, SA* pcliaddr, socklen_t clilen)
{
int n;
socklen_t len;
char mesg[MAXLINE];
while(1){
len = clilen;
n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
Sendto(sockfd, mesg, n, 0, pcliaddr, len);
}
}
int main(int argc, char** argv)
{
int sockfd;
struct sockaddr_in cliaddr, servaddr;
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
dg_echo(sockfd, (SA*)&cliaddr, sizeof(cliaddr));
}
关于dg_echo函数的细节:
首先,该函数永不终止,因为UDP是无连接的,没有EOF之类的东西;大多数TCP服务器是并发的,大多数UDP服务器是迭代的。 对于本套接字,UDP层中隐含有排队发生。每个UDP套接字都有一个接收缓冲区,缓冲区中的数据通过FIFO顺序返回给进程。与TCP服务器不同的是,TCP服务器有多个子进程,每个已连接套接字都有各自的接收缓冲区;而此UDP服务器仅有一个服务器进程,仅有单个套接字用于接收所有到达的数据报并发回所有响应。
3.2 客户端程序
#include "unp.h"
void dg_cli(FILE* fp, int sockfd, const SA* pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + 1];
while(Fgets(sendline, MAXLINE, fp) != NULL){
Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, socklen_t);
n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
recvline[n] = 0;
Fputs(recvline, stdout);
}
}
int main(int argc, char** argv)
{
int sockfd;
struct sockaddr_in servaddr;
if(argc != 2)
err_quit("usage: udpcli <IPadress>");
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
dg_cli(stdin, sockfd, (SA*)&servaddr, sizeof(servaddr));
exit(0);
}
客户端的recvfrom中第五和第六参数都是空指针,这告知内核我们不关心是谁发送的数据报。这样做的风险是任何进程都可以向本客户的IP和端口发送数据报,而被客户读入并误以为是服务器的回应。
四、UDP服务器程序的面临的各种情况
4.1 数据报的丢失
|