您的位置:首页 > 其它

将阻塞模式的socket连结变为非阻塞模式

2009-09-28 15:49 459 查看
发信人: landyhorse (马儿), 信区: NetPRG
标 题: ---将阻塞模式的socket连结变为非阻塞模式---
发信站: BBS 水木清华站 (Fri Dec 7 15:45:40 2001)

前两天在linux下作了这方面的应用,
当时也是找了好半天才搞出来,
看到有人问,就讲讲基本原理,和一段代码,免得大家再走弯路
该方法最适合线程使用

1。原理(来自linuxforum)

创建socket后,先把connect()用的socket的模式保存,
再将其设成nonblock模式(使用fcntl函数),
然后进行connect()。
connect()会return两种情况,一是success,
那就restore socket的以前mode, again use fcntl(), 然后
就算connect好了,二则是-1, 那么就要看errno, 如果是
EINPROGRESS,则就用把socket加到FD_SET里,然后用select()
select socket for writting.
因为select()可以用timeout, 然后如果select() return的话,
如果return 0, 那就是timeout, -1就要看你根据errno如何处理了,
如果是>0, 则用FD_ISSET()看是否是socket connect好了,这时
还有个condition是connection refused, 对方送了你一个RST,
你需要用getsockopt(sockfd, SOL_SOCKET,SO_ERROR,....)去
查一下,如果没有问题那就是connect success了

2.例程代码(来自白云黄鹤)

/************************************************************************
* tcpconnect.c, by digger, 带超时限制的非阻塞式connect *
************************************************************************/

# include <stdio.h>
# include <string.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/time.h>
# include <fcntl.h>
# include <unistd.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <errno.h>

/************************************************************************
* 函数: tcpconnect( ) *
* 输入: char * server_addr, u_short server_port, int limit_seconds *
* 成功: 返回创建并连接服务端成功的socket文件号, 失败: 返回-1 *
************************************************************************/

int tcpconnect( char * server_addr, /* 服务端地址, 域名或IP地址 */
u_short server_port, /* 服务端口 */
int limit_seconds /* 超时限制, 单位为秒 */
)
{
struct hostent * hp; /* host entry pointer */
struct sockaddr_in server; /* server address structure */
int sock; /* socket to create */
fd_set fdW; /* fileds to be select */
struct timeval timeout; /* timeout parameter */
int fflag; /* file flag for fcntl */
int errcode; /* error code for getsockopt */
int errlen; /* length of errcode */

/* fill in server address structure with ip address and port */

bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(server_port);
if ((hp = gethostbyname(server_addr)) == NULL) {
if ((server.sin_addr.s_addr = inet_addr(server_addr)) == -1) {
fprintf(stderr, "%s: unknown host/n", server_addr);
exit(1);
}
} else {
bcopy(hp->h_addr_list[0], &server.sin_addr, hp->h_length);
}

/* create client socket and connect it to server */

if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
return(-1);
}

/* get original sock flag */

if ((fflag = fcntl(sock, F_GETFL, 0)) < 0) {
perror("fcntl get file flag");
exit(-1);
}

/* set O_NDELAY flag to sock */

if (fcntl(sock, F_SETFL, fflag|O_NDELAY) < 0) {
perror("fcntl set file flag O_NDELAY");
exit(-1);
}

/* connect to server using nonblock mode */

connect(sock, (struct sockaddr *)&server, sizeof(server));

/* set timeout structure */

timeout.tv_sec = limit_seconds;
timeout.tv_usec = 0;

FD_ZERO(&fdW);
FD_SET(sock, &fdW);

CNNT_AGAIN:

switch(select(sock + 1, NULL, &fdW, NULL, &timeout)) {

case -1:
if (errno == EINTR) goto CNNT_AGAIN; /* call interrupted */
close(sock); /* select error */
exit(-1);

case 0:
close(sock);
return(-1);

default:
if (FD_ISSET(sock, &fdW)) { /* check if socket writable */
errlen = sizeof(errcode);
getsockopt(sock, SOL_SOCKET, SO_ERROR,
(char *)&errcode,&errlen);
if(errcode == 0) { /* check if connect right */
/* reset original sock flag */
if (fcntl(sock, F_SETFL, fflag) < 0) {
perror("fcntl reset file flag");
exit(-1);
}
return(sock);
} else {
close(sock);
return(-1);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: