我最近想用C++面向对象的方式写一个linux下的聊天程序。
但是对一些模块不知道怎么划分:
1、服务器初始化后(socket,bind,listen后),我想参照unp里所说,把“监听listenfd有没有新的连接可以accept”和“监听所有的已经连接的套接字有没有消息发过来”这两个行为进行select多路复用。并且对有可以读的已连接套接字都创建出一个线程来处理用户发来的消息。
这一连串的动作我不知道该分在哪里
2、对于已在线用户,我打算以链表的方式储存。我目前的想法是用户管理类user和聊天管理类chat分别来执行用户的登录,登出对链表的结点进行增删和用来接收用户发来的消息然后转发给指定用户。但是这样一来user和chat必须都要能访问到用户链表,那么如果我把用户链表定义成全局变量是不是就违背了面向对象的封装特性了呢?
一下是C代码实现的1.中所说的功能:
maxfd = listenfd;
maxi = -1;
for(i=0; i<FD_SETSIZE; ++i)
{
client_sockfd[i] = -1;
}//for
/*清空allset描述符集*/
FD_ZERO(&allset);
/*将监听描述符加到allset中*/
FD_SET(listenfd , &allset);
/*(6) 接收客户链接*/
while(1)
{
rset = allset;
/*得到当前可读的文件描述符数*/
nready = select(maxfd+1 , &rset , NULL , NULL , 0);
/*测试listenfd是否在rset描述符集中*/
if(FD_ISSET(listenfd , &rset))
{
/*接收客户端的请求*/
clilen = sizeof(cliaddr);
if((connfd = accept(listenfd , (struct sockaddr *)&cliaddr , &clilen)) < 0)
{
perror("accept error.\n");
exit(1);
}//if
printf("server: got connection from %s\n", inet_ntoa(cliaddr.sin_addr));
/*查找空闲位置,设置客户链接描述符*/
for(i=0; i<FD_SETSIZE; ++i)
{
if(client_sockfd[i] < 0)
{
client_sockfd[i] = connfd; /*将处理该客户端的链接套接字设置在该位置*/
break;
}//if
}//for
if(i == FD_SETSIZE)
{
perror("too many connection.\n");
exit(1);
}//if
/* 将来自客户的连接connfd加入描述符集 */
FD_SET(connfd , &allset);
/*新的连接描述符 -- for select*/
if(connfd > maxfd)
maxfd = connfd;
/*max index in client_sockfd[]*/
if(i > maxi)
maxi = i;
/*no more readable descriptors*/
if(--nready <= 0)
continue;
}//if
/*接下来逐个处理连接描述符*/
for(i=0 ; i<=maxi ; ++i)
{
if((sockfd = client_sockfd[i]) < 0)
continue;
if(FD_ISSET(sockfd , &rset))
{
/*如果当前没有可以读的套接字,退出循环*/
if(--nready < 0)
break;
pthread_create(&pid , NULL , (void *)handleRequest , (void *)&sockfd);
}//if
/*清除处理完的链接描述符*/
FD_CLR(sockfd , &allset);
client_sockfd[i] = -1;
}//for
}//while
全局变量可以放到单独的单例类里,还有要是多线程访问的话需要互斥。。
服务器的话可以简单划分为建立监听等待用户,之后客户端消息处理不就行了。。