Socket編程基本流程實踐

通信基本流程圖以下所示: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
相關文章
相關標籤/搜索