• Winsock基础编程


    Winsock基础编程

           Socket的英文原义是插座。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作“套接字”,用于描写叙述IP地址和port,是一个通信链的句柄。在Internet上的主机一般执行了多个服务软件,同一时候提供几种服务。每种服务都打开一个Socket,并绑定到一个port上,不同的port相应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。一台主机宛如布满各种插座的房间,每一个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就能够得到不同的服务。

    1 基于TCP

    它是面向连接的,稳定的网络通信。它的通信流程例如以下所看到的:


    <1>TCP 连接建立

    TCP连接的建立须要经过三次握手,所谓的三次握手例如以下所看到的:


    • client向server发送一个SYN J
    • server向client响应一个SYN K,并对SYN J进行确认ACK J+1
    • client再想server发一个确认ACK K+1

    通俗的讲就是:

    A:B,我想连接你

    B:A,我看到了,你连接吧

    A:那我连了

    B:等待接受。。。

    <2>TCP连接终止

    TCP连接的终止须要以下四次挥手。


    • 某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
    • 还有一端接收到FIN M之后,运行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,由于FIN的接收意味着应用进程在对应的连接上再也接收不到额外数据;
    • 一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
    • 接收到这个FIN的源发送端TCP对它进行确认。

    通俗的讲就是:

    A:我要关了啊

    B:你要关吗?

    B:这么长时间没反应,那我要关了。

    A:看到你要关了

    B:关闭

    <3>TCP稳定性的保证

    它採用的是确认重传机制,没发送一个消息它都会启动一个定时器,观察一定时间内有没有收到对方的确认信息,假设收到就算了,没收到它就以为消息没发过去然后就会又一次发送。正是因为这样的确认重传机制导致了TCP通信的效率没UDP高。

    2 基于UDP

    它是提供面向无连接的,不可靠的通信协议。它的通信步骤例如以下图所看到的:


    3 相关函数介绍

    <1>WSAStartup函数

    intPASCAL FAR WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

    wVersionRequested:用来指定载入Winsock库的版本号,低位字节代表主版本号号,高位字节代表副版本号号。通经常使用MAKEWORD(x,y)表示版本号号,当中x表示高位自己,y表示低位字节。

    lpWSAData:返回的指向WSADATA结构体的指针,这个结构体里面包括着主要关于版本的信息。

    返回值:

    成功:返回0。

    失败:

    WSASYSNOTREADY:表示网络设备没有准备好。

    WSAVERNOTSUPPORTTED:Winsock的版本号信息号不支持。

    WSAEINPROGRESS:一个堵塞式的Winsock1.1存在于进程中。

    WSAEPROCLIM:已经达到Winsock的使用量的上限。

    WSAEFAULT:lpWSAData不是一个有效的指针。

    注意:

    这个函数winsock的初始化函数,假设它失败那么后面的全部相关Winsock的都不会被运行。

    <2>socket函数

    SOCKETsocket(int af,  int type,  int proctocol);

    这个socket有点类似句柄,或者文件指针。

    af:address family(地址族),一般都填AF_INIT,表示是在Internet上的Socket;

    type:Socket的类型,当採用流连接方式时用SOCK_STREAM,用数据报文方式时用

    SOCK_DGRAM。

    proctocol:一般都为0,表示对2种类型的Socket分别採用缺省的TCP和UDP传输协议。

    <3>bind函数

    int bind(SOCKET s,  const sockaddr* name,  intnamelen);

    这个函数的作用是把socket绑定到指定的IP地址以及port。

    s:要绑定的socket

    name:指向sockaddr*的结构体指针。结构体例如以下所看到的:

    struct sockaddr
    {
     u_shortsa_family;
    char sa_data[14];
    };

    可是通常在Winsock中用sockaddr_in结构体取代这个结构体,它例如以下所看到的:

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

    当中in_addr是这样一个结构体

    struct in_addr {
            union {
                    struct{ UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
                    struct{ USHORT s_w1,s_w2; } S_un_w;
                    ULONG S_addr;
            } S_un;
    }

    我们能够看到这个结构体里面就有一个union,字节长度是4个字节。能够利用这个结构方便的将点分十进制格式的IP地址转换成u_long类型,而且将结果赋予S_addr。

    namelen: name所指向的结构体的长度

    注意:

    port号的范围是0-65536,可是1024一下的port都被预占,所以在指定port号的要在其上面。

    另外,in_addr和sockaddr结构体的字节长度相等。

    <4>listen函数

    int listen(SOCKET s,  int backlog);

    s: 启动监听的socket

    backlog:等待连接队伍的最大长度,一般设置为1-5

    <5>accept函数

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

    s:监听状态的套接字

    addr:保存client的信息,ip,port号等

    addrlen:地址信息的长度,这也是个返回值

    注意:

    当调用accept函数的时候,程序就会等待client调用connect函数发生连接。

    <6>connect函数

    Int connect(SOCKET s,const sockaddr*name,int namelen);

    s:连接的套接字

    addr:欲要连接的地址

    addrlen:地址信息的长度

    <7>send/recv函数

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

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

    s:发送信息的socket

    buf:发送/接受 数据的缓冲区

    len:发送/接受 信息的长度

    flags:影响函数行为的值,一般设置为0

    注意:

    发送的长度一般要依据详细的字节长度来指定,接受的长度则是整个缓冲区的长度。

    <8>sendto/recvfrom 函数

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

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

    s:己方的socket

    buf:缓冲区

    len:缓冲区长度

    flags:一般设置为0,是个影响函数行为的參数

    from/to:返回值,表示对方的地址信息 / 输入的对方地址信息

    fromlen/tolen:返回值,表示对方地址信息的长度 / 输入的对方地址信息长度

    <9> closesocket函数

    int closesocket(socket s);

    关闭一个socket。

    UDP的演示样例程序:

    //server
    #include <WinSock2.h>
    #include <stdio.h>
    #pragma comment(lib,"ws2_32.lib")
     
    void main()
    {
             WORD wVersionRequested;
             WSADATA wsaData;
             interr;
             wVersionRequested = MAKEWORD(1,1);
             err =WSAStartup(wVersionRequested,&wsaData);
             if(err!= 0)
             {
                       return;
             }
     
             if(LOBYTE(wsaData.wVersion)!= 1 || HIBYTE(wsaData.wVersion) != 1 )
             {
                       WSACleanup();
                       return;
             }
     
             SOCKET sockSrv = socket(AF_INET,SOCK_DGRAM,0);
     
             SOCKADDR_IN addrSrv;
             addrSrv.sin_addr.S_un.S_addr =htonl(INADDR_ANY);
             addrSrv.sin_family = AF_INET;
             addrSrv.sin_port = htons(6000);
     
             bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR) );
             charrecvBuf[100];
             charsendBuf[100];
             chartempBuf[200];
     
             SOCKADDR_IN addrClient;
             int len= sizeof(SOCKADDR);
     
             while(1)
             {
                       recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
                       if('q' == recvBuf[0])
                       {
                                sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClient,len);
                                printf("chat end");
                       }
                       sprintf(tempBuf,"%s say : %s",inet_ntoa(addrClient.sin_addr),recvBuf);
                       printf("%s
    ",tempBuf);
     
                       printf("please input data:
    ");
                       gets(sendBuf);
                       sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClient,len);
             }
             closesocket(sockSrv);
             WSACleanup();
    }


    //client
    #include <WinSock2.h>
    #include <stdio.h>
    #pragma comment(lib,"ws2_32.lib")
     
    void main()
    {
             WORD wVersionRequested;
             WSADATA wsaData;
             interr;
     
             wVersionRequested = MAKEWORD(1,1);
             err =WSAStartup(wVersionRequested,&wsaData);
             if(err!= 0)
             {
                       printf("WSAStartup failed
    ");
                       return;
             }
             if(LOBYTE(wsaData.wVersion)!= 1 || HIBYTE(wsaData.wVersion) != 1)
             {
                       WSACleanup();
                       return;
             }
     
             SOCKET sockClient =socket(AF_INET,SOCK_DGRAM,0);
            
             SOCKADDR_IN addrServer;
             addrServer.sin_addr.S_un.S_addr =inet_addr("127.0.0.1");
             addrServer.sin_family = AF_INET;
             addrServer.sin_port = htons(6000);
     
             charrecvBuf[100];
             charsendBuf[100];
             chartempBuf[200];
             int len= sizeof(SOCKADDR);
     
             while(1)
             {
                       printf("please input data
    ");
                       gets(sendBuf);
                       sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrServer,len);
                       recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrServer,&len);
                       if('q' == recvBuf[0])
                       {
                                sendto(sockClient,"q",strlen("q")+1,0,(SOCKADDR*)&addrServer,len);
                                printf("chat end
    ");
                                break;
                       }
                       sprintf(tempBuf,"%s say :%s",inet_ntoa(addrServer.sin_addr),recvBuf);
                       printf("%s
    ",tempBuf);
     
             }
             closesocket(sockClient);
             WSACleanup();
    }


     

  • 相关阅读:
    推荐几个不错的console调试技巧
    JS字符串截取函数slice(),substring(),substr()的区别
    ES6入门基础
    setTimeout的小尴尬
    Javascript自定义事件功能与用法实例分析
    JavaScript中函数定义的三种方法
    数组方法
    数组选择排序
    数组冒泡排序
    this指向问题(1)
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4518092.html
Copyright © 2020-2023  润新知