通信基本流程圖以下所示:ios
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):
socket
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