通讯基本流程图如下所示:
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