Socket & Select

以下是我写的socket服务器端的代码,使用select接受连接,

 

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define D_LINE(_STR_) printf("f:%s(l:%d)-fn:%s():%s/n",__FILE__,__LINE__,__FUNCTION__,_STR_);

#define SERVER_PORT 1025
#define LENGTH_OF_LISTEN_QUEUE 100
#define BUFFER_SIZE 1024
#define MAX_RECV 10

#define SELECT_WAIT_SEC 0;  // in second
#define SELECT_WAIT_USEC 3000; // in usecond

int main()
{
 int i;
 struct sockaddr_in server_addr;

 /*================ for felect ===================*/
 int ret = -1;
 fd_set fdsr;
 struct timeval tv;

 

 
 bzero(&server_addr, sizeof(server_addr));
 server_addr.sin_family = AF_INET;
 server_addr.sin_addr.s_addr = htons(INADDR_ANY);
 server_addr.sin_port = htons(SERVER_PORT);

 int sockfd = socket(AF_INET, SOCK_STREAM,0);
 if(sockfd < 0)
  {
   printf("Create socket failed/n");
   exit(-1);
  }
 printf("server sockfd is %d/n", sockfd);

 if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)))
  {
   printf("server bind port: %d failed/n", SERVER_PORT);
   exit(-1);
  }

 if(listen(sockfd, LENGTH_OF_LISTEN_QUEUE))
  {
   printf("server listen failed/n");
  }

 printf("Start to listen/n");
 while(1)
 {
  struct sockaddr_in client_addr;
  socklen_t length = sizeof(client_addr);
  
  printf("before Server accept/n");

  int new_server_socket = accept(sockfd, (struct sockaddr*)&client_addr,&length);
  if(new_server_socket < 0)
  {
   printf("Server accept failed/n");
   break;
  }

  printf("Server accept success/n");

  char buffer[BUFFER_SIZE];
  bzero(buffer, BUFFER_SIZE);

  strcpy(buffer, "Hello from Platform");
  strcat(buffer, "/n");

   if((ret = send(new_server_socket, buffer, BUFFER_SIZE, 0)) < 0)
   {
    printf("send error at %d time/n", i);
    return -1;
   }
   printf("send success/n");
   usleep(3000);
 //   sleep(1);
//  send(new_server_socket, buffer, BUFFER_SIZE, 0);
  bzero(buffer, BUFFER_SIZE);
/*  for(i = 0; i < MAX_RECV; i++)
  {
   length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);

   if(length < 0)
   {
    printf("server recieve data failed/n");
    exit(-1);
   }
   printf("%s/n", buffer);
  }
*/  while(1)
  {
   FD_ZERO(&fdsr);
   FD_SET(new_server_socket, &fdsr);

   tv.tv_sec = SELECT_WAIT_SEC;
   tv.tv_usec = SELECT_WAIT_USEC;
   ret = select(new_server_socket + 1, &fdsr, NULL, NULL, &tv);

   D_LINE("select/n")
  
   if(ret < 0)
   {
    D_LINE("select error")
    return -1;
   }
   else if(ret == 0)
   {
    D_LINE("select timeout")
    break;
   }

   if(FD_ISSET(new_server_socket, &fdsr))
   {//Note! Remainber to judge here! or select think there is always have data to read
    ret= recv(new_server_socket, buffer, BUFFER_SIZE, 0);
    if(ret == -1)
    {
     break;
    }
    printf("%s/n", buffer);
   }
  }

  close(new_server_socket);
 }

 close(sockfd);
 return 0;
}

 以下内容摘自网上~

讨论关于利用select()检测对方Socket关闭的问题: 
 
仍然是本地Socket有东东可读,因为对方Socket关闭时,会发一个关闭连接 
通知报文,会马上被select()检测到的。关于TCP的连接(三次握手)和关 
闭(二次握手)机制,敬请参考有关TCP/IP的书籍。 
 
不知是什么原因,UNIX好象没有提供通知进程关于Socket或Pipe对方关闭的 
信号,也可能是cpu所知有限。总之,当对方关闭,一执行recv()或read(), 
马上回返回-1,此时全局变量errno的值是115,相应的sys_errlist[errno] 
为"Connect refused"(请参考/usr/include/sys/errno.h)。所以,在上 
篇的for(;;)...select()程序块中,当有东西可读时,一定要检查recv()或 
read()的返回值,返回-1时要作出关断本地Socket的处理,否则select()会 
一直认为有东西读,其结果曾几令cpu伤心欲断针脚。不信你可以试试:不检 
查recv()返回结果,且将收到的东东(实际没收到)写至标准输出... 
在有名管道的编程中也有类似问题出现。具体处理详见拙作:发布一个有用 
的Socket客户方原码。 
 
至于主动写Socket时对方突然关闭的处理则可以简单地捕捉信号SIGPIPE并作 
出相应关断本地Socket等等的处理。SIGPIPE的解释是:写入无读者方的管道。 
在此不作赘述,请详man signal。 
 
以上是cpu在作tcp/ip数据传输实验积累的经验,若有错漏,请狂炮击之。 
 
唉,昨天在hacker区被一帮孙子轰得差点儿没短路。ren cpu(奔腾的心) z80 
 
补充关于select在异步(非阻塞)connect中的应用,刚开始搞socket编程的时候 
我一直都用阻塞式的connect,非阻塞connect的问题是由于当时搞proxy scan 
而提出的呵呵 
通过在网上与网友们的交流及查找相关FAQ,总算知道了怎么解决这一问题.同样 
用select可以很好地解决这一问题.大致过程是这样的: 
 
1.将打开的socket设为非阻塞的,可以用fcntl(socket, F_SETFL, O_NDELAY)完 
成(有的系统用FNEDLAY也可). 
 
2.发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧 
在进行还没有完成. 
 
3.将打开的socket设进被监视的可写(注意不是可读)文件集合用select进行监视, 
如果可写,用 
        getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int)); 
来得到error的值,如果为零,则connect成功. 
 
在许多unix版本的proxyscan程序你都可以看到类似的过程,另外在solaris精华 
区->编程技巧中有一个通用的带超时参数的connect模块. 

 

你可能感兴趣的:(C编程)