• C++ Socket学习记录 2


    WinSock TCP 编程流程

      TCP通信,就像是固定电话,首先是要安装基站,然后是将电话号绑定到电话,然后拨号,接通之后说事,完事之后还要挂电话(甭管谁先挂)。

    1、初始化环境

      使用函数 int WSAstartup(WORD Version,LPWSAData dtPtr);

      参数 Version 为套接字版本,dtPtr 为一个WSAData 结构的 指针,表示获取到的 套接字的详细信息。

      typedef srcut WSAData{

        WORD Version ;        //当前版本

        WORD HightestVersion;    //当前库支持的最高版本

        char szDescription[WSADESCRIPTION_LEN + 1];      //库文件描述字符串

        char szSystemStatus[WASYS_STATUS_LEN + 1];     //系统状态字符串

        unsigned short maxSockets;   //同时支持的最大套接字数目

        unsigned short maxUdpDlg;   //已弃用(目的是什么都不知道就已经弃用了……)

        char FAR * VersionIndo;     // 也是在目的都不知道的情况被弃用了……
      }

      在使用WinSockt 前必须首先对库进行初始化

      DWORD wrd = MAKEWORD(2,0);//假设版本为2.0

      WSAData data;

      int res = WSAstartup(wrd,&data);

      函数返回一个int类型的值,为0则成功,否则失败;

      if(res != 0 )

      {

        MessageBox("初始化失败!");

      }

      注:1)初始化过程可放在程序启动时实现;2)在使用结束后(一般是在程序结束时,需要使用 WSAcleanup()释放资源)

    2、创建套接字

      在初始化库之后,就可以创建套接字了,WinSockt 中创建套接字使用 socket(int af,int type,int protocol);

      参数 af:地址格式:可以是 AF_INET,表示IPV4;或 AF_INET6,表示IPV6等

      参数 type:套接字类型 ,可以是 TCP :SOCK_STREAM;或 UDP:SOCK_DGRAM

      参数  protocol:如果type设定为 TCP或UDP,则设定为0

      那么创建一个基于TCP 的套接字可以写为

      SOCKET Socket = ::socket(AF_INET,SOCK_STREAM,0):

    3、绑定地址信息

      创建套接字后,需要绑定到特定的地址之后,才能使用

      了么首先需要定义一个 sockaddr_in 地址结构

      sockaddr_in addr;

      ......        //省略地址结构的初始化

      使用bind函数将套接字绑定到特定的地址

      int bres = ::bind(Socket, (sockaddr*)&addr, sizeof(addr));

      同样,成功返回 0,否则绑定失败。

    4、开始监听或链接

      对于服务端来说,在绑定成功后,需要监听;对于客户端来说,就可以连接到特定的服务器了。

      服务端监听:listen(SOCKET s,int flags)

      参数 s:执行监听的 套接字

      参数 flags:最多同时处理连接数,取值 1~5,默认 5

      则监听可写为

      ::listen(Socket,5);

      客户端链接:connnect(SOCKET s, sockaddr* lpAddr,int addrlen);

      参数 s:执行链接的套接字对象

      参数 lpaddr:要链接到的地址结构对象

      参数 addrlen:地址结构长度

      则链接可以写为:

      ::connnect(Socket,(sockaddr*)&addr,sizeof(addr));

    5、服务端接受链接

      对于服务器来说,因为客户端需要链接到她,所以它较客户端多一步,即接收(accept)来自客户端的链接请求。

      使用函数 SOCKET s :: accept(SOCKET Lsocket ,(sokaddr*) addr,int* lpLen) 接收来自客户端的请求

      参数 Lsocket :服务端负责监听的套接字

      参数 addr:绑定到的地址结构

      参数 lpLen:地址长度指针

      返回值 s:一个新的套接字,该套接字才是真正与客户端进行链接的套接字,使用该套接字实现与客户端的通信,因为Lsocket 正忙着监听其他的链接呢,没空。

      那么接受过程可以写为:

      while (true)
        {
            int len = sizeof(addr);
            SOCKET linkSock = ::accept(Socket, (sockaddr*)&addr, &len);
        }

      为什么使用无线循环呢,这样可以保证在接受到一个链接后,还可以继续接收其他的链接请求,不然在接收到一个链接请求后,程序向下执行,而其他客户端又在请求链接,但是负责开门的人已经走了,于是乎客户端就只能在门外苦苦的等待,直到死亡……

      注:accept函数也是一个比较傻的函数,在等待客户端链接过程,如果没有客户端请求,则会一直等待下去,程序将会阻塞在 accept处不再向下执行,直到接收到一个链接请求为止。所以建议将accept放到一个单独的线程执行,否则程序将出现“假死”状态。

    6、数据收发

      数据接收

      在套接字之间,使用 int ::recv(SOCKET s,char* buffer,int len ,int flags)进行接收数据

      参数 s:已经建立链接的套接字对象

      参数 buffer:接收到的数据缓存

      参数 len:欲接收的数据长度(最好和缓存区长度一致)

      参数 flags: 通常设为0

      返回值 int :表示实际接收到的字节数

      那么从对面接收数据可写为

      char buffer[512];
          int cnt = ::recv(linkSock, buffer, 512,0);

      数据发送

      套接字之间使用 int ::send(SOCKET s,char* buffer,int len,int flags)发送数据

      各参数与 recv 相同。

      发送数据可写为

      char* datas = "Hello world!\r\n";
          int scnt = ::send(linkSock, datas, sizeof(datas), 0);

      注:

        1)在服务端,负责与客户端通信的是与客户端建立链接的套接字对象,而不是负责监听的套接字。

        2)数据接收函数(recv)也是一个阻塞函数,因此建议在单独线程处理数据的收发。

        3)套接字TCP通信需要遵守“三次握手”原则,不然要出大问题的。

        4)每发送一条数据,在最后必须加上结束标识符(\r\n),否则在接收端接收的数据会出人意料的哦……

    7、关闭套接字

      在数据发送完毕,不再需要链接时,需要关闭套接字。

      ::closesocket(SOCKET s);

    至此,基于WinSock的TCP网络编程编程告一段落。至于在数据收发过程,根据不同的场景不同的需求可能需要遵守或制定特定的协议,但是“三次握手”是永恒不变的。

  • 相关阅读:
    shell脚本-awk
    shell脚本-sed命令
    shell脚本-grep和正则表达式
    wuti
    dmesg、stat命令
    uname、hostname命令
    tee、vi/vim命令
    tr、od命令
    vimdiff、rev命令
    dos2unix、diff命令
  • 原文地址:https://www.cnblogs.com/xtblog/p/5813226.html
Copyright © 2020-2023  润新知