第二部分基本套接口编程_第1页
第二部分基本套接口编程_第2页
第二部分基本套接口编程_第3页
第二部分基本套接口编程_第4页
第二部分基本套接口编程_第5页
已阅读5页,还剩87页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、基本套接字编程 主要内容主要内容n套接字基础n套接字地址结构n套接字基本函数n简单TCP套接字编程n简单UDP套接字编程 套接字基础套接字基础n网络编程接口有两个发展方向:Socket,TLIn套接字是一种网络API,程序员可以用之开发网络程序。套接字接口本意在于提供一种进程间通信的方法,使得在相同或不同的主机上的进程能以相同的规范进行双向信息传送。进程1进程2网络编程接口(socket)网络通信协议服务接口(TCP/IP)底层通信协议和网络介质套接字类型套接字类型n套接字支持多种通信协议:nUnix:Unix系统内部协议nINET:IP版本4nINET6:IP版本6nLinux支持多种套接字

2、类型,即应用程序希望的通信服务类型nSOCKET_STREAM:双向可靠数据流,对应TCPnSOCKET_DGRAM:双向不可靠数据报,对应UDPnSOCKET_RAW:是低于传输层的低级协议或物理网络提供的套接字类型,可以访问内部网络接口。例如接收和发送ICMP报套接字地址结构(IPv4)n大多数套接字函数需要一个指向套接字地址结构的参数,每个协议族都定义它自己的套接字地址结构,一般以”sockaddr_”开头,并以协议簇为后缀。(netinet/in.h)typedef uint32_t in_addr_t;typedef uint16_t in_port_t;typedef unsign

3、ed short sa_family_t;struct in_addr in_addr_t s_addr;struct sockaddr_in uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; char sin_zero8;struct sockaddr_in serverbzero(&server,sizeof(server)server.sin_family=AF_INET; server.sin_port=htons(PORT); server.sin_add

4、r.s_addr = htonl(INADDR_ANY).nIPv6地址为128位。(netinet/in.h)套接字地址结构(IPv6)typedef uint16_t in_port_t;typedef unsigned short sa_family_t;struct in6_addr uint8_ts6_addr16;struct sockaddr_in6 uint8_t sin6_len; sa_family_t sin6_family; in_port_t sin6_port; uint32_t sin6_flowinfo; struct in6_addr sin6_addr;si

5、n6_flowinfo成员分成三个字段:n低24位是流量标号;n下4位是优先级;n再下4位保留IPv4与IPv6地址结构比较长度AF_INET616位端口号32位流标签128位IPv6地址sockaddr_in6 长度AF_INET16位端口号32位IP地址未用sockaddr_in 固定长度(固定长度(16字节)字节)固定长度(固定长度(24字节)字节)n套接口地址结构仅在给定主机上使用,虽然有些成员用在不同主机间通信,但结构本身并不参与通信。通用套接字地址结构通用套接字地址结构n由于套接字函数需接收来自不同协议的地址结构,ANSI的办法是使用通用的指针类型,即(void *).套接字函数方

6、法是定义一个通用的套接字地址结构。struct sockaddr uint8_t sa_len; sa_family_t sa_family; char sa_data14;这就要求调用套接字函数时,需将指向特定于协议的地址结构的指针类型转换成指向通用的地址结构的指针, 如: struct sockaddr_in serv bind(sockfd, (struct sockaddr *)&serv,sizeof(serv);字节排序函数n为保证”大端“和”小端“字节序的机器之间能相互通信,需在发送多字节整数时,将主机字节序转换成网络字节序,或反之。高序字节低序字节MSB(最高有效位)1

7、6位值 LSB高序字节低序字节内存地址增大方向内存地址增大方向小端字节序大端字节序网络字节序字节排序函数(cont.)#include uint16_t htons(uint16_t hostshort)uint32_t htonl(uint32_t hostlong) 均返回:网络字节序值uint16_t ntohs(uint16_t netshort)uint32_t ntohl(uint32_t netlong) 均返回:主机字节序值h:主机 n:网络 s:短整数 l:长整数字节操纵函数n#include void bzero(void *dest, size_t nbytes);voi

8、d bcopy(const void *src, void *dest, size_t nbytes);int bcmp(const void *src, void *dest, size_t nbytes); /*返回0则相同,非0不相同*/ 上述三个函数源自BSDvoid *memset(void *dest, int c, size_t len);void *memcpy(void *dest, const void *src, size_t nbytes);int memcmp(const void *ptr1, const void *ptr2, size_t nbytes)上述三个

9、函数属于ANSI C#include #include main() char *s1=Hello, Programmers!; char *s2=Hello, programmers!; int r; r=memcmp(s1,s2,strlen(s1); if(!r) printf(s1 and s2 are identical); else if(r0) printf(s1 less than s2); else printf(s1 greater than s2); Memset函数#include #include int main() char buffer = This is a

10、test of the memset function; printf( Before: %sn, buffer ); memset( buffer, *, 4 ); printf( After: %sn, buffer ); Before: This is a test of the memset function After: * is a test of the memset function 地址转换函数#include 将点分十进制数串转换成32位网络字节序二进制值。int inet_aton(const char *cp, struct in_addr *inp); 返回:1-串有

11、效,0-串有错in_addr_t inet_addr(const char *cp); 返回:若成功,返回32位二进制的网络字节序地址,若有错,则返回一个 常值INADDR_NONE(32位均为1). ninet_aton函数将cp所指的字符串(点分十进制数串,如192.168.0.1)转换成32位的网络字节序二进制,并通过指针inp来存储。这个函数需要对字符串所指的地址进行有效性验证。但如果cp为空,函数仍然成功,但不存储任何结果。ninet_addr进行相同的转换,但不进行有效性验证,也就是说,所有232种可能的二进制值对inet_addr函数都是有效的。地址转换函数(续)地址转换函数(续

12、)char *inet_ntoa(struct in_addr inaddr); 返回:指向点分十进制数串的指针n函数inet_ntoa将32位的网络字节序二进制IPv4地址转换成相应的点分十进制数串。但由于返回值所指向的串留在静态内存中,这意味着函数是不可重入的。n需要注意的是这个函数是以结构为参数的,而不是指针。n上述三个地址转换函数都只能处理IPv4协议,而不能处理IPv6地址。地址转换函数(地址转换函数(cont.)n#include int inet_pton(int family, const char *strptr, void *addrptr); 返回:1-成功,0输入无效,

13、-1:出错const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len); 返回:指向结果的指针成功,NULL出错字母P和N分别代表presentation(地址的表达格式)和numeric(数值格式)。nfamily参数可以是AF_INET,也可以是AF_INET6。n长度参数len是目标的大小,如果太小无法容纳表达格式结果,则返回一个空指针。另外,目标指针调用前必须先由调用者分配空间。inet_pton的实现(只支持的实现(只支持IPv4)int inet_pton(int family, c

14、onst char *strptr, void *addrptr) if (family = AF_INET) struct in_addr in_val; if (inet_aton(strptr, &in_val) memcpy(addrptr, &in_val, sizeof(struct in_addr); return (1); return(0); else errno = EAFNOSUPPORT; /*以不被支持的地址族做为family的参数*/ return (-1); Tcp套接字TCP套接字编程(cont.)socket()bind()listen()ac

15、cept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客户连接请求TCP服务器端TCP客户端基本套接字函数socket#include int socket(int family, int type, int protocol) 返回:非负套接字(sockfd)成功;-1出错。family:协议族;type:套接字类型; protocol:如调用者不想指定,一般为0 ,表示缺省,原始套接字除外。 family typeAF_INET IPv4协议SOCK_STREAM 字节流套接口AF_INET6IPv6协议

16、SOCK_DGRAM 数据报套接口AF_LOCALunix域协议SOCK_RAW 原始套接口AF_ROUTE 路由套接口AF_KEY 密钥套接口Protocol:指明此socket请求所使用的协议,可以使用如下相关符号常数来表示。IPPROTO_TCP:表示TCP协议IPPROTO_UDP:表示UDP协议 AF_INET AF_INET6 AF_LOCAL AF_ROUTE AF_KEY SOCK_STREAM TCP TCP Yes SOCK_DGRAM UDP UDP Yes SOCK_RAW IPv4 IPv6 Yes Yes #include int sockfd; /crteate

17、socketif(sockfd = socket(AF_INET, SOCK_STREAM, 0) = = -1) /handle exceptionTCP套接字编程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客户连接请求TCP服务器端TCP客户端基本套接字函数bind#include int bind(int sockfd, const struct sockaddr *addr, socklen_len len) 返回:0成功;-1出

18、错并置errnon该函数指明套接字将使用本地的哪一个协议的参数进行数据传送(IP地址和端口号),注意:协议地址addr是通用地址。nLen是该地址结构(第二个参数)的长度。n一般而言,服务器调用此函数,而客户则很少调用它。 绑定地址时,可以指定地址和端口号,也可以指定其中之一,甚至一个也不指定。通配地址:INADDR_ANY,其值一般为0,它通知内核选择IP地址。IP地址 端口 结果通配地址 0 内核选择IP地址和端口号通配地址 非0 内核选择IP地址,进程指定端口本地IP 0 进程指定IP地址,内核选择端口本地IP 非0 进程指定IP地址和端口号 若指定端口号为0,调用函数bind时,内核选

19、择一个临时端口(在实际中,端口号都要指定);但若指定一个通配IP地址,则直到套接字已连接(TCP)或数据报已在套接字上发出(UDP),内核才选择一个本地IP地址。bind函数(续)n另外,需要注意以下几点:n参数addr中的相关字段在初始化时,必须是网络字节序;n如果由内核来选择IP地址和临时端口号,函数并不返回所选择的值。为了获得这些值,进程必须调用getsockname函数getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *localaddrlen); n函数bind返回的一个常见错误是:所绑定的地址已被其它进程使用

20、,此时errno的值为EADDRINUSE, 我们可以通过设置套接字选项SO_REUSEADDR。setsockopt函数函数#include int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); 返回:0OK;-1出错。nsockfd必须指向一个打开的套接字描述字。nLevel是选项所在的层及协议,有如下值: SOL_SOCKET (通用套接字 ) IPPROTO_TCP(传输层,TCP协议) IPPROTO_IP(网际层,IP协议)nOptname是所要操作的选项名

21、。(SO_、IP_、TCP_是选项名的前缀,不同的level有不同的前缀。)noptval是一个指向变量的指针,通过它设置选项的新值,此变量的大小由最后一个参数指定。bind函数的用法struct sockaddr_in addr;int port = 1234;int opt = 1;setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt); bzero(&addr,sizeof(addr);addr.sin_family = AF_INET;addr.sin_addr.s_addr = htonl(INADDR_A

22、NY);addr.sin_port = htons(port);if (bind(fd, (struct sockaddr *)&addr, sizeof(addr) = -1)/* 错误处理 */TCP套接字编程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客户连接请求TCP服务器端TCP客户端基本套接字函数listen#include int listen(int sockfd, int backlog) 返回:0成功;-1出错

23、并置errno值;n函数listen仅被服务器调用,它完成两件事情:n函数listen将未连接的套接字转化成被动套接字,指示内核应接受指向此套接字的连接请求;n函数的第二个参数规定了内核为此套接字排队的最大连接个数;n对于给定的监听套接字,内核要维护两个队列n未完成连接队列n已完成连接队列n两个队列之和不超过backlog;客户服务器Connect调用在未完成队列建立条目SYN JSYN K, ack J+1ack K+1该条目从未完成队列移至已完成队列,acceptConnect返回TCP三路握手和监听套接口的两个队列listen函数(续)三路握手完成两队列之和不能超过backlog已完成连

24、接队列(ESTABLISHED状态)未完成连接队列(SYN_RCVD状态)新到达的SYN分节服务器TCPacceptlisten函数(续)另外几点说明:n不同的实现对backlog有不同的解释,如源自Berkeley的实现将backlog增加一个模糊因子,把它乘以1.5,再作为两个队列之和;n不要把backlog定义为0,因为有些实现允许1个连接排队,而有些实现不允许有连接排队;n当一个客户SYN到达时,若两个队列都是满的,tcp就忽略此分节,且不发送RST。这是因为,这种情况是暂时的,客户tcp将重发SYN,期望不久的将来就能在队列中找到空闲条目。要是TCP服务器发送一个RST,客户的con

25、nect函数立即返回一个错误,强制应用进程处理这种情况,而不是让TCP正常的重传机制处理。客户区别不了两种情况,作为SYN的响应,意为“此端口上没有服务器”的RST和意为“有服务器在此端口上但其队列满”的RST。TCP套接字编程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客户连接请求TCP服务器端TCP客户端基本套接字函数connect#include int connect(int sockfd, const struct sockad

26、dr *addr, socklen_t addrlen); 返回:0成功;-1出错;n函数connect激发TCP的三路握手过程;仅在成功或出错返回;错误有以下几种情况:n如果客户没有收到SYN分节的响应(总共75秒,这之间需要可能需要重发若干次SYN),则返回ETIMEDOUT。n如果对客户的SYN的响应是RST,则表明该服务器主机在指定的端口上没有进程在等待与之相连。函数返回错误ECONNREFUSED;n如果客户发出的SYN在中间路由器上引发一个目的地不可达ICMP错误,客户上的内核保存此消息,并按第一种情况,连续发送SYN,直到规定时间,返回保存的消息(即ICMP错误)作为EHOSTU

27、NREACH或ENETUNREACH错误返回给进程。connect函数(续)n客户在调用connect前不必非得调用bind函数,此时,内核会选择一个合适的IP地址和临时端口号;n如果函数connect失败,则套接字不可再用,必须关闭。不能再对此套接字再调用函数connect。int sockfd;struct sockaddr_in server;bzero(&server, sizeof(server);server.sin_family = AF_INET;server.sin_port = htons(1234);server.sin_addr.s_addr= inet_add

28、r(“127.0.0.1”);if (connect(sockfd, (struct sockaddr *)&server, sizeof(server) = -1) /handle exceptionTCP套接字编程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客户连接请求TCP服务器端TCP客户端基本套接字函数accept#include int accept(int sockfd, struct sockaddr *cliad

29、dr, socklen_t *addrlen); 返回:非负描述字(connfd)OK;-1出错;naccept函数由TCP服务器调用;从已完成连接队列头返回下一个已完成连接;如果该队列空,则进程进入睡眠状态。n函数返回的套接字为已连接套接字,应与监听套接字区分开来n该函数最多:一个既可能是新套接字也可能是错误指示的整数,一个客户进程的协议地址(由cliaddr所指),以及该地址的大小(这后两个参数是值结果参数);也就是说,服务器可以通过参数cliaddr来得到请求连接并获得成功的客户的地址和端口号;accept函数示例struct sockaddr_inservaddr, cliaddr;s

30、ocklen_tlen;intlistenfd, connfd;connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);if (connfd = -1)/* 出错处理 */printf(“ connection from %s”, inet_ntop(AF_INET, cliaddr.sin_addr, buff, sizeof(buf);TCP套接字编程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write(

31、)read()close()阻塞直到接收到客户连接请求TCP服务器端TCP客户端基本套接字函数close#include int close(int sockfd); 返回:0OK;-1出错;nclose函数缺省功能是将套接字做上“已关闭”标记,并立即返回到进程。这个套接字不能再为该进程所用。n正常情况下,close将引发向TCP的四分节终止序列,但在终止前将发送已排队的数据;n如果套接字描述符访问计数在调用close后大于0(在多个进程共享同一个套接字的情况下),则不会引发TCP终止序列(即不会发送FIN分节);基本套接字函数-shutdown#include int shutdown(in

32、t sockfd, int howto); 返回:0OK;-1出错,并置相应的errno的值;n该函数立即发送FIN分节(无论其访问计数是否大于0)。shutdown根据参数howto关闭指定方向的数据传输;nSHUT_RD:关闭连接的读这一半,不再接收套接字中的数据且现留在接收缓冲区的数据作废;nSHUT_WR :关闭连接的写这一半(半关闭),当留在套接字发送缓冲区中的数据都被发送,后跟tcp连接终止序列,不管访问计数是否大于0;此后将不能在执行对套接字的任何写操作;nSHUT_RDWR:连接的读、写都关闭,这等效于调用shutdown两次,一次调用是用SHUT_RD,第二次用SHUT_WR

33、。数据数据FIN数据和FIN的确认数据数据FIN数据和FIN的确认writewriteshutdownRead返回大于0Read返回大于0Read返回0writewritecloseRead返回大于0Read返回0Read返回大于0客户服务器调用shutdown关闭一半TCP连接基本套接字函数read#include int read(int fd, char *buf, int len);返回:大于0读写字节大小;-1出错;n调用函数read时,有如下几种情况:n套接字接收缓冲区接收数据,返回接收到的字节数;ntcp协议收到FIN数据,返回0;ntcp协议收到RST数据,返回1,同时errn

34、o为ECONNRESET;n进程阻塞过程中接收到信号,返回1,同时errno为EINTR。read(connfd,buff,strlen(buff);基本套接字函数write#include int write(int fd, char *buf, int len); 返回:大于0读写字节大小;-1出错;n调用函数write,有如下几种情况:n套接字发送缓冲区有足够空间,返回发送的字节数;ntcp协议接收到RST数据,返回1,同时errno为ECONNRESET; ;n进程阻塞过程中接收到信号,返回1,同时errno为EINTR。write(connfd,buff,strlen(buff);数

35、据传输函数send#include #include ssize_t send (int fd, const void *msg, size_t len, int flags); 返回:非0发送成功的数据长度;-1出错;nflags 是传输控制标志,其值定义如下:n0:常规操作,如同write()函数nMSG_OOB,发送带外数据(TCP紧急数据)。nMSG_DONTROUTE:忽略底层协议的路由设置,只能将数据发送给与发送机处在同一个网络中的机器上。数据传输函数recv#include #include ssize_t recv(int fd, void *buf ,size_t len,

36、int flags); 返回:大于0表示成功接收的数据长度;0: 对方已关闭,-1:出错。nflags是传输控制标志,其值定义如下:n0:常规操作,如同read()函数;nMSG_PEEK:只查看数据而不读出数据,后续读操作仍然能读出所查看的该数据;nMSG_OOB:忽略常规数据,而只读带外数据;nMSG_WAITALL:recv函数只有在将接收缓冲区填满后才返回。TCP套接字编程(cont.)socket()bind()listen()accept()read()write()close()socket()connect()write()read()close()阻塞直到接收到客户连接请求T

37、CP服务器端TCP客户端域名解析函数gethostbyname#include struct hostent *gethostbyname(const char *hostname) 返回:非空指针成功;空指针出错,同时设置h_errorn该函数既可解析IPv4地址,也可解析IPv6地址;n该函数既可接收域名,也可接收点分十进制参数n当hostname为点分十进制时,函数并不执行网络查询,而是直接将其拷贝到结果字段中。n此函数返回的非空指针指向下面的hostent结构结构hostentstruct hostent char *h_name; /* official (canonical) na

38、me of host */char *h_aliases;/* pointer to array of pointers to alias names */int h_addrtype;/* host address type: AF_INET or AF_INET6 */inth_length;/* length of address :4 or 16 */char *h_addr_list;/* ptr to array of ptrs with IPv4 or IPv6 address */;#define h_addr h_addr_list0 /* first address in

39、list */nstruct hostent * he;n*(struct in_addr *)he-h_addr)gethostbyname 返回的信息h_nameh_aliasesh_addrtypeh_lengthh_addr_listhostent 正式主机名 0别名1 0别名2 0IP地址1IP地址2IP地址3NULLNULLAF_INETin_addr in_addr in_addr if (he = gethostbyname(argv1) = NULL) perror(gethostbyname error.); exit(1); if (fd = socket(AF_INET

40、, SOCK_STREAM, 0) = -1) perror(Create socket failed.); exit(1); bzero(&server, sizeof(server); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr = *(struct in_addr *) he-h_addr);char *fgets(char *buf, int num, FILE *fp); n从fp指向的文件读取一个长度为num-1的字符串,存入起始地址为buf的空间。返回地址buf,若

41、遇文件结束或出错,返回NULL。n其中,fp使用stdin就是从标准输入读取数据 char sendlineMAXDATASIZE;fgets(sendline, MAXDATASIZE, stdin) #includemain()char s80;fputs(fgets(s,80,stdin),stdout); TCP套接字编程n实现TCP套接字基本步骤分为服务器端和客户端两部分:n服务器端n创建套接字;n绑定套接字;n设置套接字为监听模式,进入被动接受连接状态;n接受请求,建立连接n读写数据终止连接TCP套接字编程(cont.)n客户端步骤n创建套接字n与远程服务器建立连接n读/写数据;终

42、止连接TCP服务器模板int main(void)int sockfd,connect_sock;if(sockfd=socket(AF_INET,SOCK_STREAM,0)=-1) perror(“create socket failed.”);exit(-1);/* bind sockfd to some address */* listen */loop if(connect_sock=accept(sockfd,NULL,NULL)=-1) perror(“Accept error.”); exit(-1); /* read and process request */close(c

43、onnect_sock); close(sockfd);TCP客户模板/* include some header files */int main(void)int sockfd;if(sockfd=socket(AF_INET,SOCK_STREAM,0)=-1)perror(“Create socket failed.”);exit(-1);/* connect to server */* send requst and receive response */close(sockfd);TCP套接字例程n采用客户/服务器模式,完成下列功能:n客户根据用户提供的IP地址,连接相应的服务器;

44、n服务器等待客户的连接,一旦连接成功,则显示客户的IP地址,并发欢迎信息给客户;n客户接收服务器发送的信息并显示;TCP服务器程序/*server.c*/#include #include #include #include #include #define PORT 1234#define BACKLOG 1int main(void) int listenfd, connectfd; struct sockaddr_in server; struct sockaddr_in client; int sin_size; if(listenfd=socket(AF_INET, SOCK_STR

45、EAM, 0)=-1) perror(Create socket failed); exit(-1); TCP服务器程序(cont.) int opt =1; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt); bzero(&server, sizeof(server); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(li

46、stenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)=-1) perror(Bind error); exit(-1); if (listen(listenfd, BACKLOG) = -1) perror(listen error); exit(-1); TCP服务器程序(cont.) sin_size = sizeof(struct sockaddr_in); while(1) if (connectfd = accept(listenfd, (struct sockaddr *)&client, &

47、sin_size) = -1) perror(accept error); exit(-1); printf(you get a connection from %sn, inet_ntoa(client.sin_addr); send(connectfd,welcome to my servern,22, 0); close(connectfd); /* while */ close(listenfd);TCP客户程序/*client.c*/#include #include #include #include #define PORT 1234#define MAXDATASIZE 100

48、int main(int argc, char *argv) int fd, numbytes; char bufMAXDATASIZE; struct hostent * he; struct sockaddr_in server; if (argc != 2) printf(Usage: %s n, argv0); exit(-1); TCP客户程序(cont.)if (he = gethostbyname(argv1) = NULL) perror(gethostbyname error.); exit(1); if (fd = socket(AF_INET, SOCK_STREAM,

49、0) = -1) perror(Create socket failed.); exit(1); bzero(&server, sizeof(server); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr = *(struct in_addr *) he-h_addr);TCP客户程序(cont.)if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr) = -1) perror(connect

50、failed.); exit(1); if( (numbytes = recv(fd, buf, MAXDATASIZE, 0) = -1) perror(recv error.); exit(1); bufnumbytes =0; printf(Server Message: %sn,buf); close(fd);上机2n客户端:n从命令行读入服务器的IP地址;并连接到服务器;n循环从命令行读入一行字符串,并传递给服务器,由服务器对字符串反转,并将结果返回客户程序,如果用户输入的是quit,则关闭连接;n客户程序显示反转后的字符串;n服务器端:n循环接收客户的连接请求,并显示客户的IP地址

51、和端口号;n接收并显示客户传来的字符串,反转后传递给客户; 三种异常情况n下面我们考虑在以下三种异常情况发生后,tcp客户服务器程序的反映;n服务器主机崩溃n服务器主机崩溃后重启n服务器主机关机。服务器主机崩溃n服务器主机崩溃时,已有的网络连接上发不出任何东西。n同时假设应用程序发出数据后,然后阻塞于从套接字读取响应。n由于服务器主机崩溃,因此客户tcp会持续重传数据分节,试图从服务器接收一个ACK:源自Berkeley的实现将重传12次。当客户tcp最终放弃时,返回给客户一个错误,此时错误是ETIMEDOUT,或者是因为中间路由器判定服务器主机不可达,且以一个目的地不可达的ICMP消息响应,

52、则错误是EHOSTUNREACH或ENETUNREACH。n通过设置套接字选项可以更改tcp持续重传等待的超时时间。服务器主机崩溃后重启n在这种情况下,如果客户在主机崩溃重启前不主动发送数据,那么客户是不会知道服务器已崩溃的。在服务器重启后,客户向服务器发送一个数据分节;n由于服务器重启后丢失了以前的连接信息(尽管在服务端口上有进程监听,但连接套接字所在的端口无进程等待),因此导致服务器主机的tcp响应RST;n当客户tcp收到RST,向客户返回错误,ECONNRESETn如果客户对服务器的崩溃情况很关心,即使客户不主动发送数据也这样,就需要其他技术支持(如套接口选项SO_KEEPALIVE或

53、某些客户服务器心跳函数)。服务器主机关机n当Linux主机关机时,由init进程给所有运行的进程发信号SIGTERM(我们的服务器程序可以捕获该信号,并在信号处理程序中正常关闭网络连接)。n如果服务器程序忽略了SIGTERM信号,则init进程会等待一段固定的时间(通常是5s20s),然后给所有还在运行的程序发信号SIGKILL(该信号不能由服务器程序捕获);服务器将由信号SIGKILL终止,其终止时,所有打开的描述字被关闭,这导致向客户发送FIN分节;n客户收到FIN分节后,能推断出服务器将终止服务。第四章 UDP套接字UDP套接字编程n实现UDP套接字基本步骤分为服务器端和客户端两部分:n

54、服务器端n建立UDP套接字;n绑定套接字到特定地址;n等待并接收客户端信息;n处理客户端请求;n发送信息回客户端;关闭套接字;UDP套接字编程(Cont.)n客户端步骤n建立UDP套接字;n发送信息给服务器;n接收来自服务器的信息;关闭套接字UDP套接字编程(Cont.)socket()bind()sendto()close()socket()sendto()recvfrom()close()阻塞直到接收到客户信息UDP服务器端UDP客户端recvfrom()处理客户信息UDP数据传输函数sendto#include #include ssize_t sendto(int sockfd, co

55、nst void *msg, size_t len, int flags, const struct sockaddr *to, int tolen); 返回:大于0成功发送数据长度;-1出错;nUDP套接字使用无连接协议,因此必须使用sendto函数,指明目的地址;nflags是传输控制标志,其值定义如下:n0:常规操作,如同write()函数;nMSG_OOB:发送带外数据;nMSG_DONTROUTE:忽略底层路由协议,直接发送。UDP数据传输函数recvfrom#include #include ssize_t recvfrom(int sockfd, void *buf, size_

56、t len, int flags, struct sockaddr *from, int *fromlen); 返回:大于0成功接收数据长度;-1出错;nUDP套接字使用无连接协议,因此必须使用recvfrom函数,指明源地址;nflags是传输控制标志,其值定义如下:n0:常规操作,如同read()函数;nMSG_PEEK:只察看数据而不读出数据;nMSG_OOB:忽略常规数据,而只读取带外数据;nfrom 和 fromlen 是“值结果”参数。内核长度套接口地址结构用户进程结果值 当函数被调用时,结构大小是一值,当函数返回时,结构大小又是一个结果,这种参数类型叫值-结果参数。参数是一个整型

57、指针。bind(int sockfd, const struct sockaddr *addr, socklen_len len)recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlen)值值-结果参数说明结果参数说明UDP服务器模板#include #include #inlcude int main(void)int socketfd;if (sockfd = socket(AF_INET, SOCK_DGRAM, 0) = -1) perror(“Create so

58、cket failed.”);exit(1);/* Bind socket to address */ loop /* receive and process data from client */ /* send resuts to client */close(sockfd); UDP客户模板#include #include #inlcude int main(void)int sockfd;if (sockfd = socket(AF_INET, SOCK_DGRAM, 0) = -1) perror(“Create socket failed.”);exit(1);/* send d

59、ata to the server */* receive data from the server */close(sockfd);UDP套接字例程n本例程分为服务器和客户两部分,主要完成如下功能:n客户端:n从命令行读入服务器的IP地址和要传送的字符串,并将字符串传递给服务器,由服务器对字符串进行处理,并将结果返回客户程序;n客户程序显示处理后的字符串;n服务器端:n接收客户的字符串,显示客户IP地址和端口号;n对接收的客户字符串显示并进行处理,并发送回客户; UDP服务器#include #include #include #include #include #define PORT 1234#define MAXDATASIZE 100int mai

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论