Lomo's BLOG

行之而不著焉 习矣而不察焉 终身由之而不知其道也 众也 ~
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

《Unix网络编程 - TCP客户服务器例子》

Posted on 2006-04-23 12:16  Lomo  阅读(653)  评论(0)    收藏  举报

Sample:

TcpServer:

#include "unp.h"

int main(int argc,char * *argv)
{
  
int listenfd,connfd;
  pid_t childpid;
  socklen_t clilen;
  
struct sockaddr_in cliaddr,servaddr;

  listenfd 
= Socket(AF_INET,SOCK_STREAM,0);

  bzero(
&servaddr,sizeof(servaddr));
  servaddr.sin_family 
= AF_INET;
  servaddr.sin_addr.s_addr 
= htonl(INADDR_ANY);
  servaddr.sin_port 
= htons(SERV_PORT);
  Bind(listenfd,(SA 
*)&servaddr,sizeof(servaddr));

  Listen(listenfd,LISTENQ);

  
for(;;){
    clilen 
= sizeof(cliaddr);
    connfd 
= Accept(listenfd,(SA *)&cliaddr,&clilen);
    
if((childpid = fork()) == 0)
    
{
       Close(listenfd);
       str_echo(connfd);
       exit(
0);
    }

    Close(connfd); 
/*parent close the connected socket*/
  }

}

TcpServerAction:

void str_echo(int sockfd)
{
  ssize_t n;
  
char line[MAXLINE];
  
for(;;)
  
{
    
if((n = Readline(sockfd,line,MAXLINE)) == 0)
      
return;/*connection close by other end*/
    writen(sockfd,line,n);
  }

}

TcpClient:

#include "unp.h"

int main(int argc,char * *argv)
{
  
int sockfd;
  
struct sockaddr_in servaddr;

  
if(argc != 2)
     err_quit(
"usage:tcpcli <IPaddress>");
  
  sockfd 
= Socket(AF_INET,SOCK_STREAM,0);

  bzero(
&servaddr,sizeof(servaddr));
  servaddr.sin_family 
= AF_INET;
  servaddr.sin_port 
= htons(SERV_PORT);
  Inet_pton(AF_INET,argv[
1],&servaddr,sizeof(servaddr));
  Connect(sockfd,(SA 
*)&servaddr,sizeof(servaddr));
  str_cli(stdin,sockfd); 
/*do it all*/
  exit(
0);
}

ClientAction:

void str_cli(FILE *fp, int sockfd)
{
  
char sendline[MAXLINE],recvline[MAXLINE];

  
while(Fgets(sendline,MAXLINE,fp) != NULL)
    
{
    Writen(sockfd,sendline,strlen(sendline));

    
if(Readline(sockfd,recvline,MAXLINE) == 0)
      err_quit(
"str_cli:server termilated prematurely");

    Fputs(recvline,stdout);
    }

}

正常启动:服务器启动linsten,客户连接,三次握手完成,服务器接收连接,fork子进程保持和客户的连接,服务器本身关闭已连接套接字,继续listen等待下一个客户发出请求。

正常终止:在客户端,键入任何键都将得到回射,除非键入EOF(Ctrl+D)以终止客户,在这个过程中,首先,EOF引发str_cli中fgets返回空指针,str_cli返回后引发客户mian返回,进程终止连带是关闭所有描述字,客户套接口因此而由内核关闭,导致客户发终止序列FIN,服务器响应ACK,tcp终止序列前半部分完成,服务器处于CLOSE_WAIT状态,同时,当服务器接收FIN时,其子进程阻塞于readline,返回0后服务器子进程因此而终止,连带关闭其已连接套接口,引发子进程发送终止序列后半部分,至此,连接完全终止,客户套接口进入TIME_WAIT状态。

事实上子进程的终止会触发一个SIGCHLD信号送到父进程,但我们的实例代码并没有捕获它,而是利用默认动作忽略该信号。通过ps命令可以看到子进程成为了僵尸进程。