套接字联网API之二 select作用和案例

来源:互联网 发布:手机java编程软件 编辑:程序博客网 时间:2024/06/05 02:36

这个系列的上一篇文章讲了套接字联网API在服务端和客户端的几个主要函数。 这篇文章用来实现,顺便说一下select模型,也在下面的代码中用到。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

select模型出现的真正原因在网上一直找不到,经过仔细思考其实只有一个,就是解决异步的问题;而且这个select是要结合多线程的。下面对这些进行说明。

1) 当使用阻塞 I/0时, 当阻塞在输入的时候无法进行读取的监听;

2) 当使用非阻塞I/O时,会在读取的监听上消耗CPU的资源。 其实这种情况即使忽略消耗的CPU资源,也是不能实现异步的。

3) 这时可能会考虑到使用多线程,就是让读取的监听  和  输入放在不同的线程中。

比如采用select监听一个套接字, 如果返回一个结果,就开辟一个新线程用来进行交互,假设这个新的线程阻塞在输入上,监听线程还是能够监听的,如果收到了一个FIN,能够及时的终止这个新的线程。  这就是select的作用,但是是不是每个连接都要设置一个select?  不用的,之需要在全局的主线程设置一个select就可以了,每当一个连接建立,就信开辟一个线程,主线程仍然在监听;而它又收到监听描述符上的信息后(比如FIN报文),就会对该描述符对应的线程进行处理。


下面是select的一个模型,分为服务端和客户端,有详细的注释。

服务端流程如下: 只发了一个文件,目的是弄清流程

#include <stdio.h>#include <stdlib.h>#include "server.h"#include "database.h"#include "userdata.h"#include "struct.h"#include "thread.h"int main(){    int x = 0;    //关于在线用户变量    userlist_init(&L);    pthread_t hThread[FD_SETSIZE];    //套接字变量    struct message ma;    int i,maxi;   //maxi代表存储最大已连接描述符的序号    fd_set allset,curset;    int readyfd,maxfd;    //select返回的已准备好的描述符    int client[FD_SETSIZE];//连接描述符数组    int ser_sockfd,cli_sockfd,confd;    int ser_length,cli_length;    struct sockaddr_in server_address;    struct sockaddr_in client_address;    //先连上数据库     连接的mysql的数据库,用户名和密码均为majintao    my_sql=mysql_init(NULL);    if(NULL == my_sql)    {        printf("mysql init error\n");        return mysqlinit;    }    my_sql=mysql_real_connect(my_sql,"localhost","majintao","majintao","chatroom",0,NULL,0);    if(NULL == my_sql)    {        printf("connect error\n");        return mysqlconnect;    }    //创建一个未命名的套接字    ser_sockfd=socket(AF_INET, SOCK_STREAM,0);    if(ser_sockfd < 0)    {        printf("create socket error\n");        return 0;    }    bzero(&server_address,sizeof(server_address));    server_address.sin_family=AF_INET;    server_address.sin_addr.s_addr=htonl(INADDR_ANY);   //暂定    server_address.sin_port=htons(9734);//暂定    int bindfd=bind(ser_sockfd,(struct sockaddr*)&server_address,sizeof(server_address));    if(bindfd < 0)    {        printf("bind error\n");        return 0;    }    //客户端数据处理    bzero(&client_address,sizeof(client_address));    //cl_echo(ser_sockfd,(struct sockaddr*)&server_address,sizeof(server_address));    listen(ser_sockfd,LISTENQ);    FD_ZERO(&allset);    FD_SET(ser_sockfd,&allset);    maxfd=ser_sockfd;    //初始化描述符数组    for(i=0;i<FD_SETSIZE;i++)    {        client[i]=-1;    }    //开始select监听套接字    curset=allset;    for(;;)    {        //        <span style="color:#ff0000;">readyfd=select(maxfd+1,&curset,NULL,NULL,NULL);</span>        if(FD_ISSET(ser_sockfd,&curset))        {                cli_length=sizeof(client_address);                if(-1 == (confd=accept(ser_sockfd,(struct sockaddr*)&client_address,&cli_length) ) )                {                    printf("accept error\n");                    exit(1);                }                //把描述符放到合适的位置                for(i =0;i<FD_SETSIZE;i++)                {                    if(client[i] <= 0)                    {                        client[i] = confd;                        break;                    }                }                //客户端连接太多                if(i == FD_SETSIZE)                {                    printf("too many clients\n");                    exit(1);                }                //重新设置                FD_SET(confd,&allset);                //如果已连接描述符大于监听描述符,更改                //if(confd > maxfd)                //{                  //  maxfd = confd;               // }                //更新描述符所在序号的最大值                if(i > maxi)                {                    maxi = i;                }                //如果只有一个监听描述符,就不检查已连接描述符了                /*if(--readyfd  <= 0)                {                    continue;                }*/        }   //fd_set    //对每一个已经连接描述符做处理    for(i = 0;i <=maxi; i++)    {        if( (testfd = client[i]) < 0)            continue;        if( FD_ISSET(testfd,&allset) )        {            int tThread;            //这个时候创建一个线程,意思为每个已经连接的描述符创建一个线程            <span style="color:#ff0000;">tThread = pthread_create(&hThread[x++], NULL, thread_ser,&testfd);</span>            if(tThread != 0)            {                printf("Thread create failed\n");                return -1;            }        }        client[i] = -1;   //保证不会重复创建线程        if(--readyfd <= 0)                break;    }//第二个for    }//最外层for循环}//main

客户端没有使用select,就不介绍了。




0 0