Socket编程基本流程实践

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

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

 

 

 

你可能感兴趣的:(socket编程)