以下是我写的socket服务器端的代码,使用select接受连接,
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D_LINE(_STR_) printf("f:%s(l:%d)-fn:%s():%s/n",__FILE__,__LINE__,__FUNCTION__,_STR_);
#define SERVER_PORT 1025
#define LENGTH_OF_LISTEN_QUEUE 100
#define BUFFER_SIZE 1024
#define MAX_RECV 10
#define SELECT_WAIT_SEC 0; // in second
#define SELECT_WAIT_USEC 3000; // in usecond
int main()
{
int i;
struct sockaddr_in server_addr;
/*================ for felect ===================*/
int ret = -1;
fd_set fdsr;
struct timeval tv;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
int sockfd = socket(AF_INET, SOCK_STREAM,0);
if(sockfd < 0)
{
printf("Create socket failed/n");
exit(-1);
}
printf("server sockfd is %d/n", sockfd);
if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)))
{
printf("server bind port: %d failed/n", SERVER_PORT);
exit(-1);
}
if(listen(sockfd, LENGTH_OF_LISTEN_QUEUE))
{
printf("server listen failed/n");
}
printf("Start to listen/n");
while(1)
{
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
printf("before Server accept/n");
int new_server_socket = accept(sockfd, (struct sockaddr*)&client_addr,&length);
if(new_server_socket < 0)
{
printf("Server accept failed/n");
break;
}
printf("Server accept success/n");
char buffer[BUFFER_SIZE];
bzero(buffer, BUFFER_SIZE);
strcpy(buffer, "Hello from Platform");
strcat(buffer, "/n");
if((ret = send(new_server_socket, buffer, BUFFER_SIZE, 0)) < 0)
{
printf("send error at %d time/n", i);
return -1;
}
printf("send success/n");
usleep(3000);
// sleep(1);
// send(new_server_socket, buffer, BUFFER_SIZE, 0);
bzero(buffer, BUFFER_SIZE);
/* for(i = 0; i < MAX_RECV; i++)
{
length = recv(new_server_socket, buffer, BUFFER_SIZE, 0);
if(length < 0)
{
printf("server recieve data failed/n");
exit(-1);
}
printf("%s/n", buffer);
}
*/ while(1)
{
FD_ZERO(&fdsr);
FD_SET(new_server_socket, &fdsr);
tv.tv_sec = SELECT_WAIT_SEC;
tv.tv_usec = SELECT_WAIT_USEC;
ret = select(new_server_socket + 1, &fdsr, NULL, NULL, &tv);
D_LINE("select/n")
if(ret < 0)
{
D_LINE("select error")
return -1;
}
else if(ret == 0)
{
D_LINE("select timeout")
break;
}
if(FD_ISSET(new_server_socket, &fdsr))
{//Note! Remainber to judge here! or select think there is always have data to read
ret= recv(new_server_socket, buffer, BUFFER_SIZE, 0);
if(ret == -1)
{
break;
}
printf("%s/n", buffer);
}
}
close(new_server_socket);
}
close(sockfd);
return 0;
}
以下内容摘自网上~
讨论关于利用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伤心欲断针脚。不信你可以试试:不检
查recv()返回结果,且将收到的东东(实际没收到)写至标准输出...
在有名管道的编程中也有类似问题出现。具体处理详见拙作:发布一个有用
的Socket客户方原码。
至于主动写Socket时对方突然关闭的处理则可以简单地捕捉信号SIGPIPE并作
出相应关断本地Socket等等的处理。SIGPIPE的解释是:写入无读者方的管道。
在此不作赘述,请详man signal。
以上是cpu在作tcp/ip数据传输实验积累的经验,若有错漏,请狂炮击之。
唉,昨天在hacker区被一帮孙子轰得差点儿没短路。ren cpu(奔腾的心) z80
补充关于select在异步(非阻塞)connect中的应用,刚开始搞socket编程的时候
我一直都用阻塞式的connect,非阻塞connect的问题是由于当时搞proxy scan
而提出的呵呵
通过在网上与网友们的交流及查找相关FAQ,总算知道了怎么解决这一问题.同样
用select可以很好地解决这一问题.大致过程是这样的:
1.将打开的socket设为非阻塞的,可以用fcntl(socket, F_SETFL, O_NDELAY)完
成(有的系统用FNEDLAY也可).
2.发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧
在进行还没有完成.
3.将打开的socket设进被监视的可写(注意不是可读)文件集合用select进行监视,
如果可写,用
getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int));
来得到error的值,如果为零,则connect成功.
在许多unix版本的proxyscan程序你都可以看到类似的过程,另外在solaris精华
区->编程技巧中有一个通用的带超时参数的connect模块.