http://www.cnblogs.com/GameDeveloper/p/3406565.html 经典
http://blog.csdn.net/shellching/article/details/7663086
http://blog.csdn.net/shellching/article/details/7663086
步骤1: 设置非阻塞,启动连接
实现非阻塞 connect ,首先把 sockfd 设置成非阻塞的。这样调用
connect 可以立刻返回,根据返回值和 errno 处理三种情况:
(1) 如果返回 0,表示 connect 成功。
(2) 如果返回值小于 0, errno 为 EINPROGRESS, 表示连接
建立已经启动但是尚未完成。这是期望的结果,不是真正的错误。
(3) 如果返回值小于0,errno 不是 EINPROGRESS,则连接出错了。
步骤2:判断可读和可写
然后把 sockfd 加入 select 的读写监听集合,通过 select 判断 sockfd
是否可写,处理三种情况:
(1) 如果连接建立好了,对方没有数据到达,那么 sockfd 是可写的
(2) 如果在 select 之前,连接就建立好了,而且对方的数据已到达,
那么 sockfd 是可读和可写的。
(3) 如果连接发生错误,sockfd 也是可读和可写的。
判断 connect 是否成功,就得区别 (2) 和 (3),这两种情况下 sockfd 都是
可读和可写的,区分的方法是,调用 getsockopt 检查是否出错。
步骤3:使用 getsockopt 函数检查错误
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)
在 sockfd 都是可读和可写的情况下,我们使用 getsockopt 来检查连接
是否出错。但这里有一个可移植性的问题。
如果发生错误,getsockopt 源自 Berkeley 的实现将在变量 error 中
返回错误,getsockopt 本身返回0;然而 Solaris 却让 getsockopt 返回 -1,
并把错误保存在 errno 变量中。所以在判断是否有错误的时候,要处理
这两种情况。
代码:
int conn_nonb(int sockfd, const struct sockaddr_in *saptr, socklen_t salen, int nsec)
{
int flags, n, error, code;
socklen_t len;
fd_set wset;
struct timeval tval;
flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
error = 0;
if ((n == connect(sockfd, saptr, salen)) == 0) {
goto done;
} else if (n < 0 && errno != EINPROGRESS){
//返回EINPROGRESS不一定代表对方端口开放了
return (-1);
}
/* Do whatever we want while the connect is taking place */
FD_ZERO(&wset);
FD_SET(sockfd, &wset);
tval.tv_sec = nsec;
tval.tv_usec = 0;
if ((n = select(sockfd+1, NULL, &wset,
NULL, nsec ? &tval : NULL)) == 0) {
close(sockfd); /* timeout */
errno = ETIMEDOUT;
return (-1);
}
if (FD_ISSET(sockfd, &wset)) {
len = sizeof(error);
code = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
/* 如果发生错误,Solaris实现的getsockopt返回-1,
* 把pending error设置给errno. Berkeley实现的
* getsockopt返回0, pending error返回给error.
* 我们需要处理这两种情况 */
if (code < 0 || error) {
close(sockfd);
if (error)
errno = error;
return (-1);
}
} else {
fprintf(stderr, "select error: sockfd not set");
exit(0);
}
done:
fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
return (0);
}
linux c 非阻塞connect版端口扫描程序
之前有个connect的端口扫描程序,那个东西只适合扫localhost,一扫其他机器就×××××,反正是不能扫内网其他机器,更不要说外网主机了。这是由于connect的返回超时设置问题,《UNP》第一卷 第三版 P85-P86指出伯克利系统的超时时限为75s,Solaris9超时时限为4分钟,所以一般认为是75s到几分钟不等,而我测试的时限为189s(Linux Kernel 2.6.24),SYN6次重传~~
好了,这就是为什么要用非阻塞connect了。
一.非阻塞connect的一般步骤:
0. sockfd=socket();
1. fcntl设置sockfd为非阻塞;
2. connect();
3. select();
4. FD_ISSET();
上面这个步骤也就只能执行一次,一个connect.
二.所以下面是执行多个connect的步骤:
for(;;)
{
0. sockfd=socket();
1. fcntl设置sockfd为非阻塞;
2. connect();
}
3. select();
4. FD_ISSET();
NOTE:
下面的程序在select()之前设置了sleep(5),也可设置其他时间,这个似乎比较重要,因为这样让select之前返回所以应该返回的connect.(重要性有待考证,欢迎留言哦!!!)
三.代码如下:
|
NOTE:
1. MINPORT其实不是端口号,而是最小端口号-1,方便循环而已~~
2. select的第一个参数最大为1024,所以设置的MAXPORT 1000检测1000个端口
四.问题???
1. 有时还是有问题,用nmap能扫到的端口用这个扫不到。确实很无解,只能先放一放了~
2. 扫内网有一台机器时21端口始终扫不出来,nmap可以,难道是拒绝链接?OR?继续无解
3. UBUNTU中文论坛的服务器真牛B,开了十多个端口,不过用这个扫有一个扫不到~
4.问题就是还需要实现扫更多的端口,暂时不忙这个,似乎不难~有兴趣的朋友可以做一下然后给我说哦
PS: 31号就百度之星了~虽然没有任何希望,还是去见识下,呵呵~