《UNIX网络编程 卷1:套接字联网API》学习笔记——基本UDP套接字编程

概述

UDP是无连接不可靠的数据报协议。
使用UDP编写的一些常见的应用程序有:DNS(域名系统)、NFS(网络文件系统)和 SNMP(简单网络管理协议)。

在这里插入图片描述

recvfrom 和 sendto函数

#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);

                                             均返回:若成功则为读或写的字节数,若出错则为-1

前三个参数 sockfd、buff 和 nbytes 等同于 read 和 write 函数的三个参数:描述符、指向读入或写成缓冲区的指针和读写字节数。

使用sendto、recvfrom发送和接收数据量为0的数据报是允许的。
recvfrom返回0,并不意味着收到对端FIN(sendto,recvfrom使用的场景下没有连接的概念)。

UDP 回射服务器程序:main 函数

在这里插入图片描述

#include  "unp.h"
int
main(int argc, char **argv)
{
	int sockfd;
	struct sockaddr_in servaddr, cliaddr;
	/*创建一个UDP套接字*/
	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); /*SERV_PORT 服务器的众所周知端口*/
	bind(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
	/*调用函数 dg_echo 来执行服务器的处理工作*/
	dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
}

UDP 回射服务器程序:dg_echo 函数

#include  "unp.h"
void
dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
{
	int n;
	socklen_t len;
	char mesg[MAXLINE];
	/*该函数是一个简单的循环,它使用 recvfrom 读入下一个到达服务器端口的数据报,再使用 sendto 把它发送回发送者*/
	for ( ; ;)
	{
		len = clilen;
		n = recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
		sendto(sockfd, mesg, n, 0, pcliaddr, len);
	}
}

图中总结了TCP客户/服务器在两个客户与服务器建立连接时情形。
在这里插入图片描述服务器主机上有两个已连接套接字,其中每一个都有各自的套接字接收缓冲区。

下图展示了两个客户发送数据报到UDP服务器的情形。
在这里插入图片描述

UDP 回射客户程序: main 函数

#include  "unp.h"
int
main(int argc, char **argv)
{
	int sockfd;
	struct sockaddr_in servaddr;
	if (argc != 2)
		err_quit("usage: udpcli <IPaddress>");
	/*把服务器的IP地址和端口号填入一个IPv4的套接字地址结构*/
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERV_PORT);
	inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
	/*创建一个UDP套接字,然后调用dg_cli*/
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));
	exit(0);
}

UDP 回射客户程序: dg_cli 函数

#include  "unp.h"
void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
	int n;
	char sendline[MAXLINE], recvline[MAXLINE + 1];
	/*客户处理循环中的四个步骤*/
	/*使用 fgets 从标准输入读入一个文本行*/
	while (fgets(sendline, MAXLINE, fp) != NULL)
	{
	/*使用 sendto 将该文本行发送给服务器*/
		sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
		/*使用 recvfrom 读回服务器的回射*/
		n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
		recvline[n] = 0;       /*null terminate*/
		/*使用 fputs 把回射的文本行显示到标准输出*/
		fputs(recvline, stdout);
	}
}

数据报的丢失

如果一个客户数据报丢失(如,被客户主机与服务器主机之间的某个路由器丢弃),客户将永远阻塞于 dg_cli 函数中的 recvfrom 调用,等待一个永远不会到达的服务器应答。

防止这样永久阻塞的一般方法是给客户的 recvfrom 调用设置一个超时。

验证接收到的响应

recvfrom 返回的IP地址(UDP数据报的源IP地址)不是我们所发送数据报的目的IP地址。

保证应答的源地址与请求的目的地址相同的方法:

  • 一个解决办法是:得到由 recvfrom 返回的IP地址后,客户通过在DNS中查找服务器主机的名字来验证该主机的域名(而不是它的IP地址)。
  • 另一个解决办法是:UDP服务器给服务器主机上配置的每个IP地址创建一个套接字,用bind捆绑每个IP地址到各自的套接字,然后在所有这些套接字上使用 select (等待其中任何一个变得可读),再从可读的套接字给出应答。

学习参考资料:

《UNIX网络编程 卷1:套接字联网API》 第3版

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>