各位老师帮帮忙,帮我看下服务端代码

legone2008 2008-07-30 12:46:29
今天写客户端时,突然发现服务端有两个很大的问题,时间紧急,哪位高手帮我看下,怎么完善一下啊。谢谢!

int
cmd_prep( char* srv_ip, int cmd_port )
{
int ii;
int retval;
int clisock_set[5];
int svrsock, clisock;
fd_set readfd, rfds;
struct sockaddr_in svr_addr, cli_addr;
pthread_t pth_id;
struct timeval tmout;
int cnt = 1;

int maxfd = -1;
int addr_len = sizeof(cli_addr);

svrsock = create_socket( srv_ip, cmd_port );
if ( svrsock == -1 ) {
perror( "create_socket()" );
exit(-1);
}

for ( ii = 0; ii < MAX_NUM; ii++ )
clisock_set[ii] = -1;

maxfd = svrsock;
FD_ZERO( &readfd );
FD_SET( svrsock, &readfd );

clisock_set[0] = svrsock;

while( 1 ) {
FD_ZERO( &rfds );
rfds = readfd;
tmout.tv_sec = 30;
tmout.tv_usec = 0;

retval = select( maxfd+1, &rfds, NULL, NULL, &tmout );
if ( -1 == retval && EINTR == errno )
continue;
if ( retval < 0 ) {
perror( "select err" );
exit(-1);
}

printf( "循环第[%d]次..........................\n", cnt );

/* 如果listen句柄svrsock可读,则生成与客户端连接可通信clisock描述符,
并放入select监测集合clisock_set中*/
if ( FD_ISSET( svrsock, &rfds ) ) {
clisock = accept( svrsock, (struct sockaddr *)(&cli_addr), &addr_len );
if ( clisock < 0 ) {
perror( "accept err" );
exit(-1);
}

for ( ii = 1; ii < MAX_NUM-1; ii++ ) {
if ( clisock_set[ii] != -1 ) { /* 已经是可通信描述符 */
continue;
}
else {
clisock_set[ii] = clisock; /* 新生成的可通信描述符 */
printf( "接收到客户端连接请求,socket=[%d]\n",clisock_set[ii] );
break;
}
}

if ( MAX_NUM - 1 == ii )
clisock_set[MAX_NUM - 1] = clisock;


FD_SET( clisock, &readfd ); /* 加入到监测描述符集中 */
if ( maxfd < clisock )
maxfd = clisock;

}

/* 与客户端所有可通信描述符中有数据通信产生,处理数据的与返回 */
for ( ii = 1; ii < MAX_NUM; ii++ ) {
int ret, nread;

if ( clisock_set[ii] == -1 )
continue;

if ( !FD_ISSET( clisock_set[ii], &rfds ) ) {
continue;
}

ioctl(clisock_set[ii],FIONREAD,&nread);
if (nread==0){
continue;
}

printf( "处理客户连接请求[%d],\n",clisock_set[ii] );
ret = pthread_create( &pth_id, NULL, msg_process,
(void *)clisock_set[ii] );
if ( ret ) {
perror( "pthread_create() err" );
continue;
}

FD_CLR( clisock_set[ii], &readfd );
clisock_set[ii] = -1;
}

//printf( "\n" );
cnt++;
}

close( svrsock );
return 0;
}


问题1:如果客户端异常中断,也就是客户端关闭了socket,服务端就会进入不断的循环中,我在服务端设置的超时循环就不起作用了,请问一下什么原因?
问题2:如果我的select同时监听了1024文件描述符,在没有描述符发生变化的情况下会30秒轮循一次。
如果第1024个描述符每30秒会置位一次,也就意味着select在30秒内必然有一个描述符发生变化,从而调用处理函数去处理,而其余1023个描述符仍然等待,等待多久是未知,很有可能是永远不会有数据变化。那么会有1023个描述符一直白白占用着1023位,不能清理,这种问题大家该如何解决啊?也就是说那些失效的描述符怎么清理?

谢谢!
...全文
101 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
fierygnu 2008-07-31
  • 打赏
  • 举报
回复
看看TCP协议连接建立和拆除过程。
fuqd273 2008-07-31
  • 打赏
  • 举报
回复
建议还是仔细读一下你最早贴出来的代码。
其实那个代码框架挺好的。

client的write/send/close都能使server端的select解消阻塞。而ioctl就是为了判断是哪种原因使得select阻塞被解消的。

这样说明白了么?
如果你在客户端write/send之前就中断客户端,那么服务端就不是要对新描述符一直检测(循环检测),而是会发现原有连接已经断开,而将clisock_set[ii]初期化。另一方面,客户端在重新connect之前是无法直接send的。
legone2008 2008-07-30
  • 打赏
  • 举报
回复
  
ioctl(clisock_set[ii],FIONREAD,&nread);
if ( nread==0 ){
FD_CLR( clisock_set[ii], &readfd );
close( clisock_set[ii] );
clisock_set[ii] = -1;
continue;
}


我这样修改了一下,问题1解决了。但解决得不明白,fuqd273 担心的那个我也没有明白。
而且还有个问题,服务端accept到一个新的描述符后,判断这个描述符是否可读,应该是客户端write/send调用执行之后,服务端select才能检测到吧?
如果是这样的话,那么我在客户端write/send之前就中断客户端,那么服务端是不是要对新描述符一直检测(循环检测),ioctl就应该不起作用了啊,因为他在FD_ISSET( clisock[ii], &readfd )之后啊。这样看来,失效的描述就得不到清理,
我对ioctl的理解为:FIONREAD命令读取串行端口输入缓冲区中的字节数。
快乐田伯光 2008-07-30
  • 打赏
  • 举报
回复
问题1,读写的时候要检查返回值。要不就算对方断开的socket也会一直认为有东西读
fierygnu 2008-07-30
  • 打赏
  • 举报
回复
就不能这样用:
ioctl(clisock_set[ii],FIONREAD,&nread);
if (nread==0){
continue;
}
fuqd273 2008-07-30
  • 打赏
  • 举报
回复
                          rec  =  recv(fd[j],buffer,BUF_SIZE,0);  
if(rec < 0)
{
fprintf(stderr,"received from client: %s failure.\n",inet_ntoa(clientaddr.sin_addr));
exit(1);
}
else if (rec == 0) //如果客户端终止
{
close(fd[j]); //关闭该套接口
FD_CLR(fd[j], &rd); //将该套接口描述字从rd中清除
fd[j] = -1; //将fd数组中相应的描述字置为-1
}
else
{
fprintf(stdout,"success received from client: %s ,the word is: %s.\n",inet_ntoa(clientaddr.sin_addr),buffer);
if(send(fd[j], buffer, rec, 0) != rec)
{
fprintf(stderr,"sento client: %s ,failure. \n",inet_ntoa(clientaddr.sin_addr));
exit(1);
}
}

其实你最初的版本里面就有扫尾处理的,但是被你搞没了。
看看,补上吧。
fuqd273 2008-07-30
  • 打赏
  • 举报
回复

ioctl(clisock_set[ii],FIONREAD,&nread);
if (nread==0){
continue;
}

这段处理得不够细致
这个处理是针对client 的close处理的。
应该在发现close之后clisock_set[ii]=-1,进行初期化处理。

但是你现在的流程中,又不能简单的初期化之。
因为我担心client的connect和close动作都有可能走到这里。。。

至于问题2,问题1解决了也就自然没有问题2了。

23,218

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧