第七章 UDP数据报
·7.1 UDP数据报
UDP源端口、UDP目的端口 标示进程;UDP数据报长度;校验和,可选。
UDP非面向连接,不可靠传输,具有较小传输时延。
·7.2 UDP传输过程
SERVER: socket() --> bind() --> recvfrom() --> sendto()-->close()
CLIENT : socket() --> sendto() --> recvfrom() --> close()
服务器端和TCP协议相比少了listen()和accept()两个过程,而客户端不需要建立连接
头文件:<socket.h>
发送和接收函数:
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen); //参数to是指定接收方地址
int recvfrom(int sockfd, const void *buf, int len, unsigned int flags, const struct sockaddr *from, socklen_t *addrlen); //参数from是保存发送方的地址
int send(int sockfd, void *buf, int flags); //使用connect绑定后,发送到缺省地址
int recv(int sockfd, void *buf, int flags); //使用connect绑定后,从缺省地址接收
//socket(AF_INET, SOCK_DGRAM, 0) 创建一个UDP套接字
·7.3 UDP服务器和TCP服务器的比较
使用UDP协议的服务器通常是非面向连接的,因此不用listen和accept的。UDP服务器只需要在其端口上等待客户机发来的数据报即可。
TCP服务器需要和客户端进行连接然后,独占一个连接套接字为其服务;而UDP服务器实际并不和客户机进行连接,UDP服务器仅是接收报文,处理并返回结果。
UDP协议并不关心数据报的可靠性和次序,而希望应用程序保证这些。
TCP服务器中,服务器可以调用getsockpeer函数获取客户机地址信息和端口号,使用getsockname获取套接字对应的IP地址和端口号。
UDP服务器中,可以在recvfrom函数中获取数据报的源地址,使用getsockname获取数据报的端口。如果该UDP服务器有多个IP地址,则无法判断是哪个IP地址获取该数据报。因为UDP协议非面向连接,没有记录接收方的IP地址,此时如果需要明确知道是哪个IP地址收到该数据报,在服务器端需要为每个建立多个UDP套接字绑定在不同网络接口上,通过getsockname获取对应的IP号。
·7.4 UDP的“连接”
UDP是非面向连接,但是也可以调用connect函数对套接字进行绑定到缺省IP地址上。
UDP服务器调用conncet后,并不启用“三次握手”,仅仅记住目的地址和端口。在使用send函数时候会自动按照缺省情况来填写数据报头;同时也可以用sendto函数发送指定位置。
但在接收时候,如果套接字已绑定一个地址和端口,那么UDP服务器只接收该套接字上源地址和端口相同的数据报。如果一个数据报源地址和端口和该套接字设置不同,则丢弃它。而没有绑定地址和端口的UDP套接字可以接收任意来源的数据报。
可以多次调用connect函数修改UDP套接字的绑定地址和端口设置。如果调用connect(AF_UNSPEC, NULL, 0)可以取消对套接字的绑定。
·7.4 UDP应用程序性能改进
1 解决报文的无序问题:
对UDP报文设计一个数据序列号,接收到报文先进行排序后再转交给数据处理程序。
2 解决报文的流量控制问题:
如果客户端接收能力远高于服务器端发送速率,则会让网络传输性能大大下降;
如果客户端接收能力远低于服务器端发送速率,则会让UDP报文大量丢失,消耗服务器端资源。因此需要匹配双方的发送和接收速率,有利用提高整体性能。我们需要在应用程序中建立一种端对端的流量控制反馈机制,由客户端给服务器端发送接收能力的反馈,让服务器端动态调整发送速率。
第八章 域名系统和通用套接字选项
·8.1 域名系统
#include <netdb.h>
struct hostent *gethostbyname(const char *hostname); //指定域名地址来获取IP地址
struct hostent *gethostbyaddr(const char *addr, size_t len, int family); //指定IP地址获取域名
struct hostent{
char *h_name; //主机名
char **h_alias; //主机别名列表
int h_addrtype; //主机地址类型
int h_length; //主机地址长度
char **h_addr_list;//主机IP地址列表
};
int gethostname(char *name, size_t len); //返回本机域名地址 <unistd.h>
int uname(struct utsname *name); //同上,#Include <sys/utsname.h>
struct servent *getservbyname(const char *servname, const char *protoname);//服务名获取端口
struct servent *getservbyport(int port, const char *protoname); //由端口名获取服务信息
·8.2 套接字选项
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *potval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, cosnt void *potval, socklen_t *optlen);
[通用套接字选项,参考手册,略。]
第八章 域名系统和通用套接字选项
·8.1 域名系统
#include <netdb.h>
struct hostent *gethostbyname(const char *hostname); //指定域名地址来获取IP地址
struct hostent *gethostbyaddr(const char *addr, size_t len, int family); //指定IP地址获取域名
struct hostent{
char *h_name; //主机名
char **h_alias; //主机别名列表
int h_addrtype; //主机地址类型
int h_length; //主机地址长度
char **h_addr_list;//主机IP地址列表
};
int gethostname(char *name, size_t len); //返回本机域名地址 <unistd.h>
int uname(struct utsname *name); //同上,#Include <sys/utsname.h>
struct servent *getservbyname(const char *servname, const char *protoname);//服务名获取端口
struct servent *getservbyport(int port, const char *protoname); //由端口名获取服务信息
·8.2 套接字选项
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *potval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, cosnt void *potval, socklen_t *optlen);
[通用套接字选项,参考手册,略。]