Chinaunix

标题: select的返回值老是为0,我有数据输入的 [打印本页]

作者: jd808    时间: 2009-08-05 19:10
标题: select的返回值老是为0,我有数据输入的
#include<stdio.h>
#include <stdlib.h>
#include<strings.h>
#include <string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>

#define PORT 4502
#define BACKLOG 1
#define MAXDATASIZE 4000
#define ONLINE 8000 /*定义一个最大5000人在线的情况*/

#define  PTHREAD_THREADS_MAX   50 /*允许一个进程创建这个数量的线程*/

struct ARG
{
    int connfd[ONLINE][6]; //用户信息列表 数字型
    char *connfds[ONLINE][20]; //用户信息列表 字符串型
    int currently,pthread_id[PTHREAD_THREADS_MAX];
    struct sockaddr_in client;
    int pth;//下一个线程
};

////////////////////////////////////////
void *func(void *arg );
int simulate_accept(void *arg,int pt);
int acceptfz(void *arg,int client_fd,int e);

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

int main()
{
     pthread_t tid;
    pthread_attr_t attr;

     int listenfd,i, connectfd;
     struct sockaddr_in server;
     struct sockaddr_in client;
     socklen_t addrlen;
     char buf[MAXDATASIZE];
     struct ARG *arg, in;
     arg = &in;
   

    printf("初始化数据\n";
    for (i=0;i<ONLINE;i++)
    {
         //初始化在线人数
        arg->connfd[0]=0;//连接句柄
        arg->connfd[1]=0;//联盟ID
        arg->connfd[2]=0;//门派ID
        arg->connfd[3]=0;//是否允许登陆0为不允许
        arg->connfd[4]=0;//使用那个线程
        arg->connfd[5]=0;//是否已经加入select的句柄集
   
        arg->connfds[0]=(char*)"";//昵称
        arg->connfds[1]=(char*)"";//玩家用户名
    }
    arg->pth=100;

    printf("启动线程\n";
    pthread_attr_init(&attr);
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS);//把线程脱离出去
    pthread_attr_setstacksize(&attr,512);//线程堆贱大小

     for(i=0;i<THREAD_THREADS_MAX;i++)
     {
            //创建50个应用线程
         if(pthread_create(&tid,&attr, func, (void *)arg))
         {
              perror("error pthread_create2";
         }
        printf("启动处理线程%d\n",tid);
        arg->pthread_id=tid;//取得线程ID
     }

    printf("建立socket\n";
     if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
     {
        perror("socket() error.";
        //exit(1);
     }
     
     int opt = 1;
     setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
     
     bzero(&server, sizeof(server));
     server.sin_family = AF_INET;
     server.sin_port = htons(PORT);
     server.sin_addr.s_addr = htonl(INADDR_ANY);
     if(bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1)
     {
          perror("bind() error.";
          //exit(1);
     }
     if(listen(listenfd, BACKLOG) == -1)
     {  
          perror("listen() error.";
          //exit(1);
     }
     printf("主程序进入守护状态\n";
     addrlen = sizeof(client);
     while(1)
     {
         if((arg->currently= accept(listenfd, (struct sockaddr *)&client, &addrlen )) == -1)
         {
              perror("accept() error.";
              //exit(1);
         }
        acceptfz(arg,arg->currently,1);//像socket注册句柄
     }
    return 0;
}

void *func(void *arg)
{
    //便利属于这个线程的100个用户
    //pthread_detach(pthread_self());//上面已经把这个线程分离出来啦
    struct ARG *args;
    args =  (struct ARG *)arg;

    int retval,i,j,pt=pthread_self();//取得线程ID
    int in=0;
    char *tmp[20];
    char*bufs = (char *) malloc ( sizeof(int) *MAXDATASIZE);

    fd_set rfds;
    struct timeval tv;
    int oks=0,maxfd=-1;
   
    maxfd = 0;

    while(1)
    {
            i=simulate_accept(args,pt);//返回用户下标
            printf("句柄分析 %i-------用户序列号:%i\n",args->connfd[0],i);
            //提取出属于自己线程的句柄,并且句柄不能为0
            
            //是否加入集合中
            if(args->connfd[5]==0)
            {
                //把集合清空
                FD_ZERO(&rfds);
                // 把当前连接句柄new_fd加入到集合中
                FD_SET(args->connfd[0], &rfds);
                args->connfd[5]=1;
                printf("已将句柄 %i 加入到集合中----%i\n",args->connfd[0],i);
            }
            
            if (args->connfd[0] > maxfd)
            {
                maxfd = args->connfd[0];
            }
            // 设置最大等待时间
            tv.tv_sec = 0;
            tv.tv_usec =100;
            //printf("最大句柄----%i\n",maxfd);
        
            // 开始等待
            retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
        
            if (retval == -1)
            {
                perror("select error.";
               continue;
           } else if (retval == 0) {
               //printf("没有任何消息到来,继续等待……%i\n",i);
                continue;
            } else {
                printf("有一个操作信号\n");
                if (FD_ISSET(args->connfd[0], &rfds))
                {
                        memset(bufs, 0, MAXDATASIZE);
                        memset(tmp, 0, 20);
                        
                         if (read(args->connfd[0], bufs, MAXDATASIZE)< 0)
                        {
                                perror("read error");
                                acceptfz(args,args->connfd[0],0);
                                close(args->connfd[0]);
                                //exit(0);
                                 continue;
                        }
                        
                        printf("读取客户端信息: %s\n",bufs);
                }
            }
    }
}



//将accept加入到用户列表中或注销
int acceptfz(void *arg,int client_fd,int e)
{
    struct ARG *info;
    info =  (struct ARG *)arg;
    int j;
    if(e==1)
    {
        for(j=0;j<ONLINE;j++)
        {
                //找一个空的句柄存储仓
                if(info->connfd[j][0]==0)
                {
                    //将句柄交给存储仓
                    info->connfd[j][0]=client_fd;//句柄注册
               
                    //为句柄分配线程号
                    if(info->pth==100 || info->pth==PTHREAD_THREADS_MAX)
                    {
                        info->pth=1;
                        info->connfd[j][4]=info->pthread_id[0];//初始化第一个
                    }else{
                        info->connfd[j][4]=info->pthread_id[info->pth];
                        printf("新线程号:%i--------pth:%i\n",info->pthread_id[info->pth],info->pth);
                        info->pth++;
                    }
               
                    printf("将连接句柄交给%s:%i,线程号:%i\n","connfd",info->connfd[j][0],info->connfd[j][4]);
                    return j;
                }
        }
    }else{
        for(j=0;j<ONLINE;j++)
        {
            if(info->connfd[j][0]==client_fd)
            {
                printf("用户:%i连接被销毁\n", info->connfd[j][0]);
               
                info->connfd[j][0]=0;
                info->connfd[j][1]=0;
                info->connfd[j][2]=0;
                info->connfd[j][3]=0;
                info->connfd[j][4]=0;
                info->connfd[j][4]=0;
            
                info->connfds[j][0]="";
                info->connfds[j][1]="";
               
                pthread_exit((char*)pthread_self());//销毁线程释放内存
                break;
            }
        }
    }
    return 0;
}


int simulate_accept(void *arg,int pt)
{
    struct ARG *args;
    args =  (struct ARG *)arg;
    int i,j,oks=0;
    //int j = (int ) malloc (sizeof(int)* 5);
    for(i=0;i>-1;i++)
    {
        if(i==ONLINE){i=0;}
            
        for (j=0;j<ONLINE;j++)
        {
            if(args->connfd[j][4]==pt && args->connfd[j][0]!=0 && args->connfd[j][5]==0)
            {
                printf("已经检查到一个新句柄 %i----用户序列号:%i----是否在集合中%i\n",args->connfd[j][0],j,args->connfd[j][5]);
                return j;
            }
        }
        
        if(oks==0)
        {//句柄列表中是否有有效的连接
            //printf("线程处于等待状态%d\n",pt);
            sleep(1);
             continue;
        }
    }
}

代码我是提取出来的,帮我看看逻辑是否有问题,我是新学C的,忘高手帮忙看下为什么select检查不到我前台发送过来的数据(就是read给没被执行到郁闷,连接正常)。
这个是我自己写的,我的思路是想让一个线程守候端口,如果有客户端连接则忘一个数组里把这个句柄加入到数组中,
然后其他50个线程就处理这个数组中的数据,acceptfz函数在守候线程里就开始分配句柄属于那个线程的

[ 本帖最后由 jd808 于 2009-8-6 16:18 编辑 ]
作者: jd808    时间: 2009-08-06 11:18
没人给个意见??
作者: shurady    时间: 2009-08-06 12:20
代码太长了
作者: jd808    时间: 2009-08-06 12:59
这是一个基本的呀,在精简了你们就找不到问题所在,而且还不知道代码到底是干什么的
作者: Cyberman.Wu    时间: 2009-08-06 14:19
程序有够乱的,这么简单的功能写出这么复杂的代码来。你的线程中是不是可以select多个socket?那你每得一个新的之后好像就FD_ZERO了,只有一个。另外看如下判断:
            //是否加入集合中

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(args->connfd[5]==0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//把集合清空

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FD_ZERO(&rfds);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 把当前连接句柄new_fd加入到集合中

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FD_SET(args->connfd[0], &rfds);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;args->connfd[5]=1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("已将句柄 %i 加入到集合中----%i\n",args->connfd[0],i);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}


如果这段代码不是每次都执行那问题就来了,select的几个文件句柄集合都是In-Out参数,一旦超时一次集合中全部为空。
作者: jd808    时间: 2009-08-06 16:03
大哥 代码不简单啊,里面很多功能我只是把代码抽取出来才显得比较乱的
上面那段代码是否执行要看  
i=simulate_accept(args,pt);
当他接收到一个新的句柄之后就返回(属于本线程的),否者就一直挺在这里呢 与真正的accept效果差不多的方式。
不然的话 func 函数可能就一直处于死循环很消耗cpu呢
当然前面的程序把接到的句柄分配给这个线程的时候,而这个线程见到有新的句柄加入就会执行下面,那个args->connfd[5]是用来判断是否已经加入到集合中,这个其实在simulate_accept函数里我已经判断过的,
分配句柄交给那个线程处理是油 acceptfz(arg,arg->currently,1);
函数处理,这些还数我都贴出,带回我把他弄成彩色的加在下面方便你们观看
if(args->connfd[5]==0)
{
    //把集合清空

    FD_ZERO(&rfds);
    // 把当前连接句柄new_fd加入到集合中

    FD_SET(args->connfd[0], &rfds);
    args->connfd[5]=1;
    printf("已将句柄 %i 加入到集合中----%i\n",args->connfd[0],i);
}


[ 本帖最后由 jd808 于 2009-8-6 16:14 编辑 ]
作者: xieweihua    时间: 2009-08-06 16:12
最好是用一个变量来存rfds的值。
然后才传给select,

因为select执行后会改娈rfds的值。你最好再去了解一下select是如何工作的。

我没也没仔细看你的代码,因为太长了。

[ 本帖最后由 xieweihua 于 2009-8-6 16:13 编辑 ]
作者: jd808    时间: 2009-08-06 16:15
发不了彩色的 没办法说太长了,在说了分太多次发你们也不定看的舒服

[ 本帖最后由 jd808 于 2009-8-6 16:17 编辑 ]




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2