linux 网络编程---->多路复用:select实例!

好吧,我承认找了好久,网上都没有像样的完整的实例,然后自己参照书自己写一个吧!

//!> server 端代码
//!> server.c


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>

#define BUF_LEN 1024
#define SERV_PORT 6000
#define FD_SIZE 100
#define MAX_BACK 100

int main( int argc, char ** argv )
{
    int             listenfd, connfd, sockfd, maxfd, maxi, i;
    int             nready, client[FD_SIZE];        //!> 接收select返回值、保存客户端套接字
    int             lens;
    ssize_t     n;                //!> read字节数
    fd_set        rset, allset;    //!> 不要理解成就只能保存一个,其实fd_set有点像封装的数组
    char         buf[BUF_LEN];               
    socklen_t    clilen;
    struct sockaddr_in servaddr, chiaddr;
   
    if( ( listenfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
    {
        printf( "Create socket Error : %d\n", errno );
        exit( EXIT_FAILURE );
    }
   
    //!>
    //!> 下面是接口信息
    bzero( &servaddr, sizeof( servaddr ) );
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr  =htonl( INADDR_ANY );
    servaddr.sin_port = htons( SERV_PORT );
   
    //!>
    //!> 绑定
    if( bind( listenfd, ( struct sockaddr * )&servaddr, sizeof( servaddr ) ) == -1 )
    {   
        printf("Bind Error : %d\n", errno);
        exit(EXIT_FAILURE  );
    }
   
    //!>
    //!> 监听
    if( listen( listenfd, MAX_BACK ) == -1 )
    {
        printf("Listen Error : %d\n", errno );
        exit( EXIT_FAILURE );
    }
   
    //!> 当前最大的感兴趣的套接字fd
    maxfd = listenfd;    //!> 当前可通知的最大的fd
    maxi = -1;            //!> 仅仅是为了client数组的好处理
   
    for( i = 0; i < FD_SIZE; i++ )    //!> 首先置为全-1
    {
        client[i] = -1;        //!> 首先client的等待队列中是没有的,所以全部置为-1
    }
   
    FD_ZERO( &allset );        //!> 先将其置为0
    FD_SET( listenfd, &allset );
                    //!> 说明当前我对此套接字有兴趣,下次select的时候通知我!
   
    while( 1 )
    {
        rset = allset;//!> 由于allset可能每次一个循环之后都有变化,所以每次都赋值一次
        if( (nready = select( maxfd + 1, &rset, NULL, NULL, NULL )) == -1)
        {                    //!> if 存在关注
            printf("Select Erorr : %d\n", errno );
            exit( EXIT_FAILURE );
        }
       
        if( nready <= 0 )            //!> if 所有的感兴趣的没有就接着回去select
        {
            continue;
        }
       
       
       
        if( FD_ISSET( listenfd, &rset ) )            //!> if 是监听接口上的“来电”
        {                                            //!>
            //!> printf("server listen ...\n");
            clilen = sizeof( chiaddr );
           
            printf("Start doing... \n");
           
               if( ( connfd  = accept( listenfd, (struct sockaddr *)&chiaddr, &clilen ) ) == -1 )
               {                                        //!> accept 返回的还是套接字
                   printf( "Accept Error : %d\n", errno );
                   continue;
               }
              
              
               for( i = 0; i < FD_SIZE; i++ )    //!> 注意此处必须是循环,刚开始我认
                                                   //!> 为可以直接设置一个end_i来直接处
                                                   //!> 理,实质是不可以的!因为每个套接
               {                                    //!> 字的退出时间是不一样的,后面的
                   if( client[i] < 0 )                //!> 可能先退出,那么就乱了,所以只
                   {                                //!> 有这样了!
                       client[i] = connfd;            //!> 将client的请求连接保存
                       break;
                   }
               }
              
               if( i == FD_SIZE )                //!> The last one
               {
                   printf( "To many ... " );
                   close( connfd );            //!> if 满了那么就不连接你了,关闭吧
                continue;                    //!> 返回
               }
                                            //!> listen的作用就是向数组中加入套接字!
            FD_SET( connfd, &allset );    //!> 说明现在对于这个连接也是感兴趣的!
                                            //!> 所以加入allset的阵容
            if( connfd > maxfd )            //!> 这个还是为了解决乱七八糟的数组模型
                                            //!> 的处理
            {
                maxfd = connfd;
            }
           
            if( i > maxi )                    //!> 同上
            {
                maxi = i;
            }
        }

        //!> 下面就是处理数据函数( 其实说本质的select还是串行 )
        for( i = 0; i <= maxi; i++ )        //!> 对所有的连接请求的处理
        {
            if( ( sockfd = client[i] ) > 0 )    //!> 还是为了不规整的数组
            {            //!> 也就说client数组不是连续的全正数或者-1,可能是锯齿状的
                if( FD_ISSET( sockfd, &rset ) )    //!> if 当前这个数据套接字有要读的
                 {
                     memset( buf, 0, sizeof( buf ) );    //!> 此步重要,不要有时候出错
                
                     n = read( sockfd, buf, BUF_LEN);
                     if( n < 0 )
                     {
                         printf("Error!\n");
                         close( sockfd );            //!> 说明在这个请求端口上出错了!
                        FD_CLR( sockfd, &allset );
                        client[i] = -1;
                        continue;
                     }
                    if( n == 0 )
                    {
                        printf("no data\n");
                        close( sockfd );            //!> 说明在这个请求端口上读完了!
                        FD_CLR( sockfd, &allset );
                        client[i] = -1;
                        continue;
                    }
                   
                    printf("Server Recv: %s\n", buf);
                   
                    if( strcmp( buf, "q" ) == 0 )                //!> 客户端输入“q”退出标志
                    {
                        close( sockfd );
                        FD_CLR( sockfd, &allset );
                        client[i] = -1;
                        continue;
                    }
                   
                    printf("Server send : %s\n", buf);
                    write( sockfd, buf, n );        //!> 读出来的写进去
                }
            }
        }
       
    }
   
    return 0;
}


//!> client 端代码
//!> client.c


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include  <arpa/inet.h>
#include <sys/select.h>

#define MAXLINE 1024
#define SERV_PORT 6000

//!> 注意输入是由stdin,接受是由server发送过来
//!> 所以在client端也是需要select进行处理的
void send_and_recv( int connfd )
{
    FILE * fp = stdin;
    int   lens;
    char send[MAXLINE];
    char recv[MAXLINE];
    fd_set rset;
    FD_ZERO( &rset );
    int maxfd = ( fileno( fp ) > connfd ? fileno( fp ) : connfd  + 1 );   
                                                //!> 输入和输出的最大值
    int n;
   
    while( 1 )
    {
        FD_SET( fileno( fp ), &rset );
        FD_SET( connfd, &rset );            //!> 注意不要把rset看作是简单的一个变量
                                                //!> 注意它其实是可以包含一组套接字的哦,
                                                //!> 相当于是封装的数组!每次都要是新的哦!
       
        if( select( maxfd, &rset, NULL, NULL, NULL ) == -1 )
        {
            printf("Client Select Error..\n");
            exit(EXIT_FAILURE  );
        }
       
        //!> if 连接口有信息
        if( FD_ISSET( connfd, &rset ) )    //!> if 连接端口有信息
        {
            printf( "client get from server ...\n" );
            memset( recv, 0, sizeof( recv ) );
            n = read( connfd, recv, MAXLINE );
            if( n == 0 )
            {
                printf("Recv ok...\n");
                break;
            }
            else if( n == -1 )
            {
                printf("Recv error...\n");
                break;
            }
            else
            {
                lens = strlen( recv );
                recv[lens] = '\0';
                //!> 写到stdout
                write( STDOUT_FILENO, recv, MAXLINE );
                printf("\n");
            }

        }
       
        //!> if 有stdin输入
        if( FD_ISSET( fileno( fp ), &rset ) )    //!> if 有输入
        {
            //!> printf("client stdin ...\n");
           
            memset( send, 0, sizeof( send ) );
            if( fgets( send, MAXLINE, fp ) == NULL )
            {
                printf("End...\n");
                exit( EXIT_FAILURE );
            }
            else
            {
                //!>if( str )
                lens = strlen( send );
                send[lens-1] = '\0';        //!> 减一的原因是不要回车字符
                                            //!> 经验值:这一步非常重要的哦!!!!!!!!
                if( strcmp( send, "q" ) == 0 )
                {
                    printf( "Bye..\n" );
                    return;
                }
               
                printf("Client send : %s\n", send);
                write( connfd, send, strlen( send ) );
            }
        }
       
    }
   
}

int main( int argc, char ** argv )
{
    //!> char * SERV_IP = "10.30.97.188";
    char     buf[MAXLINE];
    int       connfd;
    struct sockaddr_in servaddr;
   
    if( argc != 2 )
    {
        printf("Input server ip !\n");
        exit( EXIT_FAILURE );
    }
   
    //!> 建立套接字
    if( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
    {
        printf("Socket Error...\n" , errno );
        exit( EXIT_FAILURE );
    }

    //!> 套接字信息
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
   
    //!> 链接server
    if( connect( connfd, ( struct sockaddr *  )&servaddr, sizeof( servaddr ) ) < 0 )
    {
        printf("Connect error..\n");
        exit(EXIT_FAILURE);
    }   
   

    //!>
    //!> send and recv
    send_and_recv( connfd );
   
    //!>

    close( connfd );
    printf("Exit\n");
   
    return 0;
}


编译+运行:

gcc -o server server.c
gcc -o client   client.c

./server
./client 10.80.1.251    ( 注意参数你指定的server的IP哦  )


如果要多个一起运行,给个脚本:
#!/bin/sh
index=10

while [ $index -gt 0 ]
do
     ./client  10.80.1.251 &          # 注意在后台哦,所以只能看到在server下的现象,可以自己改进
      $index = `expr $index - 1`
done

exit 0



FROM: http://blog.sina.com.cn/s/blog_6dc9e4cf0100ycuw.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值