Chinaunix首页 | 论坛 | 博客
  • 博客访问: 157522
  • 博文数量: 85
  • 博客积分: 366
  • 博客等级: 一等列兵
  • 技术积分: 455
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-16 18:32
个人简介

闲下来的时候看看书

文章分类

全部博文(85)

文章存档

2016年(1)

2013年(2)

2012年(81)

2011年(1)

我的朋友

分类:

2012-09-20 17:03:06

Andrew Huang 转载请注明作者及联络方式

一.select编程流程
--------------------------------------------------------------

二.实现的代码
--------------------------------------------------------------

聊天服务器代码 tcp_echo_svr.c


/*
  TCP Select Echo Server
 
  Author :Andrew Huang

 */


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <pthread.h>


#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <unistd.h>



#include <stdlib.h>
#include <stdio.h>
#include <string.h>



static fd_set user_list; //用来表示在线的用户

static int max_fd =0;

static void process_fn(int client_fd)
{
   
   int len;
        char buf[1024];

          printf("RECV from socket %d\n",client_fd);

     
   //第五步:与客户端在新的socket进行通讯

             //注意,是从client_fd,

               len = recv(client_fd,buf,sizeof(buf),0);
                  if(len <= 0)
                          {
                        close(client_fd);
                       FD_CLR(client_fd,&user_list); //从在线用户表中清除当前的socket


                         fprintf(stdout,"CLOSE socket %d\n",client_fd);
                             return ;
                           }

                  buf[len] = 0;
                  fprintf(stdout,"RECV(%d):\"%s\"\n",client_fd,buf);
                  
              //第六步:用send把数据照原样发给客户端

                   send(client_fd,buf,len,0);

       
               
}


int main(int argc ,char * argv[])
{
  unsigned short port = 9000;
   
   int listen_fd ,ret;
   int client_fd,fd;
   pthread_t pth;

    fd_set fdset;

   struct sockaddr_in addr; //ipv4的地址结构

   struct sockaddr_in client_addr ;
   socklen_t addr_len;


   int len,maxfd ;
  
   if(argc >1)
     {
         port = (unsigned short)atoi(argv[1]);
     }

  //第一步:创建一个IPV4,TCP,可以运行所有协议的SOCKET

  listen_fd = socket(PF_INET,SOCK_STREAM,0);
  if(listen_fd == -1)
   {
        perror("socket");
         return -1;
   }


   //第二步:用bind来指定端口

   memset(&addr,0,sizeof(addr));
    addr.sin_port = htons(port); //转成网络序的端口

   addr.sin_family = AF_INET; //ipv4的地址类型

   //addr.sin_addr.s_addr = inet_addr("0.0.0.0"); //bind 的sin_addr表示在哪一个网卡上有效,

   //绑定在0.0.0.0上表示这个SOCKET在所有网卡侦听

   addr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY = 0 = inet_addr("0.0.0.0")


    if(bind(listen_fd,(const struct sockaddr *)&addr,sizeof(addr)) == -1)
       {
          perror("bind");
            close(listen_fd);
          return -2;
       }

   //第三步: 通知内核里的TCP/IP协议栈,让这个SOCKET开始侦听网络上的请求

   // listen只是通知一下,通知成功立即执行后面代码

     if(listen(listen_fd,20) == -1)
       {
             perror("listen");
        close(listen_fd);
             return -3;
       }


      fprintf(stdout,"TCP Select Echo Server listen on %d\n",port);


      FD_ZERO(&user_list);

       
 
      
     while(1)
       {
             
          FD_ZERO(&fdset);
          FD_SET(listen_fd,&fdset);
           //把所有在线的socket加入到监听的集合中,每次循环都要重新加,

           // 因为上一次的集合已经被select清空了


          if(max_fd < listen_fd )

                     maxfd = (listen_fd +1);
               else
                   maxfd = (max_fd +1) ;

                  
           
            printf("maxfd = %d,max_fd=%d,listen_fd=%d\n",maxfd,max_fd,listen_fd);

           for(fd = 3 ; fd <= max_fd ; fd++)
               {
                  if(FD_ISSET(fd,&user_list))
                           FD_SET(fd,&fdset);
 

               }

         
          //开始监听,等着客户端的请求

           ret = select(maxfd,&fdset,NULL,NULL,NULL);
 

            if(ret < 1)
                  continue;
            
            //说明已经有socket接收数据,此时select会清空fdset,并把发现变化的socket重新放入fdset

            
           if(FD_ISSET(listen_fd,&fdset)) //说明有客户端发来联接请求

             {
             memset(&client_addr,0,sizeof(client_addr));
             client_addr.sin_family = AF_INET;
             addr_len = sizeof(client_addr); //必须赋值!

   

             
                printf("accept client ...\n");
                client_fd = accept(listen_fd,(struct sockaddr *)&client_addr,&addr_len);
               if(client_fd == -1)
                   {
                  perror("accept");
                     continue;
                   }

             fprintf(stdout,"client socket %d,addr %s,port %d\n",client_fd,
                    inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));

                  //把socket加入在线用户表

                FD_SET(client_fd,&user_list);
                   if(client_fd > max_fd)
                           max_fd = client_fd;

          
             }
           else //说明客户端发来ECHO信息,用FD_ISSET查出是哪一个socket

                {
                        printf("recv max_fd %d\n",max_fd);
                      for(fd = 3 ; fd <=max_fd ; fd++)
                            {
                               if(FD_ISSET(fd,&fdset))
                                        {
                                     process_fn(fd);
                                     break;
                                        }
                            }
               }
          
       }

     printf("exit");
      close(listen_fd);

}


客户端实现代码tcp_echo_cli.c



/*
  TCP select Echo Client
 
  Author :Andrew Huang

 */


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <string.h>


int main(int argc ,char * argv[])
{
   unsigned short port = 9000;
   
   int sock ;
    char ipaddr[32] = "127.0.0.1";
    char buf[32] = "hello server";
    struct sockaddr_in serv_addr;
    int len;

     if(argc >1)
     {
         port = (unsigned short)atoi(argv[1]);
     }

    if(argc >2)
     {
          memset(ipaddr,0,sizeof(ipaddr));
         strncpy(ipaddr,argv[2],sizeof(ipaddr)-1);
     }

    if(argc > 3)
       {
            memset(buf,0,sizeof(buf));
         strncpy(buf,argv[3],sizeof(buf)-1);
        }


   printf("connect server %s:%d,buf %s\n",ipaddr,port,buf);

  //第一步:创建一个IPV4,TCP,可以运行所有协议的SOCKET

   sock = socket(PF_INET,SOCK_STREAM,0);
   if(sock == -1)
      {
          perror("socket");
          return -1;
      }


    //第二步:对于客户端,port可用任意一个编号,如果不使用bind,协议栈会自动分配没用的端口0

    // 省略bind ....


   //第三步:用connect()去联接服务器

    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    serv_addr.sin_addr.s_addr = inet_addr(ipaddr);
 
     //printf("ipaddr %s,s_addr %x\n",ipaddr,ntohl( serv_addr.sin_addr.s_addr));


     if(connect(sock,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) == -1)
        {
            perror("connect");
              close(sock);
              return -2;
        }

      while(1)
        {
           
          //键盘输入

         if(fgets(buf,sizeof(buf)-1,stdin)== NULL)
                 break;

           //如果直接输入回车则退出

          if(buf[0]== '\n')
                   break;

        if(send(sock,buf,strlen(buf)+1,0)<= 0)
           {
               perror("send");
              close(sock);
              return -3;
           }

           len = recv(sock,buf,sizeof(buf),0);
           if(len >0)
             {
                 buf[len] = 0;
                 printf("SERVER:%s\n",buf);
             }

          }
              //关闭socket

            close(sock);
           
   
}


阅读(268) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~