zoukankan      html  css  js  c++  java
  • 网络编程之Winsock2

    1、在Windows操作系统环境下,使用Windows Sockets API进行网络程序开发时,需要调用Windows操作系统的Windows Sockets动态库 包含Sockets头文件 导入相应库文件;下面是相应的导入

    具体用代码实现是这样的:

    这是用1.1版本

    #include <WINSOCK.h>
    
    #pragma comment(lib, "wsock32.lib");

    2.2版本

    #include <WINSOCK2.h>
    
    #pragma comment(lib, "WS2_32.lib");

    也可在项目属性当中设置库文件,根据下面的步骤在附加依赖项里面写入上面库文件的名称

    配置属性-》连接器-》输入-》附加依赖项

    2、下面是这个Winsock程序的起始与结束

    3、具体函数使用

    1)int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData );

    作用:完成WinSock的初始化任务,协商WinSock的版本支持,并分配必要的资源。

    参数:

    wVersionRequested参数用于指定准备加载的Winsock库的版本。高位字节指定所需要的Winsock库的副版本,而低位字节则是主版本。可用MAKEWORD(x,y)(其中,x是高位字节,y是低位字节)方便地获得wVersionRequested的正确值

    lpWSAData参数是指向WSADATA结构的指针,WSAStartup用其加载的库版本有关的信息填在这个结构中。即返回Winsock的实现信息。

    返回:

    如果WinSock.dll或底层网络子系统没有被正确初始化或没有被找到,WSAStartup将返回WSASYSNOTREADY。

    调用成功WSAStartup将返回0。

    此函数允许你的应用程序协商使用某种版本的WinSock规范,如果请求的版本等于或高于DLL所支持的最低版本,WSAData的wVersion成员中将包含你的应用程序应该使用的版本,它是DLL所支持的最高版本与请求版本中较小的那个。反之,如果请求的版本低于DLL所支持的最低版本,WSAStartup将返回WSAVERNOTSUPPORTED。

    WSADATA的结构:

    typedef struct WSAData { 
      WORD wVersion; 
      WORD wHighVersion; 
      char szDescription[WSADESCRIPTION_LEN+1]; 
      char szSystemStatus[WSASYS_STATUS_LEN+1]; 
      unsigned short iMaxSockets; 
      unsigned short iMaxUdpDg; 
      char FAR * lpVendorInfo; 
    } WSADATA, *LPWSADATA;

    各个成员说明:

    注意:一般不要使用下面这两个字段:

    iMaxSockets和iMaxUdpDg,它们是假定同时最多可打开多少套接字和数据报的最大长度。然而,要知道数据报的最大长度应该通过WSAEnumProtocols来查询协议信息。同时最多可打开套接字的数目不是固定的,很大程度上和可用物理内存的多少有关。

    2)int WSACleanup() ;

    作用:当不需WinSock DLL的服务,释放DLL所使用的资源。

    参数:无

    返回:调用成功返回0

    对应于每一次WSAStartup()调用必须有一个WSACleanup()调用。

    3)SOCKET socket( int af, int type, int protocol );

    作用:创建不同类型的套接字用来通信

    参数:

    af:指定地址族,对于TCP/IPv4协议的套接字,它只能是AF_INET(也可写成PF_INET)

    type:指定Socket类型,对于1.1版本的Socket,它只支持两种类型的套接字,SOCK_STREAM指定产生流式套接字,SOCK_DGRAM产生数据报套接字

    protocol:指定与特定的地址家族相关的协议,如果指定为0,那么它就会根据地址格式和套接字类别,自动选择一个合适的协议。

    返回:

    如果函数调用成功,它将返回一个新的SOCKET数据类型的套接字描述符。

    如果调用失败,这个函数就会返回一个INVALID_SOCKET,错误信息可以通过WSAGetLastError函数返回。

    下面是相关参数的对应说明:

    4)WSAGetLastError();

    作用:调用任何一个WinSock函数之后可用WSAGetLastError函数来获得详细的错误代码 int WSAGetLastError (void);

    参数:无

    返回值 详细的错误代码(可以通过相应的代码查到相应的错误)

    5)int bind( SOCKET s, const struct sockaddr FAR *name, int namelen );

    作用:对创建的套接字绑定一个ip,一般用在服务器端

    参数:

    s:指定要绑定的套接字

    name:指定了该套接字的本地地址信息,是指向sockaddr结构的指针变量,由于该地址结构是为所有的地址家族准备的,这个结构可能(通常会)随所使用的网络协议不同而不同,所以,要用

    namelen:指定该地址结构的长度。

    返回:

    如果这个函数调用成功,它将返回0。

    如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

    6)地址结构体解析:

     struct sockaddr {
          u_short sa_family; 
          char sa_data[14]; 
         }; 

    sa_family指定该地址家族,在这里IPv4必须设为AF_INET。

    sa_data仅仅是表示要求一块内存分配区,起到占位的作用,该区域中指定与协议相关的具体地址信息。由于实际要求的只是内存区,所以对于不同的协议家族,用不同的结构来替换sockaddr。在IPv4中,我们用sockaddr_in结构替换sockaddr,以方便我们填写地址信息。

    除了sa_family外,sockaddr是按网络字节顺序表示的。

    sockaddr_in的结构如下:

    struct sockaddr_in{ 
      short sin_family; 
      unsigned short sin_port; 
      struct in_addr sin_addr; 
      char sin_zero[8]; 
    };

    sin_family表示地址族,对于IPv4地址,sin_family成员将一直是AF_INET。

    成员sin_port指定的是将要分配给套接字的端口

    成员sin_addr给出的是套接字的主机IP地址。

    成员sin_zero只是一个填充数,以使sockaddr_in结构和sockaddr结构的长度一样。

    struct in_addr { 
      union {           
        struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;  
    struct { u_short s_w1,s_w2; }S_un_w;  
           u_long S_addr;
       } S_un;
    }

    下面对sockaddr和sockaddr_in对比:

    所以很多时候,我们绑定ip的时候是这样的:

    sockaddr_in s;
    s.sin_addr.s_un.s_addr = ip;

    一般服务器这样设置ip

    将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。 将地址指定为INADDR_ANY,允许一个独立应用接受发自多个接口的回应。

    如果想要自己设置ip,需要用到下面的函数:

    unsigned long inet_addr(constchar FAR * cp)
    作用:该函数将一个点分十进制IP地址字符串转换成32位数字表示的IP地址,适合分配给S_addr的u_long类型的数值

    这个函数是上面函数的反函数:

    char * inet_ntoa ( struct in_addr in ) in为传入参数,表示一个结构型的IP主机地址,该函数将一个32位数字表示的IP地址转换成点分十进制IP地址字符串

    这些结构的宏定义

    #define s_addr  S_un.S_addr
    #define s_host  S_un.S_un_b.s_b2
    #define s_net   S_un.S_un_b.s_b1
    #define s_imp   S_un.S_un_w.s_w2
    #define s_impno S_un.S_un_b.s_b4
    #define s_lh    S_un.S_un_b.s_b3

    7)int listen ( SOCKET s, int backlog );

    作用:服务器监听某个套接字,以便处理各个连接

    参数:

    s:要监听的套接字

    backlog:等待连接队列所能放置的连接数的最大数

    返回:如果这个函数调用成功,它将返回0。如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

    8)SOCKET accept ( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen );

    作用:用来接收某个连接的套接字

    s:接收连接的Socket

    addr:建立连接的对方地址信息

    addrlen:addr的长度

    返回:如果函数调用成功,它将返回一个新的SOCKET数据类型的套接字描述符。 如果调用失败,这个函数就会返回一个INVALID_SOCKET,错误信息可以通过WSAGetLastError函数返回。

    9)int send ( SOCKET s, const char FAR * buf, int len, int flags );

    作用:向目的主机发送数据

    参数:

    flags:用于控制数据传输方式,0表示按正常方式发送数据;宏MSG_DONTROUTE说明系统目标主机就在直接连接的本地网络中,无需路由选择;MSG_OOB指出数据是按带外数据发送的

    s:发送的套接字

    buf:发送数据所在缓冲区的首地址

    len:发送数据的大小

    返回:如果这个函数调用成功,它将返回发送的字节数。如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

     10)int recv ( SOCKET s, char FAR* buf, int len, int flags );

    作用:接受数据

    参数:

    flags:指定调用的方式。0表示接收的是正常数据,无特殊行为。MSG_PEEK表示会使有用的数据复制到所提供的接收端缓冲区内,但是没有从系统缓冲区中将数据删除。MSG_OOB表示处理带外数据。

    s:接受数据的套接字

    buf:接受缓冲区的首地址

    len:接受缓冲的大小

    返回:如果这个函数调用成功,它将返回接收到的字节数。如果连接被关闭,返回值为0。调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

    11)int connect ( SOCKET s, const struct sockaddr FAR* name, int namelen );

    作用:在客户端使用该函数请求建立连接时,将激活建立连接的三次握手,用来建立一条到服务器TCP的连接。

    参数:

    s:要连接的套接字

    name:地址结构

    namelen:地址结构长度

    返回:

    如果这个函数调用成功,它将返回0。如果调用失败,这个函数就会返回一个SOCKET_ERROR,错误信息可以通过WSAGetLastError函数返回。

    若TCP没有收到SYN分节的响应,则返回WSAETIMEDOUT

    若对客户SYN的响应是RST,则表明该服务器主机在我们指定的端口上没有进程在等待与之连接(例如服务器进程也许没有运行) ,客户一收到RST就马上返回WSAECONNREFUSED

    若客户发出的SYN在中间的某个路由器上引发了一个目的不可达ICMP错误,则客户主机内核保存该消息,并按一定时间间隔继续发送SYN,若在规定的时间仍未收到响应,则返回错误WSAEHOSTUNREACH 或WSAENETUNREACH

    12) int closesocket( SOCKET s );

    作用:关闭套接字,释放所占资源

    13)int shutdown( SOCKET s, int how );

    作用:在一个套接字上的读写操作完成后,应该首先使用shutdown()函数来关闭套接字的读通道、写通道或读写通道,这样做的好处是当双方不再有数据要发送或接收时,可以通知对方, “优雅”地关闭连接。

    参数:

    s:关闭的套接字

    how:用来规定关闭的方式

    shutdown函数只关闭读写通道,并不关闭套接字,且套接字所占有的资源将被一直保留到closesocket()调用之前。

    UDP接受发送数据与TCP接受数据大致相似,不过由于udp在通信之前不用连接,所以在发送的时候要加入地址信息:

    int recvfrom ( SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen );

    int sendto ( SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR * to, int tolen );

    附送源码(服务器时间同步)实例(客户端):

    #include <stdio.h>
    #include <Winsock2.h>
    
    #pragma comment(lib, "WS2_32.lib");
    
    int main()
    {
        WORD wVersionRequested;  
        WSADATA wsaData;  
        int err;  
        
        wVersionRequested = MAKEWORD(2, 2);  
       
        err = WSAStartup(wVersionRequested,&wsaData);  
        if (err != 0)
        {    
            printf("WSAStartup failed witherror: %d
    ", err);  
            return 0;  
        } 
        
        SOCKET mySocket = socket(AF_INET, SOCK_STREAM, 0);
        
        struct sockaddr_in s;
        s.sin_family = AF_INET;
        s.sin_port = 5555;
        s.sin_addr.S_un.S_addr = inet_addr("xxx.xxx.xxx.xxx");
    
        err = connect(mySocket, (struct sockaddr*)&s, sizeof(s));
        if (SOCKET_ERROR == err)
        {
            int err_num = WSAGetLastError();
            printf("connect error%d
    ", err_num);
            system("pause");
            return 1;
        }
        
        char sendBuff[200];
        char recvBuff[200];
        memset(recvBuff, 0, 200);
        memset(sendBuff, 0, 200);
    
        while (1)
        {
            gets(sendBuff);
            send(mySocket, (const char*)sendBuff, 200, 0);
            
            err = recv(mySocket, recvBuff, 200, 0);
            if (SOCKET_ERROR == err)
            {
                int err_num = WSAGetLastError();
                printf("recv´íÎó%d
    ", err_num);
                system("pause");
                return 1;
            }
            printf("%s", recvBuff);
            printf("echo once again?
    ");
            memset(recvBuff, 0, 200);
            memset(sendBuff, 0, 200);
        }
        closesocket(mySocket);
        WSACleanup(); 
        return 0;
    }

    服务器端

    #include <stdio.h>
    #include <Winsock.h>
    #include <time.h>
    #include <ctime>
    #include <stdlib.h>
    
    #pragma comment(lib, "WS2_32.lib");
    
    int main()
    {
        WORD wVersionRequested;  
        WSADATA wsaData;  
        int err;  
        
        wVersionRequested = MAKEWORD(2, 2);  
       
        err = WSAStartup(wVersionRequested,&wsaData);  
        if (err != 0)
        {    
            printf("WSAStartup failed witherror: %d
    ", err);  
            return 0;  
        } 
        
        SOCKET mySocket = socket(AF_INET, SOCK_STREAM, 0);
        SOCKET clientS;
    
        struct sockaddr_in s;
        s.sin_family = AF_INET;
        s.sin_port = 5555;
        s.sin_addr.S_un.S_addr = INADDR_ANY;
    
        err = bind(mySocket, (struct sockaddr*)&s, sizeof(s));
        if (SOCKET_ERROR == err)
        {
            int err_num = WSAGetLastError();
            printf("bind error %d
    ", err_num);
            system("pause");
            return 1;
        }
        
        err = listen(mySocket, 2);
        if (SOCKET_ERROR == err)
        {
            int err_num = WSAGetLastError();
            printf("listen error %d
    ", err_num);
            system("pause");
            return 1;
        }
        
        printf("Waiting for client connecting!
    ");
        int size = sizeof(s);
        clientS = accept(mySocket, (struct sockaddr*)&s, &size);
    
        if (INVALID_SOCKET == clientS)
        {
            printf("accept error
    ");return 1;
        }
    
        char sendBuff[200];
        char recvBuff[200];
        memset(recvBuff, 0, 200);
        memset(sendBuff, 0, 200);
        
        time_t t;
    
        while (1)
        {    
            err = recv(clientS, recvBuff, 200, 0);
            if (SOCKET_ERROR == err)
            {
                int err_num = WSAGetLastError();
                printf("recv error %d
    ", err_num);
                system("pause");
                return 1;
            }
            
            t = time(&t);
            strcat(sendBuff, "echo:");
            strcat(sendBuff+sizeof("echo:")-1, ctime(&t));
    
            printf("send:%s", sendBuff+sizeof("echo:"));
            err = send(clientS, sendBuff, 200, 0);
            if (SOCKET_ERROR == err)
            {
                int err_num = WSAGetLastError();
                printf("send error %d 
    ", err_num);
                system("pause");
                return 1;
            }
            memset(sendBuff, 0, 200);
        }
    
        closesocket(mySocket);
        WSACleanup(); 
        return 0;
    }
  • 相关阅读:
    20155313 杨瀚 《网络对抗技术》实验九 Web安全基础
    20155313 杨瀚 《网络对抗技术》实验八 Web基础
    20155313 杨瀚 《网络对抗技术》实验七 网络欺诈防范
    20155313 杨瀚 《网络对抗技术》实验六 信息搜集与漏洞扫描
    20155313 杨瀚 《网络对抗技术》实验五 MSF基础应用
    20155313 杨瀚 《网络对抗技术》实验四 恶意代码分析
    20155313 杨瀚 《网络对抗技术》实验三 免杀原理与实践
    20155313 杨瀚 《网络对抗技术》实验二 后门原理与实践
    20155313 杨瀚 《网络对抗技术》实验一 PC平台逆向破解(5)M
    20155313 2017-2018-1 《信息安全系统设计基础》课程总结
  • 原文地址:https://www.cnblogs.com/MyselfDancing/p/3660167.html
Copyright © 2011-2022 走看看