windows网络编程技术 一书上有详细讲解,看看就明白了

解决方案 »

  1.   

    msdn上有讲呀,很详细还有例子,你查getsockopt就可以了
      

  2.   

    有函数介绍,但没有关于:IPPROTO_IP 选项的介绍
      

  3.   

    我找到一篇中文的,希望你能用的上。
    简述:
      设置套接口的选项。  #include <winsock.h>  int PASCAL FAR setsockopt( SOCKET s, int level, int optname,
      const char FAR* optval, int optlen);  s:标识一个套接口的描述字。
      level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。
      optname:需设置的选项。
      optval:指针,指向存放选项值的缓冲区。
      optlen:optval缓冲区的长度。注释:
      setsockopt()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。
      有两种套接口的选项:一种是布尔型选项,允许或禁止一种特性;另一种是整形或结构选项。允许一个布尔型选项,则将optval指向非零整形数;禁止一个选项optval指向一个等于零的整形数。对于布尔型选项,optlen应等于sizeof(int);对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度。SO_LINGER选项用于控制下述情况的行动:套接口上有排队的待发送数据,且closesocket()调用已执行。参见closesocket()函数中关于SO_LINGER选项对closesocket()语义的影响。应用程序通过创建一个linger结构来设置相应的操作特性:
      struct linger {
    int l_onoff;
    int l_linger;
      };
      为了允许SO_LINGER,应用程序应将l_onoff设为非零,将l_linger设为零或需要的超时值(以秒为单位),然后调用setsockopt()。为了允许SO_DONTLINGER(亦即禁止SO_LINGER),l_onoff应设为零,然后调用setsockopt()。
      缺省条件下,一个套接口不能与一个已在使用中的本地地址捆绑(参见bind())。但有时会需要“重用”地址。因为每一个连接都由本地地址和远端地址的组合唯一确定,所以只要远端地址不同,两个套接口与一个地址捆绑并无大碍。为了通知WINDOWS套接口实现不要因为一个地址已被一个套接口使用就不让它与另一个套接口捆绑,应用程序可在bind()调用前先设置SO_REUSEADDR选项。请注意仅在bind()调用时该选项才被解释;故此无需(但也无害)将一个不会共用地址的套接口设置该选项,或者在bind()对这个或其他套接口无影响情况下设置或清除这一选项。
      一个应用程序可以通过打开SO_KEEPALIVE选项,使得WINDOWS套接口实现在TCP连接情况下允许使用“保持活动”包。一个WINDOWS套接口实现并不是必需支持“保持活动”,但是如果支持的话,具体的语义将与实现有关,应遵守RFC1122“Internet主机要求-通讯层”中第4.2.3.6节的规范。如果有关连接由于“保持活动”而失效,则进行中的任何对该套接口的调用都将以WSAENETRESET错误返回,后续的任何调用将以WSAENOTCONN错误返回。
      TCP_NODELAY选项禁止Nagle算法。Nagle算法通过将未确认的数据存入缓冲区直到蓄足一个包一起发送的方法,来减少主机发送的零碎小数据包的数目。但对于某些应用来说,这种算法将降低系统性能。所以TCP_NODELAY可用来将此算法关闭。应用程序编写者只有在确切了解它的效果并确实需要的情况下,才设置TCP_NODELAY选项,因为设置后对网络性能有明显的负面影响。TCP_NODELAY是唯一使用IPPROTO_TCP层的选项,其他所有选项都使用SOL_SOCKET层。
      如果设置了SO_DEBUG选项,WINDOWS套接口供应商被鼓励(但不是必需)提供输出相应的调试信息。但产生调试信息的机制以及调试信息的形式已超出本规范的讨论范围。
      setsockopt()支持下列选项。其中“类型”表明optval所指数据的类型。
    选项        类型  意义
    SO_BROADCAST    BOOL    允许套接口传送广播信息。
    SO_DEBUG    BOOL    记录调试信息。
    SO_DONTLINER    BOOL    不要因为数据未发送就阻塞关闭操作。设置本选项相当于将SO_LINGER的l_onoff元素置为零。
    SO_DONTROUTE    BOOL    禁止选径;直接传送。
    SO_KEEPALIVE    BOOL    发送“保持活动”包。
    SO_LINGER   struct linger FAR*  如关闭时有未发送数据,则逗留。
    SO_OOBINLINE    BOOL    在常规数据流中接收带外数据。
    SO_RCVBUF   int 为接收确定缓冲区大小。
    SO_REUSEADDR    BOOL    允许套接口和一个已在使用中的地址捆绑(参见bind())。
    SO_SNDBUF   int 指定发送缓冲区大小。
    TCP_NODELAY BOOL    禁止发送合并的Nagle算法。  setsockopt()不支持的BSD选项有:
    选项名      类型    意义
    SO_ACCEPTCONN   BOOL    套接口在监听。
    SO_ERROR    int 获取错误状态并清除。
    SO_RCVLOWAT int 接收低级水印。
    SO_RCVTIMEO int 接收超时。
    SO_SNDLOWAT int 发送低级水印。
    SO_SNDTIMEO int 发送超时。
    SO_TYPE     int 套接口类型。
    IP_OPTIONS      在IP头中设置选项。返回值:
      若无错误发生,setsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。错误代码:
      WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
      WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
      WSAEFAULT:optval不是进程地址空间中的一个有效部分。
      WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
      WSAEINVAL:level值非法,或optval中的信息非法。
      WSAENETRESET:当SO_KEEPALIVE设置后连接超时。
      WSAENOPROTOOPT:未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM类型的套接口不支持SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE选项。
      WSAENOTCONN:当设置SO_KEEPALIVE后连接被复位。
      WSAENOTSOCK:描述字不是一个套接口。
      

  4.   

    谢谢kingzai!这个我看了,我想知道关于它里面有没有的,例如它说:setsockopt()不支持的BSD选项有:
    选项名      类型    意义
    SO_ACCEPTCONN  BOOL    套接口在监听。
    SO_ERROR    int 获取错误状态并清除。
    SO_RCVLOWAT int 接收低级水印。
    SO_RCVTIMEO int 接收超时。
    SO_SNDLOWAT int 发送低级水印。
    SO_SNDTIMEO int 发送超时。
    SO_TYPE    int 套接口类型。
    IP_OPTIONS      在IP头中设置选项。
    而我在看一个ping程序时却看到了,用到了IP_OPTONS
    部分代码如下:
    void CPing::Ping(int timeout)
    {   
     m_hSocket = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0,
                               WSA_FLAG_OVERLAPPED);    if (m_hSocket == INVALID_SOCKET) 
        {
            printf("WSASocket() failed: %d\n", WSAGetLastError());
            return ;
        }    if (m_bRecordRout)
        {
            // Setup the IP option header to go out on every ICMP packet
            //
            ZeroMemory(&m_ipopt, sizeof(m_ipopt));
            m_ipopt.code = IP_RECORD_ROUTE; // Record route option
            m_ipopt.ptr  = 4;               // Point to the first addr offset
            m_ipopt.len  = 39;              // Length of option header
      
            int ret = setsockopt(m_hSocket, IPPROTO_IP, IP_OPTIONS, 
                (char *)&m_ipopt, sizeof(m_ipopt));
            if (ret == SOCKET_ERROR)
            {
                printf("setsockopt(IP_OPTIONS) failed: %d\n", 
                    WSAGetLastError());
            }
        }
        // Set the send/recv timeout values
        //
        int bread = setsockopt(m_hSocket, SOL_SOCKET, SO_RCVTIMEO, 
                    (char*)&timeout, sizeof(timeout));
        if(bread == SOCKET_ERROR) 
        {
            printf("setsockopt(SO_RCVTIMEO) failed: %d\n", 
                WSAGetLastError());
            return ;
        }
        timeout = 1000;
        bread = setsockopt(m_hSocket, SOL_SOCKET, SO_SNDTIMEO, 
                    (char*)&timeout, sizeof(timeout));
        if (bread == SOCKET_ERROR) 
        {
            printf("setsockopt(SO_SNDTIMEO) failed: %d\n", 
                WSAGetLastError());
            return ;
        }
        memset(&m_addrDest, 0, sizeof(m_addrDest));
        //
        // Resolve the endpoint's name if necessary
        //
        m_addrDest.sin_family = AF_INET;
        if ((m_addrDest.sin_addr.s_addr = inet_addr(lpdest)) == INADDR_NONE)
        {   
     struct hostent *hp = NULL;        if ((hp = gethostbyname(lpdest)) != NULL)
            {
                memcpy(&(m_addrDest.sin_addr), hp->h_addr, hp->h_length);
                m_addrDest.sin_family = hp->h_addrtype;
                printf("m_addrDest.sin_addr = %s\n", inet_ntoa(m_addrDest.sin_addr));
            }
            else
            {
                printf("gethostbyname() failed: %d\n", 
                    WSAGetLastError());
                return ;
            }
        }            // 
        // Create the ICMP packet
        //       
        datasize += sizeof(IcmpHeader);      icmp_data =(char*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                      MAX_PACKET);
        recvbuf =(char*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                      MAX_PACKET);
        if (!icmp_data) 
        {
            printf("HeapAlloc() failed: %d\n", GetLastError());
            return ;
        }
        memset(icmp_data,0,MAX_PACKET);
        FillICMPData(icmp_data,datasize);
        //
        // Start sending/receiving ICMP packets
        //
        while(1) 
        {
            static int nCount = 0;
            int        bwrote;
                    
            if (nCount++ == 4) 
                break;
                    
            ((IcmpHeader*)icmp_data)->i_cksum = 0;
            ((IcmpHeader*)icmp_data)->timestamp = GetTickCount();
            ((IcmpHeader*)icmp_data)->i_seq = seq_no++;
            ((IcmpHeader*)icmp_data)->i_cksum = 
                checksum((USHORT*)icmp_data, datasize);        bwrote = sendto(m_hSocket, icmp_data, datasize, 0, 
                         (struct sockaddr*)&m_addrDest, sizeof(m_addrDest));
            if (bwrote == SOCKET_ERROR)
            {
                if (WSAGetLastError() == WSAETIMEDOUT) 
                {
                    printf("timed out\n");
                    continue;
                }
                printf("sendto() failed: %d\n", WSAGetLastError());
                return ;
            }
            if (bwrote < datasize) 
            {
                printf("Wrote %d bytes\n", bwrote);
            } int fromlen = sizeof(m_addrFrom);
            bread = recvfrom(m_hSocket, recvbuf, MAX_PACKET, 0, 
                        (struct sockaddr*)&m_addrFrom, &fromlen);
            if (bread == SOCKET_ERROR)
            {
                if (WSAGetLastError() == WSAETIMEDOUT) 
                {
                    printf("timed out\n");
                    continue;
                }
                printf("recvfrom() failed: %d\n", WSAGetLastError());
                return ;
            }
            DecodeICMPHeader(recvbuf, bread, &m_addrFrom);
            
        }
    }若谁需要完全代码的留下email:我一定发到
      

  5.   

    小弟试了这个经典的ping程序,基本上没有问题了。但是还有一个疑点:书上说IP选项头记录的是这个ICMP包经过每个路由器时此路由器的地址,但是打印出来的不对--正好差了一位,如192.168.0.1的路由器会被记作是168.0.1.192。有哪位大侠出手相助??
      

  6.   

    <windows网络编程技术>第9章。