您的位置:首页 > 其它

使用select侦查客户端的连接以及接收客户端的数据

2013-11-05 17:07 691 查看
主要测试select的用法,同时包含socket/bind/listen/accept/connect/send/recv/close/closesocket等的使用

开始打开一个服务器socket。通过select侦查 1 键盘输入 2 客户的连接请求 3 客户连接之后的数据传送请求。

1 侦查键盘的输入,有的话就输入到bre变量中,测试其值,是-1的话退出循环进而退出程序。

2 serverSocket1 收到客户的连接请求后,accept出一个新的socket,放入subSockets[MAX_CLIENTS]中。

3 每一个有效的subSockets[] 都在select里侦查,有数据需要收的时候就读进来,printf出去

selectaccept.c:

/*
主要测试select的用法,同时包含socket/bind/listen/accept/connect/send/recv/close/closesocket等的使用
开始打开一个服务器socket。通过select侦查 1 键盘输入 2 客户的连接请求  3 客户连接之后的数据传送请求。

1 侦查键盘的输入,有的话就输入到bre变量中,测试其值,是-1的话退出循环进而退出程序。
2 serverSocket1 收到客户的连接请求后,accept出一个新的socket,放入subSockets[MAX_CLIENTS]中。
3 每一个有效的subSockets[] 都在select里侦查,有数据需要收的时候就读进来,printf出去

对应的client 在selectclient.c里
*/

#include <stdio.h>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/errno.h>

#define closesocket close

#endif

#define LEN 1024
#define MAX_CLIENTS  32
int main()
{
char buf[LEN] ;
int serverSocket1  ;
int subSockets[MAX_CLIENTS] = {-1} ;
int sockets = 0 , is;
int selected ;
int readed;
struct sockaddr   remoteAddr ;
int size_remoteAddr = sizeof( remoteAddr )  ;
struct sockaddr_in sockAddr_in = {0};

int nfds = 0 ;
fd_set readfds ;
int re ;
int bre;
struct timeval ttv= {0, 10000};

#ifdef WIN32
WSADATA wsd ;
serverSocket1 = WSAStartup( WINSOCK_VERSION , &wsd ) ;
#endif

printf("start\n" ) ;
serverSocket1 = socket( PF_INET , SOCK_STREAM ,  0 ) ;

if( serverSocket1 < 0 )
printf("open server socket 1 error\n" ) ;

printf("open server socket  %d \n" , serverSocket1 ) ;

sockAddr_in.sin_family = AF_INET ;
sockAddr_in.sin_addr.s_addr = 127+(1<<24) ;//127.0.0.1 // INADDR_ANY ;
sockAddr_in.sin_port = 1800 ;

re = bind( serverSocket1 , (struct sockaddr* )&sockAddr_in , sizeof( sockAddr_in ) ) ;
printf(" bind ok %d\n" , re ) ;

re = listen( serverSocket1 , 4 ) ;
printf("listen ok %d\n" , re ) ;

while ( 1 ) {
//   printf("1 time:%d\n" , GetTickCount() ) ;
FD_ZERO(  & readfds  ) ;
nfds = serverSocket1;
FD_SET( serverSocket1 , & readfds ) ;
#ifndef WIN32
FD_SET( 0 , & readfds ) ; //stdin,linux ; 只在uinx/linux下起作用,windows下会造成select错误WSAENOTSOCK
#endif

for( is = 0 ; is < sockets ; is ++ )
{
FD_SET( subSockets[is] , &readfds ) ;
if( nfds <  subSockets[is] )
nfds = subSockets[is] ;
}
nfds ++ ;

selected = select ( nfds , &readfds , 0 ,0 ,&ttv ) ;
#ifdef WIN32
if( selected < 0 )
re = WSAGetLastError() ; //WSAENOTSOCK
#endif

if( selected <= 0 )
continue ;
if( FD_ISSET( 0 , &readfds )) { /* 从stdin 标准输入-1的话整个程序退出 , 只在uinx/linux下起作用*/
scanf("%d", &bre) ;
if( bre == -1 ) {
printf("you enter -1 is to exit\n" ) ;
break ;
}
}
for( is = 0 ; is < sockets ; is ++ ) {
if( FD_ISSET( subSockets[is]  ,  &readfds )  ) {
readed = recv( subSockets[is]  , buf , LEN ,0  ) ;
printf("readed=%d\n" , readed ) ;
if( readed <= 0  ) { //对方已经关闭该socket,见注释1 ,在linux ,windows和cygwin下返回值一直是0 , errno 也是0
printf("error no : %d %s\n" , errno , sys_errlist[errno] ) ;
closesocket( subSockets[is] ) ;
subSockets[is] = subSockets[sockets-1] ;
sockets -- ;
}
else
printf("read from subSockets[%d] : %s\n" , is , buf ) ;
}
}
if( FD_ISSET( serverSocket1 ,  &readfds )  &&  ( sockets < MAX_CLIENTS)  ) {/*可以在readfds查到accept信号
因为客户方有连接需求时,会发送报文,所以它就变成可读状态 */
subSockets[sockets] = accept( serverSocket1 , &remoteAddr , &size_remoteAddr ) ;
sockets ++ ;
printf("connected romote address :  %d\n" , ((struct sockaddr_in*)&remoteAddr)->sin_port ) ;
}
}
if( serverSocket1 >= 0 )
closesocket( serverSocket1 ) ;
for( is = 0 ; is < sockets ; is ++ ) {
if(  subSockets[is]  >= 0  ) {
closesocket( subSockets[is] );
}
}
return 0 ;
}

/* 注1:来自网上,但测试结果和他描述不一样
利用select()检测对方Socket关闭的问题:

仍然是本地Socket有东东可读,因为对方Socket关闭时,会发一个关闭连接
通知报文,会马上被select()检测到的。关于TCP的连接(三次握手)和关
闭(二次握手)机制,敬请参考有关TCP/IP的书籍。

不知是什么原因,UNIX好象没有提供通知进程关于Socket或Pipe对方关闭的
信号,也可能是cpu所知有限。总之,当对方关闭,一执行recv()或read(),
马上回返回-1,此时全局变量errno的值是115,相应的sys_errlist[errno]
为"Connect refused"(请参考/usr/include/sys/errno.h)。所以,在上
篇的for(;;)...select()程序块中,当有东西可读时,一定要检查recv()或
read()的返回值,返回-1时要作出关断本地Socket的处理,否则select()会
一直认为有东西读,其结果曾几令cpu伤心欲断针脚。
*/


selectclient.c:

/*
selectaccept.c 对应的 client
*/

#include <stdio.h>
#include <string.h>

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/errno.h>

void Sleep ( int ms ) {
struct timespec ts ;
ts.tv_sec = ms/1000 ;
ts.tv_nsec = (ms%1000)*1000000 ;
nanosleep( &ts , 0 ) ;
}

#define closesocket close

#endif

#define LEN 1024
int main(int argc , char* argv[])
{
char buf[LEN] = "client" ;
int socket1  ;

struct sockaddr_in   remoteAddr ;
int size_remoteAddr = sizeof( remoteAddr )  ;
struct sockaddr_in sockAddr_in = {0 };

int re ;
int ii = 0 ;
int cancel ;

#ifdef WIN32
WSADATA wsd ;
re = WSAStartup( WINSOCK_VERSION , &wsd ) ;
#endif

if( argc < 2 )
return 1 ;
socket1 = socket( PF_INET , SOCK_STREAM ,  0 ) ;

if( socket1 < 0 )
printf("open socket  error\n" ) ;
printf("open socket %d\n" , socket1 ) ;

sockAddr_in.sin_family = AF_INET ;
sockAddr_in.sin_addr.s_addr = INADDR_ANY ;
sockAddr_in.sin_port = 2800 ;

re = bind( socket1 , (struct sockaddr* )&sockAddr_in , sizeof( sockAddr_in ) ) ; // can ignore this line
printf("bind :%d\n" , re ) ; // re may  < 0  bind other port if 2800 is used
remoteAddr.sin_family = AF_INET ;
remoteAddr.sin_port = 1800 ;
remoteAddr.sin_addr.s_addr = 127+(1<<24) ;//  127.0.0.1
re = connect(   socket1 , (struct sockaddr* )&remoteAddr , size_remoteAddr ) ;
printf(" connect : %d\n" , re ) ;
if( re < 0 )   {
printf("error no : %d %s\n" , errno , sys_errlist[errno] ) ;
return 1 ;
}

while ( 1 ) {
sprintf( buf , "client %s: line %d\0" , argv[1] , ii++  ) ;
printf("%s\n" , buf ) ;
re = send( socket1 , buf  , strlen( buf ) + 1  , 0  ) ;
printf("enter -1 for 	exit , other to continue\n" ) ;
scanf("%d" , &cancel ) ;
if( cancel == -1 )
break ;
//Sleep( 10000 ) ;
}
if( socket1 >=0 )
closesocket(  socket1 ) ;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐