前言
本章简单介绍了UDP服务器程序的编写,到22.5节再讨论如何增加UDP程序的可靠性
![这里写图片描述](https://img-blog.csdn.net/20170326153728891?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFzaHVpcGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
关注点
- recvfrom函数和sendto函数
- UDP回射服务器程序
- UDP回射客户端程序
- 采用UDP的缺点及改进措施
- UDP的connect函数
- 使用select函数的TCP和UDP回射服务器程序
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);
recvfrom像是accept和read的结合
但是中间多了一个flags参数,将在14章讨论recv、send、recvmsg函数时做介绍。本章都置为0.
UDP回射服务器程序
#include "unp.h"
void dg_echo(int , SA *, socklen_t);
int main(int argc, char const *argv[]) {
int sockfd;
struct sockaddr_in servaddr, cliaddr;
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
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 * pcliaddr, socklen_t clilen){
int n;
socklen_t len;
char msg[MAXLINE];
for( ; ; ){
len = clilen;
n = Recvfrom(sockfd, msg, MAXLINE, 0, pcliaddr, &len);
Sendto(sockfd, msg, n, 0, pcliaddr, len);
}
}
UDP回射客户端程序
#include "unp.h"
void dg_cli(FILE *, int, const SA *, socklen_t);
int main(int argc, char const *argv[]) {
if(argc != 2)
err_quit("usage: ./udpcli01 <IPaddress>");
int sockfd;
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
dg_cli(stdin, sockfd, (SA *)&servaddr, sizeof(servaddr));
}
void dg_cli(FILE * fp,int sockfd, const SA * servaddr, socklen_t servlen){
char buf[MAXLINE];
int n;
socklen_t len = servlen;
for( ; ; ){
if(Fgets(buf, MAXLINE, fp) == NULL)
return ;
else
Sendto(sockfd, buf, strlen(buf), 0, servaddr, servlen);
if((n = Recvfrom(sockfd, buf, n, 0, NULL, NULL)) == 0){
err_quit("connection close");
}
else if(n < 0)
err_quit("Recvfrom error");
buf[n] = 0;
Fputs(buf, stdout);
}
}
UDP服务端是一个典型的迭代器模型,大多数TCP服务器是并发的。对于本套接字,UDP层中隐含有排队发送,每个UDP套接字都有一个接收缓冲区,实行FIFO机制。
采用UDP的缺点及改进措施
1. 数据报丢失:
数据报丢失导致客户阻塞于recvfrom调用,可以设置超时机制解决,14.2节讨论。
2. 验证接收响应的确认:
从新malloc一个sockaddr *变量,在Recvfrom获得其值,比较该变量和原先发送的servaddr,相同就是需要的变量。
3. 服务器进程未运行
服务器主机响应“port unreachable”ICMP消息,但是此时的进程不能识别这个异步错误,它永远阻塞于recvfrom调用,我们需要用connect获取这个ICMP消息。
UDP的connect函数
1. connect只是绑定了目的IP和端口号,使得ICMP返回到指定的套接字
2. 与此同时,具有绑定端口号功能的sendto和recvfrom最好相应改为write和read
3. 多次调用connect可以更改目的端口号和IP或者断开套接字
4. 调用connect可以提高需要多次传输数据的UDP协议的性能(减少了内核中间连接、断开套接字的开销)
使用select函数的TCP和UDP回射服务器程序