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;
    }
  • 相关阅读:
    mahout协同过滤算法
    如何实现团队的自组织管理
    Trail: JDBC(TM) Database Access(3)
    JavaEE5 Tutorial_JavaBean,JSTL
    JavaEE5 Tutorial_Jsp,EL
    JavaEE5 Tutorial_Servlet
    J2SE7规范_2013.2_类
    J2SE7规范_2013.2_类型_命名
    Trail: JDBC(TM) Database Access(2)
    Trail: JDBC(TM) Database Access(1)
  • 原文地址:https://www.cnblogs.com/MyselfDancing/p/3660167.html
Copyright © 2011-2022 走看看