免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 908 | 回复: 0
打印 上一主题 下一主题

网络编程中需要考虑的一些情况 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-06-11 17:35 |只看该作者 |倒序浏览

网络编程中需要考虑的一些情况

【摘要】
本文主要说明了一些网络编程需要考虑的一些情况。
【关键词】
网络编程
一、问题的提出
在网络编程中,经常有些情况没有考虑,这就对系统带来了隐患,所以需要列出一些需要考虑的一些情况。
二、解决思路
/**************服务器*******************************/
int main()
{
   int  listenfd, connfd;
   pid_t childpid;
   socklen_t  chilen;
   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 = htol(INADDR_ANY);
   Bind(listenfd, (SA *)&servaddr, sizeof(servaddr));
   Listen(listenfd, LISTENQ);
   
   for(;;)
   {
      clilen = sizeof(cliaddr);
      connfd = Accept(listenfd, (SA *)&cliaddr, &clilen);
      if((clild=Fork()) == 0)
          {
             Close(listenfd);
             str_echo(connfd);
             exit(0);
          }
      Close(connfd);   
   }
}

void str_echo(int sockfd)
{
   ssize_t  n;
   char  buf[MAXLINE];
   again:
      while((n = read(sockfd, buf, MAXLINE))>0)
      {
          Writen(sockfd, buf, n)
      }
      if(n
          {
             goto again;
          }
      else if
          {
             err_sys("str_echo:read error");
          }   
}
/*********************客户端***********************/
int main(int argc, char **argv)
{
   int  sockfd;
   strcut sockaddr_in servaddr;
   if(argc != 2)
      {
          err_quit("usage:tcpcli");
      }
   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.sin_addr);
   Connect(sockfd, (SA *)&servaddr, sizeof(servaddr));
   str_cli(stdin, sockfd);
   exit(0);   
}

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 terminated prematurely");
          }
      Fputs(recvline, stdout);
   }
}
/**********************************************/
n         服务器没有处理僵死的子进程
需要在listen之后添加一个俘获SIGCHLD的信号处理函数,还处理僵死的子进程。
Signal(SIGCHLD, sig_chld);

Void sig_chld(int signo)
{
   Pid_t  pid;
   Int    stat;
   
   Pid  =  wait(&stat);
   Printf(”child %d terminated\n”, pid);
   Return;
}
SIGCHLID在服务器父进程阻塞于慢系统调用accept时有父进程捕获,内核于是致使accept返回ENINTR错误,而父进程不处理该错误,所以会被中止,需要修改上面的服务器代码。
for(;;)
{
   clilen = sizeof(cliaddr);
   if((connfd=accept(listenfd, (SA *)&cliaddr, &clilen))
       {
          if(errno == EINTR)
              {
                 continue;
              }
          else
              {
                 err_sys("accept error");
              }   
       }
}
n         建立一个信号处理函数并在其中调用wait并不足以防止出现僵死进程
如果有5个信号都在信号处理函数之前产生,而信号处理函数只执行一次,因为UNIX信号一般是不排队的。正确的解决方法是调用waitpid而不是wait,在一个循环调用waitpid以获取已终止的子进程的状态,指定WNOHANG选项,告知waitpid在有尚未终止的子进程在运行时不要阻塞。
void sig_chld(int signo)
{
   pid_t pid;
   int stat;
   
   while((pid = waitpid(-1, &stat, WNOHANG))>0)
   {
      printf("child %d terminated\n", pid);
   }
   return;
}
1)当fork子进程时,必须捕获SIGCHLD信号。
2)当捕获信号时,必须处理被中断的系统调用。
3)SIGCHLD的信号处理函数必须正确编写,应使用waitpid函数以免留下僵死进程。
n         accept返回前连接夭折
                  图 ESTABLISHED状态的连接在调用accept之前收到RST
Linux下accept返回前连接被夭折,accept不会返回错误,但是在使用accept返回的连接套接字时,调用它的函数会出错,需要对函数的返回值进行判断。
n         服务器进程终止
手动杀死服务器子进程,子进程中所有打开的描述符都被关闭。这就导致向客户发送一个FIN,而客户TCP则响应以一个ACK。但是问题在于:当FIN到达套接口时,客户正阻塞在fgets调用上。客户实际上在应对两个描述符――套接口和用户输入,它不能单纯阻塞在这两个源中某个特定源的输入上,而是应该阻塞在其中任何一个源的输入上,需要考虑使用select()函数,这样一旦杀死服务器子进程,客户就会立即被告知收到FIN。
void str_cli(FILE *fp, int sockfd)
{
   int maxfdp1;
   fd_set rset;
   char  sendline[MAXLINE], recvline[MAXLINE];
   
   FD_ZERO(&rset);
   for(;;)
   {
      FD_SET(fileno(fp), &rset);
      FD_SET(sockfd, &rset);
      maxfdp1 = max(fileno(fp), sockfd) +1;
      Select(maxfdp1, &rset, NULL, NULL, NULL);
      if(FD_ISSET(sockfd, &rset))
          {
             if(Readline(sockfd, recvline, MAXLINE) == 0)
                 {
                    err_quit("str_cli:server terminated prematurely");
                 }
             Fputs(recvline, stdout);
          }
      if(FD_ISSET(fileno(fp), &rset))
          {
             if(Fgets(sendline, MAXLINE, fp) == NULL)
             {
                 return;
             }
             Writen(sockfd, sendline, strlen(sednline));
          }   
   }
}
n         SIGPIPE信号的处理
如果客户不理会readline函数的返回的错误,反而写入更多的数据到服务器,例如,客户可能读取任何数据之前执行两次针对服务器的写操作,而RST是由其中的第一次写操作引发的。
void str_cli(FILE *fp, int sockfd)
{
   char sendline[MAXLINE], recvline[MAXLINE];
   
   while(Fgets(sendline, MAXLINE, fp) != NULL)
   {
      Writen(sockfd, sendline, 1);
       Sleep(1);
Writen(sockfd, sendline, strlen(sendline)-1);
      if(Readline(sockfd, recvline, MAXLINE) == 0)
          {
             err_quit("str_cli: server terminated prematurely");
          }
      Fputs(recvline, stdout);
   }
}
第一次写引发RST,第二次写产生SIGPIPE。
当一个进程向某个已经收到RST的套接口执行写操作时,内核向该进程发送一个SIGPIPE信号。该信号的缺省行为是终止进程,因此进程必须捕获它以免不情愿的被终止。
处理SIGPIPE的方法:如果没有特殊的事情要做,那么将信号处理办法设置为SIG_IGN,并假设后续的输出操作将捕获EPIPE错误并终止。如果信号出现是需要采取特殊措施,那么就必须捕获信号,以便在信号处理函数中执行所期望的动作。
n         服务器主动崩溃
当服务器主机崩溃时,已有的网络连接上发不出任何东西。客户键入一行文本,客户随后阻塞于readline调用,等待回射的响应。但是服务器是不会响应的,所以需要readline设置一个超时,来防止readline无限期的等待。上面的是客户主动向服务器发送数据时,才检测出它已经崩溃了,如果不主动向它发送数据也能检测出服务器主机崩溃,需要使用SO_KEEPALIVE套接口选项,该套接口保持连接检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。
n         服务器主机崩溃后重启
启动服务器和用户,并在客户键入一行文本以确认连接已经建立;服务器主机崩溃并重启;在客户端键入一行文本,它将作为一个tcp数据分节发送到服务器主机;当服务器主机崩溃后重启是,它的tcp丢失了崩溃前的所有连接信息,因此服务器tcp对于所受到的来自客户的数据分节响应一个RST;当客户tcp收到RST时,客户正阻塞于readline调用,导致该调用返回ECONNREST错误。
n         服务器主用关机
系统关机是,init进程通常先给所有进程发送SIGTERM信号,再等待一段固定时间,然后给所有在运行的进程发送SIGKILL信号。如果服务器不捕获SIGTERM信号并终止,服务器将由SIGKILL信号终止。当服务器子进程终止时,它的所有打开的描述字都被关闭,随后就和”服务器进程终止”一样了。所以必须在客户中使用select函数,使得服务器进程的终止一经发生,客户就马上检查到。
三、总结
主要介绍了网络编程中需要考虑的一些情况。
四、效果评价
无。
五、推广建议
无。
参考资料

—— 完 ——



本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/35065/showart_1962231.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP