Chinaunix首页 | 论坛 | 博客
  • 博客访问: 592042
  • 博文数量: 92
  • 博客积分: 5026
  • 博客等级: 大校
  • 技术积分: 1321
  • 用 户 组: 普通用户
  • 注册时间: 2008-02-28 11:04
文章分类

全部博文(92)

文章存档

2011年(9)

2010年(17)

2009年(12)

2008年(54)

我的朋友

分类: LINUX

2008-07-15 09:39:16

/*
   需求: adsl拨号上网,路由器上连了几台电脑,夜里睡觉之前看看有没有其他的电脑还连在局域网上,没有的话就关猫。都是限时上网惹的货
   原理:ping最简单的,但是其他电脑有可能有防火墙会屏蔽icmp消息的,所以就不行了。需要高层的tcp或者udp。现在用tcp,连接一个端口,根据返回的错误(0成功电脑开着, 111失败端口未开电脑还是开着, 113失败无法路由电脑关了)进行判断就ok了。
   关键:需要设置connect超时,电脑关了(不存在的ip)情况下,局域网可能还好会比较块的返回错误113.但是外网地址一般都要花75秒左右才能返回超时。原因我不说了
   作者:林汉杰2008-7-15 
   环境:g++
*/
 
 
 
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
 
#include "unistd.h"
#include "sys/types.h"
#include "sys/time.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#include "netinet/in.h"
#include "netdb.h"
#include "fcntl.h"
#include "errno.h"
 
int main() {
 
        struct timeval timeout;
        fd_set wset;
        char straddr[100];
        for(int i=1; i<255; i++) {
 
                int sockfd = socket(AF_INET, SOCK_STREAM, 0);
                if(sockfd == -1)perror("socket()");
 
                sprintf(straddr, "192.168.1.%d\0", i);
                fprintf(stderr, "scan %s\t", straddr);
 
                struct sockaddr_in server_addr;
                server_addr.sin_family = AF_INET;
                if(inet_pton(AF_INET, straddr, &(server_addr.sin_addr)) != 1)perror("inet_pton()");
                server_addr.sin_port = htons(22);
        /*     
                int result = connect(sockfd, (struct sockaddr*)(&server_addr), sizeof(server_addr));
                if(result == -1)perror("connect()");
                else {
                        printf("connect success %d \n", result);
                        sockfd = socket(AF_INET, SOCK_STREAM, 0);
                }
 
                sleep(2);
        */
                int flags;
                if((flags = fcntl(sockfd, F_GETFL, 0)) < 0)perror("fcntl()");
                if(fcntl(sockfd, F_SETFL, flags | O_NONBLOCK)< 0)perror("fcntl()");
                int result = connect(sockfd, (struct sockaddr*)(&server_addr), sizeof(server_addr));
                if(result == -1) {
                        if(errno == EINPROGRESS) {
        //                      do {
                                timeout.tv_sec  = 10;
                                timeout.tv_usec = 100000;
                                FD_ZERO(&wset);
                                FD_SET(sockfd, &wset);
                                int n = select(sockfd+1, 0, &wset, 0, &timeout);
                                if(n == -1 && errno!= EINTR) {
                                        perror("select() error");
                                } else if(n > 0) {
                                        int optval;
                                        int optlen = 4;
                                        if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&optval, (socklen_t*)&optlen) < 0)perror("getsockopt()");
                                        if(optval == 0) {
                                                fprintf(stderr, "success select\n");
                                        } else {
                                                fprintf(stderr, "failed select %d:%s\n", optval, strerror(optval));
                                        }
                                } else if(n == 0) {
                                        fprintf(stderr, "connect timeout\n");
                                }
        //                      } while(1);
                        } else {
                                close(sockfd);
                                perror("connect()");
                        }
                } else {
                        printf("connect() success\n");
                        close(sockfd);
                        continue;
                }
                close(sockfd);
        }
        return 0;
}
                  
 
ps:关于connect的select超时问题
select的返回值
0: 已经到了自己设置的timeout超时了
>0:一个或者多个描述符准备好读写或者有异常带外数据到来
可读情况:1.tcp接受缓冲区大于getsockopt SO_RCVLOWAT的值 2.有新的已经完成3次握手的连接放入接收队列 3.对方关闭写端读到0字节 4.非阻塞connect失败通过getsockopt获得错误 5.有错误比如rst,通过read返回错误
可写情况:1.tcp发送缓冲区数据大于sndlowat的值 2.本端主动关闭写端,write返回错误 3.非阻塞connect失败通过getsockopt获得错误 4.非阻塞connect成功,套接字可写 5.有错误rst,通过write返回错误
异常情况:收到带外数据
-1:比如被信号中断等等
 
以上所见:非阻塞connect的sockfd, 连接成功的时候sockfd只能写。但是连接失败的时候sockfd既可以读也可以写。所以总结起来,select非阻塞的connect的方案有两种:
1. 将sockfd加入rdset wrset,select返回时如果只有wrset被设置则连接成功。如果wrset rdset同时被设置则连接失败。通过getsockopt获得错误码判断具体是什么错误。
2.将sockfd加入wrset,连接成功失败都会返回。通过getsockopt获得错误码判断是否成功。
3.将sockfd加入rdset,连接成功不会返回,无效!!!
 
强烈注意:一定通过getsockopt获得错误码判断具体是什么错误。否则将会有很大的错误。我的理解,select返回-1就会设置errno,但是connect失败的时候select已经成功的返回了>0,所以就不应该设置errno了,但是又接收到了错误,所以内核就setsockopt SO_ERROR了,然后等待应用程序自己获取这个错误。
阅读(9755) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~