揭开unix网络之纱1 客户服务器编程范式Word格式文档下载.docx
《揭开unix网络之纱1 客户服务器编程范式Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《揭开unix网络之纱1 客户服务器编程范式Word格式文档下载.docx(29页珍藏版)》请在冰点文库上搜索。
structtimevalru_stime;
/*systemtimeused*/
longru_maxrss;
/*maximumresidentsetsize*/
longru_ixrss;
/*integralsharedmemorysize*/
longru_idrss;
/*integralunshareddatasize*/
longru_isrss;
/*integralunsharedstacksize*/
longru_minflt;
/*pagereclaims*/
longru_majflt;
/*pagefaults*/
longru_nswap;
/*swaps*/
longru_inblock;
/*blockinputoperations*/
longru_oublock;
/*blockoutputoperations*/
longru_msgsnd;
/*messagessent*/
longru_msgrcv;
/*messagesreceived*/
longru_nsignals;
/*signalsreceived*/
longru_nvcsw;
/*voluntarycontextswitches*/
longru_nivcsw;
/*involuntarycontextswitches*/
};
RETURNVALUE
Onsuccess,zeroisreturned.Onerror,-1isreturned,anderrnoissetappropriately.
ERRORS
EFAULTusagepointsoutsidetheaccessibleaddressspace.
EINVALwhoisinvalid.
3.2Getaddrinfo函数和freeaddrinfo
getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个sockaddr结构而不是一个地址列表。
这些sockaddr结构随后可由套接字函数直接使用。
如此一来,getaddrinfo函数把协议相关性完全隐藏在这个库函数内部。
应用程序只需要处理由getaddrinfo填写套接字地址结构。
netdb.h>
intgetaddrinfo(constchar*hostname,constchar*service,conststructaddrinfo*hints,structaddrinfo**result);
本函数通过result指针参数返回一个指向addrinfo结构链表的指针,而addrinof结构定义在头文件<
中。
structaddrinfo{
intai_flags;
intai_family;
intai_socktype;
intai_protocol;
socklen_tai_addrlen;
char*ai_canonname;
structsockaddr*ai_addr;
structaddrinfo*ai_next;
};
其中hostname参数是一个主机名或地址串。
service参数是一个服务名或十进制端口号数串。
hints参数可以是一个空指针,也可以是一个指向某个addrinfo结构的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。
hints结构中调用者可以设置的成员有:
●ai_flags(零个或多个或在一起的AI_xxx值);
●ai_family(某个AF_xxx值)
●ai_socktype(某个SOCK_xxx值)
●ai_protocol
其中ai_flags成员可用的标志值及其含义如下:
AI_PASSIVE
套接字将用于被动打开
AI_CANONNAME
告知getaddrinfo函数返回主机的规范名字
AI_NUMERICHOST
防止任何类型的名字到地址映射,hostname参数必须是一个地址串
防止任何类型的名字到服务映射,service参数必须是一个十进制端口号数串
AI_VAMAPPED
如果同时指定ai_family成员的值为AF_INET6,那么如果没有可用的AAAA记录,就返回与A记录对应的IPv4映射的IPv6地址。
AI_ALL
如果同时指定AI_V4MAPPED标志,那么除了返回与AAAA记录对应的IPV6地址外,还返回与A记录对应的IPV4映射的IPV6地址
AI_ADDRCONFIG
按照所在主机的配置选择返回地址类型,也就是只查找与所在主机回馈接口以外的网络接口配置的IP地址版本一致的地址。
如果hints参数是一个空指针,本函数就假设ai_flag、ai_socktype和ai_protocol的值均为0,ai_family的值为AF_UNSPEC。
如果本函数返回成功(0),那么由result参数指向的变量已被填入一个指针,它指向的是有其中的ai_next成员串接起来的adrinfo结构链表。
可导致返回多个addrinfo结构的情形有一下两个。
1)如果与hostname参数关联的地址有多个,那么适用于所请求地址族(可通过hints结构的ai_family成员设置)的每个地址都返回一个对应的结构。
2)如果service参数指定的服务支持多个套接字类型,那么每个套接字类型都可能返回一个对应的结构,具体取决于hints结构的ai_socktype成员。
在addrinfo结构中返回的信息可现成用于socket调用,随后现成用于适合客户的connect或sendto调用,或者是适合服务器的bind调用。
Socket函数的参数就是addrinfo结构中的ai_family、ai_socktype和ai_addr成员。
Connect或bind函数的第二个和第三个参数就是该结构中的ai_addr和ai_addrlen成员。
如果在hints结构中设置了AI_CANONNAME标志,那么本函数返回的第一个addrinfo结构的ai_canoname成员指向所查找主机的规范名字。
按照DNS的说法,规范名字通常是FQDN。
诸如telnet之类程序往往使用这个标志以显示所连接到主机的规范名字,这样即使用户给定的是一个简单的名字或别名,他们也能搞清真正查找的名字。
使用freeaddrinfo释放result
4tcp客户端
为了省事,也是为了学习python,更是检查跨语言的数据传递,使用python
#!
/usr/bin/envpython
fromsocketimport*
HOST='
127.0.0.1'
PORT=55555
BUFSIZE=1024
ADDR=(HOST,PORT)
tcpCliSock=socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)
whileTrue:
data=raw_input('
>
'
)
ifnotdata:
break
tcpCliSock.send(data)
data=tcpCliSock.recv(BUFSIZE)
printdata
tcpCliSock.close()
5公用函数
史蒂文的书比较困难的就是大量的交叉引用,要看一个章节就要上溯更多的资料,呜呼!
难啊!
所以先写和复习一点由史蒂文自己写的公共的函数。
5.1I/O函数
字节流套接字(例如Tcp套接字)上的read和write函数所表现的行为不同于通常的文件I/O。
字节流套接字上调用read或writen输入或输出的字节数可能比请求的数量少,然而这不是出错的状态。
这个现象的原因在于内核中用于套接字的缓冲区可能已到达了极限。
此时所需要的是调用者再次调用read或writen函数,以输入或输出剩余的字节。
有些版本的unix在往一个管道中写入4096字节的数据时也会表现出这样的行为。
这个现象在read一个字节流套接字时很常见,但是在Write一个字节流套接字时子女在套接字为非阻塞的前提下才出现。
尽管如此,为预防万一,不让实现返回一个不足的字节计数值,我们总是改为调用writen函数来取代write函数。
5.1.1readn函数
ssize_treadn(intfd,void*vptr,size_tn)
{
size_tnleft;
ssize_tnread;
char*ptr;
ptr=vptr;
nleft=n;
while(nleft>
0){
if((nread=read(fd,ptr,nleft))<
0)
{
if(errno==EINTR)
nread=0;
else
return-1;
}
elseif(nread==0)
break;
nleft-=nread;
ptr+=nread;
}
returnn-nleft;
}
5.1.2writen函数
ssize_twriten(intfd,constvoid*vptr,size_tn)
ssize_tnwritten;
constchar*ptr;
nleft=n;
if((nwritten=write(fd,ptr,nleft))<
=0){
if(nwritten<
0&
&
errno==EINTR)
{
nwritten=0;
}
nleft-=nwritten;
ptr+=nwritten;
returnn;
5.1.3readline函数
ssize_treadline(intfd,void*vptr,size_tmaxlen)
ssize_tn,rc;
charc,*ptr;
for(n=1;
n<
maxlen;
n++){
again:
if((rc=read(fd,&
c,1))==1){
*ptr++=c;
if(c=='
\n'
break;
}elseif(rc==0){
*ptr=0;
return(n-1);
}else{
gotoagain;
return-1;
*ptr=0;
5.2错误退出函数
stdarg.h>
syslog.h>
intdaemon_proc;
staticvoiderr_doit(int,int,constchar*,va_list);
voiderr_ret(constchar*fmt,...)
va_listap;
va_start(ap,fmt);
err_doit(1,LOG_INFO,fmt,ap);
va_end(ap);
return;
voiderr_sys(constchar*fmt,...)
err_doit(1,LOG_ERR,fmt,ap);
exit
(1);
voiderr_dump(constchar*fmt,...)
abort();
voiderr_msg(constchar*fmt,...)
err_doit(0,LOG_INFO,fmt,ap);
return;
voiderr_quit(constchar*fmt,...)
err_doit(0,LOG_ERR,fmt,app);
staticvoiderr_doit(interrnoflag,intlevel,constchar*fmt,va_listap)
interrno_save,n;
charbuf[MAXLINE+1];
errno_save=errno;
#ifdefHAVE_VSNPRINTF
vsnprintf(buf,MAXLINE,fmt,ap);
#else
vsnprintf(buf,fmt,ap);
#endif
n=strlen(buf);
if(errnoflag)
snprintf(buf+n,MAXLINE-n,"
:
%s"
sterror(errno_save));
strcat(buf,"
\n"
);
if(daemon_proc){
syslog(level,buf);
else{
fflush(stdout);
fputs(buf,stderr);
fflush(stderr);
6并发服务器
6.1Main函数
intmain(intargc,char**argv)
intlistenfd,connfd;
pid_tchildpid;
voidsig_chld(int),sig_int(int),web_child(int);
socklen_tclilen,addrlen;
structsockaddr*cliaddr;
if(argc==2)
listenfd=Tcp_listen(NULL,argv[1],&
addrlen);
elseif(argc==3)
listenfd=Tcp_listen(argv[1],argv[2],&
else
err_quit("
usage:
serv01[<
host>
]<
port#>
"
cliaddr=malloc(addrlen);
signal(SIGCHLD,sig_child);
signal(SIGINT,sig_int);
for(;
;
){
clilen=addrlen;
if((connfd=accept(listenfd,cliaddr,&
clilen))<
0){
if((errno==EINTR)
continue;
err_sys("
accepterror"
if((childpid=fork())==0){
close(listenfd);
web_child(connfd);
exit(0);
close(connfd);
在该代码中使用了到的函数:
消息响应函数sig_chld和sig_int,输入信息处理函数web_child,tcp监听函数Tcp_listen等四个函数。
6.2Sig_chld函数
源代码为每个客户连接fork一个子进程并处理来自垂死的子进程的SIGCHLD信号。
sig_chld代码如下:
voidsig_chld(intsigno)
pid_tpid;
intstat;
while((pid=waitpid(-1,&
stat,WNOHANG))>
;
在一个循环内调用waitpid,以获取所有已终止子进程的状态。
必须指定WNOHANG选项,它告知waitpid在有还没有终止的子进程在运行时不要阻塞。
不能在循环中调用wait,因为thereisnowaytopreventwaitfromblockingiftherearerunningchildrenthathavenotyetterminated.这里就用英语了,翻译的实在是太烂了。
6.3Sig_int函数
捕获由键入终端中终端键产生的sigint信号。
在客户运行完毕之后我们键入该键以显示服务器程序运行所需的CPU时间。
这是一个信号处理函数不返回而直接终止进程的例子。
voidsig_int(intsigno)
voidpr_cpu_time(void);
pr_cpu_time();
exit(0);
这里又用到了pr_cpu_time函数。
6.4Pr_cpu_time函数
#ifndefHAVE_GETRUSAGE_PROTO
intgetrusage(int,structrusage*);
#endif
voidpr_cpu_time(void)
doubleuser,sys;
structrusagemyusage,childusage;
if(getrusage(RUSAGE_SELF,&
myusage)<
err_sys("
getrusageerror"
if(getrusage(RUSAGE_CHILDREN,&
childusage)<
user=(double)myusage.ru_utime.tv_sec+myusage.ru_utime.tv_usec/1000000.0;
user+=(double)childusage.ru_utime.tv_sec+childusage.ru_utime.tv_usec/1000000.0;
sys+=(double)childusage.ru_stime.tv_sec+childusage.ru_stime.tv_usec/100000.0;
printf("
\nusertime=%g,systime=%g\n"
user,sys);
getrusage函数被调用了两次,分别返回调用进程(RUSAGE_SELF)和它的所有已终止子进程(RUSAGE_CHILDREN)的资源利用统计。
所显示的值包括总的用户时间(耗费在执行用户进程上的CPU时间)和总的系统时间(CPUtimespendwithinthekernel,executingonbehalfofthecallingprocess).
6.5Web_child函数
客户在建立与服务器的连接之后通过该连接写出一行文本,指出需要有服务器返送多少字节的数据给客户。
这一点与http有些类似:
客户发送一个小请求,服务器响应以所期望的信息。
在http应用系统中,服务器通常在发送回所请求的数据之后就关闭连接,不过较新的版本允许使用持续连接(persistentconnection),为在某个时限以内到达的额外客户请求继续保持tcp连接开发一段时间。
在web_child函数中,服务器允许来自客户的额外请求。
#defineMAXN16384
voidweb_child(intsockfd)
intntowrite;
charline[MAXLINE],result[