• Socket编程基本流程实践


    通讯基本流程图如下所示:

    Server端代码(ServerDemo.cpp):

     1 #include <WinSock2.h>
     2 #include <Windows.h>
     3 #include <iostream>
     4 #include <string>
     5 #include <sstream>
     6 
     7 using namespace std;
     8 
     9 #pragma comment(lib, "WS2_32.lib")
    10 
    11 int main()
    12 {
    13     /*
    14         WSAstartup 必须是应用程序首先调用的 Winsock 函数。它允许应用程序指定所需的
    15     Windows Sockets API 的版本,获取特定 Winsock 实现的详细信息。仅当这个函数成功执行之
    16     后,应用程序才能调用其他 Winsock API
    17         每一个对WSAStartup的调用必须对应一个对WSACleanup的调用, 这个函数释放Winsock库。
    18     */
    19     WORD wVersion = MAKEWORD(2, 2);
    20     WSADATA WSAData;
    21     ::WSAStartup(wVersion, &WSAData);
    22 
    23     stringstream os;
    24     cout << "初始化套接字...." << endl;
    25     SOCKET s;
    26     s = ::socket(AF_INET, SOCK_STREAM, 0);
    27     if(s == INVALID_SOCKET)
    28     {
    29         cout << "socket fail!" << endl;
    30         goto __end;
    31     }
    32     sockaddr_in addr_in;
    33     addr_in.sin_family = AF_INET;            //设置地址家族
    34     addr_in.sin_addr.S_un.S_addr = INADDR_ANY;
    35     addr_in.sin_port = htons(8080);            // 转化端口号8080到网络字节顺序,并安排它到正确的成员
    36 
    37     // 绑定这个套节字到一个本地地址
    38     cout << "绑定端口8080...." << endl;
    39     if(::bind(s, (LPSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR)
    40     {
    41         cout << "bind error!" << endl;
    42         goto __end;
    43     }
    44 
    45     // listen 函数置套节字进入监听状态
    46     os << "开始监听...." << inet_ntoa(addr_in.sin_addr) << endl;  //inet_ntoa() 将32 位的二进制数转化为字符串
    47     cout << os.str() << endl;
    48     if(::listen(s, 2) == SOCKET_ERROR)
    49     {
    50         cout << "listen error!" << endl;
    51         goto __end;
    52     }
    53 
    54     SOCKET s_client;
    55     sockaddr_in addr_client;
    56     char szServerMsg[256] = "Hello client, this is server!";
    57     int nMsgLen = sizeof(szServerMsg);
    58     while(true)
    59     {
    60         // accept 函数用于接受到来的连接。
    61         if ((s_client = ::accept(s, (LPSOCKADDR)&addr_client, &nMsgLen)) == SOCKET_ERROR)
    62         {
    63             cout << "accept error!" << endl;
    64             goto __end;
    65         }
    66         os.str("");
    67         os.clear();
    68         os << "接收到来自" << inet_ntoa(addr_client.sin_addr) << "的连接!";
    69         cout << os.str() << endl;
    70         // send 函数在一个连接的套节字上发送缓冲区内的数据,返回发送数据的实际字节数。flag参数通常设置为0
    71         ::send(s_client, szServerMsg, 256, 0);
    72         ::closesocket(s_client);
    73     }
    74     ::closesocket(s);
    75 
    76 __end:
    77     ::WSACleanup();
    78     system("pause");
    79     return 0;
    80 }

    Client端代码(ClientDemo.cpp)

     1 #include <WinSock2.h>
     2 #include <Windows.h>
     3 #include <iostream>
     4 #include <string>
     5 #include <sstream>
     6 
     7 #pragma comment(lib, "WS2_32.lib")
     8 
     9 using namespace std;
    10 
    11 int main()
    12 {
    13     WORD wVersion = MAKEWORD(2, 2);
    14     WSADATA WSAData;
    15     ::WSAStartup(wVersion, &WSAData);
    16     SOCKET s = ::socket(AF_INET, SOCK_STREAM, 0);
    17     if (s == INVALID_SOCKET)
    18     {
    19         cout << "socket fail!" << ::WSAGetLastError() << endl;
    20         ::WSACleanup();
    21         return 0;
    22     }
    23     sockaddr_in serverAddr;
    24     // inet_addr函数转化一个"aa.bb.cc.dd"类型的IP地址字符串到长整型,它是以网络字节顺序记录的IP地址,
    25     // sin_addr.S_un.S_addr指定了地址联合中的此长整型
    26     serverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");    
    27     serverAddr.sin_family = AF_INET;
    28     serverAddr.sin_port = htons(8080);
    29 
    30     // 客户端程序在创建套节字之后,要使用 connect 函数请求与服务器连接
    31     if (::connect(s, (LPSOCKADDR)&serverAddr, sizeof(sockaddr_in)))
    32     {
    33         cout << "connect server fail!" << endl;
    34         ::WSACleanup();
    35         return 0;
    36     }
    37 
    38     char buf[256];
    39     // recv 函数从对方接收数据,并存储它到指定的缓冲区。flag参数通常设置为0
    40     ::recv(s, buf, 256, 0);
    41     stringstream os;
    42     os << "从服务器接收到数据:" << buf;
    43     cout << os.str() << endl;
    44 
    45     system("pause");
    46     return 0;
    47 }

      TCP 协议由于可靠、稳定的特征而被用在大部分场合,但它对系统资源要求比较高。UDP
    协议是一个简单的面向数据报的传输层协议,又叫用户数据报协议。它提供了无连接的、不可
    靠的数据传输服务。无连接是指他不像 TCP 协议那样在通信前先与对方建立连接以确定对方
    的状态。不可靠是指它直接安装指定 IP 地址和端口号将数据包发出去,如果对方不在线的话
    数据就可能丢失。UDP 协议编程流程如下:
    1.服务器端
    (1)创建套节字(socket) 。
    (2)绑定 IP 地址和端口(bind) 。
    (3)收发数据(sendto/recvfrom) 。
    (4)关闭连接 (closesocket) 。
    2.客户端
    (1)创建套节字(socket) 。
    (2)收发数据(sendto/recvfrom) 。
    (3)关闭连接(closesocket) 。
    UDP 协议用于发送和接收数据的函数是 sendto 和 recvfrom。它们的原形如下。
    int sendto (
    SOCKET s, // 用来发送数据的套节字
    const char FAR * buf, // 指向发送数据的缓冲区
    int len, // 要发送数据的长度
    int flags, // 一般指定为0
    const struct sockaddr * to, // 指向一个包含目标地址和端口号的sockaddr_in结构
    int tolen // 为 sockaddr_in 结构的大小
    );
    同样 UDP 协议接收数据也需要知道通信对端的地址信息。
    int recvfrom (SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen);
    这个函数不同于 recv 函数的是多出来的最后两个参数,from 参数是指向 sockaddr_in 结
    构的指针,函数在这里返回数据发送方的地址,fromlen 参数用于返回前面的 sockaddr_in 结构
    的长度。
    与 TCP 协议相比,UDP 协议简单多了,编程细节就不详细介绍了。

    TCPClient封装类(tcpClient.hpp):

      1 #ifndef __TCP_CLIENT_H__
      2 #define __TCP_CLIENT_H__
      3 
      4 #include <winsock2.h>
      5 #include <stdio.h>
      6 #include <iostream>
      7 
      8 class CTcpClient
      9 {
     10 public:
     11     std::string m_strErrInfo;//错误信息
     12 
     13     CTcpClient()
     14     {
     15         WSAData wsaData;
     16         if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0)
     17         {            
     18             m_strErrInfo = "WSAStartup失败";
     19             printf(m_strErrInfo.c_str());
     20         }
     21         if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
     22         {
     23             m_strErrInfo = "WSAStartup SOCKET版本不对";
     24             printf(m_strErrInfo.c_str());
     25         }
     26     }
     27 
     28     ~CTcpClient()
     29     {
     30         WSACleanup();
     31     }
     32 
     33     int SendData(const char *pAddr, const char *pPort
     34         , int iSendTimeOut, int iRecvTimeOut
     35         , const char *pSendData, int nSendLen
     36         , char *pRecvData, int nRecevLen)
     37     {
     38         int iTimeOut;
     39         struct sockaddr_in addrServer;
     40         m_strErrInfo="";
     41         int nRet = 0;
     42 
     43         //创建SOCKET
     44         SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);
     45         do 
     46         {
     47             if(sockClient == INVALID_SOCKET)
     48             {
     49                 m_strErrInfo = "socket创建失失败";
     50                 nRet = -1;
     51                 break;
     52             }
     53             //连接到服务器
     54             memset(&addrServer,0,sizeof(sockaddr_in));
     55             addrServer.sin_family = AF_INET;
     56             addrServer.sin_addr.s_addr = inet_addr(pAddr);
     57             addrServer.sin_port = htons(atoi(pPort));
     58 
     59             if(connect(sockClient,(const struct sockaddr *)&addrServer,sizeof(sockaddr)) != 0)
     60             {
     61                 nRet = -2;
     62                 m_strErrInfo = "连接到服务器失败.";
     63                 break;
     64             }
     65             //设置发送超时
     66             iTimeOut = iSendTimeOut; 
     67 
     68             if(::setsockopt(sockClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
     69             {
     70                 m_strErrInfo = "setsockopt失败";
     71                 nRet = -3;
     72                 break;
     73             }
     74             //设置接收超时
     75             iTimeOut = iRecvTimeOut;
     76 
     77             if(::setsockopt(sockClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
     78             {
     79                 m_strErrInfo = "setsockopt失败";
     80                 nRet = -4;
     81                 break;
     82             }
     83             //发送请求
     84             if(send(sockClient, pSendData, nSendLen * sizeof(char), 0) <= 0)
     85             {
     86                 m_strErrInfo = "发送失败.";
     87                 nRet = -5;
     88                 break;
     89             }
     90 
     91             //接收服务端应答
     92             memset(pRecvData, 0, nRecevLen * sizeof(char));
     93             int rc = SOCKET_ERROR;
     94             int cnt = nRecevLen * sizeof(char);
     95 
     96             while(cnt > 0)
     97             {
     98                 rc = recv(sockClient, pRecvData, nRecevLen * sizeof(char), 0);
     99 
    100                 if(rc == SOCKET_ERROR)
    101                 {
    102                     m_strErrInfo = "接收失败";
    103                     nRet = -6;
    104                     break;
    105                 }
    106                 if(rc == 0)
    107                 {
    108                     if(nRet <= 0)
    109                     {
    110                         nRet = -7;
    111                         m_strErrInfo = "后台无应答";
    112                     }
    113                     //nRet = ( ? -7 : nRet);
    114                     break;
    115                 }
    116                 nRet += rc;
    117                 pRecvData += rc;
    118                 cnt -= rc;
    119             }
    120 
    121         }while (0);
    122         
    123         closesocket(sockClient);
    124         return nRet;
    125     }
    126 
    127     int SendData(const char *pAddr, const char *pPort
    128         , int iSendTimeOut, int iRecvTimeOut
    129         , const char *pSendData, std::string &strRecv, int iMulRecv = 0)
    130     {
    131         int iRet;
    132         int iTimeOut;
    133         struct sockaddr_in addrServer;
    134         char szRecvDataBuf[1024*64+1];
    135 
    136         m_strErrInfo="";
    137 
    138         //创建SOCKET
    139         SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);
    140         if(sockClient == INVALID_SOCKET)
    141         {
    142             m_strErrInfo = "socket创建失败";
    143             return -1;
    144         }
    145 
    146         //连接到服务器
    147         memset(&addrServer,0,sizeof(sockaddr_in));
    148         addrServer.sin_family = AF_INET;
    149         addrServer.sin_addr.s_addr = inet_addr(pAddr);
    150         addrServer.sin_port = htons(atoi(pPort));
    151         if(connect(sockClient,(const struct sockaddr *)&addrServer,sizeof(sockaddr)) != 0)
    152         {
    153             m_strErrInfo = "连接服务器失败";
    154             goto _end;
    155         }
    156 
    157         //设置发送超时
    158         iTimeOut = iSendTimeOut; 
    159         if(::setsockopt(sockClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
    160         {
    161             m_strErrInfo = "setsockopt失败";
    162             goto _end;
    163         }
    164         //设置接收超时
    165         iTimeOut = iRecvTimeOut;  
    166         if(::setsockopt(sockClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
    167         {
    168             m_strErrInfo = "setsockopt失败";
    169             goto _end;
    170         }
    171 
    172         //发送请求
    173         if(send(sockClient, pSendData, strlen(pSendData), 0) <= 0)
    174         {
    175             m_strErrInfo = "发送失败";
    176             goto _end;
    177         }
    178 
    179         //接收服务端应答
    180         strRecv = "";
    181         do 
    182         {
    183             memset(szRecvDataBuf, 0, sizeof(szRecvDataBuf));
    184             iRet = recv(sockClient, szRecvDataBuf, sizeof(szRecvDataBuf)-1, 0);
    185             strRecv += szRecvDataBuf;
    186         } while (iRet > 0 && iMulRecv);
    187         if(0 == strRecv.length())
    188         {
    189             m_strErrInfo = "接收失败";
    190         }
    191 
    192         //关闭SOCKET
    193         closesocket(sockClient);
    194         return 0;
    195 
    196 _end:
    197         closesocket(sockClient);
    198         return -1;
    199     }
    200 
    201     std::string GetIPAddrByDNS(const char *pDNS)
    202     {
    203         //通过域名得到IP地址
    204         std::string strAddr;
    205         WSADATA wsadata;
    206         WSAStartup(MAKEWORD(2,2),&wsadata);
    207         hostent *phost=gethostbyname(pDNS);
    208         if(phost)
    209         {
    210             in_addr addr;
    211             for(int i=0;;i++)
    212             {
    213                 char *p=phost->h_addr_list[i];
    214                 if(p==NULL)    break;
    215                 memcpy(&addr.S_un.S_addr,p,phost->h_length);
    216                 char* ip=inet_ntoa(addr);
    217                 strAddr = ip;
    218 
    219                 if (strAddr.length())
    220                     break;
    221             }
    222         }
    223         return strAddr;
    224     }
    225 };
    226 #endif

     

  • 相关阅读:
    DroidParts 中文系列教程(基于官方教程)
    IDEA添加其他项目为库文件的方法
    IDEA 部署项目的时候出错:Jar not loaded错误
    解决IDEA导入Myclipse项目的时候没有识别为Web项目的问题
    IDEA中安装及配置SVN
    VirtualBox下设置 XP虚拟机桥接模式
    主机上设置共享文件夹供虚拟机访问
    JS的splice()方法在for循环中使用可能会遇到的坑
    Eclipse优化
    State Design Pattern
  • 原文地址:https://www.cnblogs.com/dongsheng/p/4487777.html
Copyright © 2020-2023  润新知