我编写了一个程序,使用socket连接服务器和客户机,在局域网中可以正常使用,但是服务器放在internet上后发现服务器与客户机之间的socket通讯被禁止了,connection()函数连接不上,错误结果显示“连接超时”。确定是通讯链路中间的防火墙或者代理服务器禁止直接连接的原因,前几天看了《程序员》no.6,介绍北京威速公司时提到过他们开发的视频会议系统使用nip协议解决了这个问题,故此想请教大家。有谁知道这方面的知识,请给我发email:[email protected]。谢谢!!!

解决方案 »

  1.   

    穿透代理服务器编程
    田进恩 [email protected]
        关键词:代理服务器、Socks4、Socks5、Http代理  
        在网络程序设计过程中,我们经常要与各种类型的代理服务器打交道,比如在企业内部网通过代理去访问Internet网上的服务器等等,一般代理服务器支持几种常见的代理协议标准,如Socks4,Socks5,Http代理,其中Socks5需要用户验证,代理相对复杂。我在查阅RFC文档和相关资料后,特总结一些TCP协议穿透代理服务器的程序片断,希望对大家有所帮助。//使用到的结构
    struct sock4req1
    {
    char VN;
    char CD;
    unsigned short Port;
    unsigned long IPAddr;
    char other[1];
    };struct sock4ans1
    {
    char VN;
    char CD;
    };struct sock5req1
    {
    char Ver;
    char nMethods;
    char Methods[255];
    };struct sock5ans1
    {
    char Ver;
    char Method;
    };struct sock5req2
    {
    char Ver;
    char Cmd;
    char Rsv;
    char Atyp;
    char other[1];
    };struct sock5ans2
    {
    char Ver;
    char Rep;
    char Rsv;
    char Atyp;
    char other[1];
    };struct authreq
    {
    char Ver;
    char Ulen;
    char Name[255];
    char PLen;
    char Pass[255];
    };struct authans
    {
    char Ver;
    char Status;
    };//通过Socks4方式代理
    if( !ClientSock.Connect( g_ProxyInfo.m_strProxyIP,g_ProxyInfo.m_nProxyPort) )
    {
    m_sError = _T("不能连接到代理服务器!");
    ClientSock.Close();
    return FALSE;
    }
    char buff[100];
    memset(buff,0,100);
    struct sock4req1 *m_proxyreq;
    m_proxyreq = (struct sock4req1 *)buff;
    m_proxyreq->VN = 4;
    m_proxyreq->CD = 1;
    m_proxyreq->Port = ntohs(GetPort());
    m_proxyreq->IPAddr = inet_addr(GetServerHostName());
    ClientSock.Send(buff,9);
    struct sock4ans1 *m_proxyans;
    m_proxyans = (struct sock4ans1 *)buff;
    memset(buff,0,100);
    ClientSock.Receive(buff,100);
    if(m_proxyans->VN != 0 || m_proxyans->CD != 90)
    {
    m_sError = _T("通过代理连接主站不成功!");
    ClientSock.Close();
    return FALSE;
    }
      //通过Socks5方式代理
    if( !ClientSock.Connect( g_ProxyInfo.m_strProxyIP,g_ProxyInfo.m_nProxyPort) )
    {
    m_sError = _T("不能连接到代理服务器!");
    ClientSock.Close();
    return FALSE;
    }
    char buff[600];
    struct sock5req1 *m_proxyreq1;
    m_proxyreq1 = (struct sock5req1 *)buff;
    m_proxyreq1->Ver = 5;
    m_proxyreq1->nMethods = 2;
    m_proxyreq1->Methods[0] = 0;
    m_proxyreq1->Methods[1] = 2;
    ClientSock.Send(buff,4);
    struct sock5ans1 *m_proxyans1;
    m_proxyans1 = (struct sock5ans1 *)buff;
    memset(buff,0,600);
    ClientSock.Receive(buff,600);
    if(m_proxyans1->Ver != 5 || (m_proxyans1->Method!=0 && m_proxyans1->Method!=2))
    {
    m_sError = _T("通过代理连接主站不成功!");
    ClientSock.Close();
    return FALSE;
    }
    if(m_proxyans1->Method == 2)
    {
    int nUserLen = strlen(g_ProxyInfo.m_strProxyUser);
    int nPassLen = strlen(g_ProxyInfo.m_strProxyPass);
    struct authreq *m_authreq;
    m_authreq = (struct authreq *)buff;
    m_authreq->Ver = 1;
    m_authreq->Ulen = nUserLen;
    strcpy(m_authreq->Name,g_ProxyInfo.m_strProxyUser);
    m_authreq->PLen = nPassLen;
    strcpy(m_authreq->Pass,g_ProxyInfo.m_strProxyPass);
    ClientSock.Send(buff,513);
    struct authans *m_authans;
    m_authans = (struct authans *)buff;
    memset(buff,0,600);
    ClientSock.Receive(buff,600);
    if(m_authans->Ver != 1 || m_authans->Status != 0)
    {
    m_sError = _T("代理服务器用户验证不成功!");
    ClientSock.Close();
    return FALSE;
    }
    }
    struct sock5req2 *m_proxyreq2;
    m_proxyreq2 = (struct sock5req2 *)buff;
    m_proxyreq2->Ver = 5;
    m_proxyreq2->Cmd = 1;
    m_proxyreq2->Rsv = 0;
    m_proxyreq2->Atyp = 1;
    unsigned long tmpLong = inet_addr(GetServerHostName());
    unsigned short port = ntohs(GetPort());
    memcpy(m_proxyreq2->other,&tmpLong,4);
    memcpy(m_proxyreq2->other+4,&port,2);
    ClientSock.Send(buff,sizeof(struct sock5req2)+5);
    struct sock5ans2 *m_proxyans2;
    memset(buff,0,600);
    m_proxyans2 = (struct sock5ans2 *)buff;
    ClientSock.Receive(buff,600);
    if(m_proxyans2->Ver != 5 || m_proxyans2->Rep != 0)
    {
    m_sError = _T("通过代理连接主站不成功!");
    ClientSock.Close();
    return FALSE;
    }
      

  2.   

    //通过HTTP方式代理
    if( !ClientSock.Connect( g_ProxyInfo.m_strProxyIP,g_ProxyInfo.m_nProxyPort) )
    {
    m_sError = _T("不能连接到代理服务器!");
    ClientSock.Close();
    return FALSE;
    }
    char buff[600];
    sprintf( buff, "%s%s:%d%s","CONNECT ",GetServerHostName(),GetPort()," HTTP/1.1\r\nUser-Agent: MyApp/0.1\r\n\r\n");
    ClientSock.Send(buff,strlen(buff)); //发送请求
    memset(buff,0,600);
    ClientSock.Receive(buff,600);
    if(strstr(buff, "HTTP/1.0 200 Connection established") == NULL) //连接不成功
    {
    m_sError = _T("通过代理连接主站不成功!");
    ClientSock.Close();
    return FALSE;
    }
        我们一般先与代理服务器连通,然后向代理服务器发送代理验证的用户名和密码(如果需要,如Socks5代理),验证成功后,再向代理服务器发送需要连接的目的地址和端口。以上代码仅用于TCP连接,如果在内部网侦听或通过UDP协议发送信息,可查阅RFC1829等文档资料。// 与代理服务器建立CONNECT;    
        int retErr = connect(m_hSocketpop,(SOCKADDR*)&ToAddr,sizeof(ToAddr));
        if(retErr == SOCKET_ERROR)
        {
            printf("connect error");
            return -1;
        }
        char buf[100];
        // 让PROXY选择认证方法
        buf[0] = 0x05;
        buf[1] = 0x01;
        buf[2] = 0x00; // 无需认证
        send(m_hSocketpop,(const far char*)buf,3,0);
        recv(m_hSocketpop,buf,10,0);
        if(buf[1] != 0x00)
            printf("Need to authenticate!\n");
        // 向PROXY服务器发送CONNECT 请求,
        WORD port = 80;
        buf[0] = 0x05;
        buf[1] = 0x01;    // connection request.
        buf[2] = 0x00;    // reserved!
        buf[3] = 0x01;    // address type:Ip V4 ;
        buf[4] = 0xd3; 
        buf[5] = 0x64; 
        buf[6] = 0x1f; 
        buf[7] = 0x61;
        buf[8] = port/256;            //0x80;
        buf[9] = (BYTE)port%256;    //0x00;
        port += 1;
        send(m_hSocketpop,(const far char*)buf,10,0);
        // 接收PROXY服务器返回的REPLY
        recv(m_hSocketpop,buf,20,0);
        if(buf[1] != 0) // 只有当第二个字节为0时,才表示成功。
        {
            printf("Cannot connect to the remote server!\n");
            return -1;
        }
        // 用PROXY服务器返回的REPLY中的地址与端口号来CONNECT 目的主机
        SOCKADDR_IN SockAddr;
        SOCKET prxSock;
        prxSock = socket(AF_INET, SOCK_STREAM ,0);  // SOCK_DGRAM
        port = ((WORD)buf[8])*256 + (WORD)buf[9]; // 返回的端口号在buf[8] 和 buf[9] 中
        memset(&SockAddr,0,sizeof(SockAddr));
        SockAddr.sin_family = AF_INET;
        SockAddr.sin_port = port;
        SockAddr.sin_addr.S_un.S_un_b.s_b1 = buf[4]; 
        SockAddr.sin_addr.S_un.S_un_b.s_b2 = buf[5];
        SockAddr.sin_addr.S_un.S_un_b.s_b3 = buf[6];
        SockAddr.sin_addr.S_un.S_un_b.s_b4 = buf[7];
        retErr = connect(prxSock,(SOCKADDR*)&SockAddr,sizeof(SockAddr));
        if(retErr == SOCKET_ERROR)
            printf("error"); 
        /*连接已经建立好了,下面你就可以用prxSock用send发送数据了。*/
        char *pInfo = (char *)malloc(100);
        strcpy(pInfo,"Only a test");
        send(prxSock,pInfo,100,0);