Windows socket c++ TCP UDP 簡單客戶端 vs2013
socket 主要是網絡中進程之間的通信,起源於Unix,而“一切皆可文件”的思想一樣可以用在socket上,即 打開 -> 讀寫 -> 關閉。
int socket(int domain, int type, int protocol):(服務端 & 客戶端)
socket函數對應於普通文件的打開操作。普通文件的打開操作返回一個文件描述字,而socket()用於創建一個socket描述符(socket descriptor),它唯一標識一個socket。
這個socket描述字跟文件描述字一樣,後續的操作都有用到它,把它作為參數,通過它來進行一些讀寫操作。
- domain:即協議域,又稱為協議族(family)。常用的協議族有,AF_INET
- type:指定socket類型。常用的socket類型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
- protocol:故名思意,就是指定協議。常用的協議有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP
當我們調用socket創建一個socket時,返回的socket描述字它存在於協議族(address family,AF_XXX)空間中,但沒有一個具體的地址。如果想要給它賦值一個地址,就必須調用bind()函數,否則就當調用connect()、listen()時系統會自動隨機分配一個端口。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen):(服務端)
正如上面所說bind()函數把一個地址族中的特定地址賦給socket。例如對應AF_INET、AF_INET6就是把一個ipv4或ipv6地址和端口號組合賦給socket。
- sockfd:即socket描述字,它是通過socket()函數創建了,唯一標識一個socket。bind()函數就是將給這個描述字綁定一個名字。
- addr:一個const struct sockaddr *指針,指向要綁定給sockfd的協議地址。這個地址結構根據地址創建socket時的地址協議族的不同而不同
- addrlen:對應的是地址的長度。
通常服務器在啟動的時候都會綁定一個眾所周知的地址(如ip地址+端口號),用於提供服務,客戶就可以通過它來接連服務器;而客戶端就不用指定,有系統自動分配一個端口號和自身的ip地址組合。這就是為什麽通常服務器端在listen之前會調用bind(),而客戶端就不會調用,而是在connect()時由系統隨機生成一個。
網絡字節序與主機字節序
主機字節序就是我們平常說的大端和小端模式:不同的CPU有不同的字節序類型,這些字節序是指整數在內存中保存的順序,這個叫做主機序。引用標準的Big-Endian和Little-Endian的定義如下:
a) Little-Endian就是低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。
b) Big-Endian就是高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。
網絡字節序:4個字節的32 bit值以下面的次序傳輸:首先是0~7bit,其次8~15bit,然後16~23bit,最後是24~31bit。這種傳輸次序稱作大端字節序。由於TCP/IP首部中所有的二進制整數在網絡中傳輸時都要求以這種次序,因此它又稱作網絡字節序。字節序,顧名思義字節的順序,就是大於一個字節類型的數據在內存中的存放順序,一個字節的數據沒有順序的問題了。
所以:在將一個地址綁定到socket的時候,請先將主機字節序轉換成為網絡字節序,而不要假定主機字節序跟網絡字節序一樣使用的是Big-Endian。
int listen(int sockfd, int backlog):(服務端)
如果作為一個服務器,在調用socket()、bind()之後就會調用listen()來監聽這個socket,如果客戶端這時調用connect()發出連接請求,服務器端就會接收到這個請求。
listen函數的第一個參數即為要監聽的socket描述字,第二個參數為相應socket可以排隊的最大連接個數。socket()函數創建的socket默認是一個主動類型的,listen函數將socket變為被動類型的,等待客戶的連接請求。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen):(服務端)
TCP服務器端依次調用socket()、bind()、listen()之後,就會監聽指定的socket地址了。TCP客戶端依次調用socket()、connect()之後就想TCP服務器發送了一個連接請求。TCP服務器監聽到這個請求之後,就會調用accept()函數取接收請求,這樣連接就建立好了。之後就可以開始網絡I/O操作了,即類同於普通文件的讀寫I/O操作。
accept的第一個參數為服務器的socket描述字,是服務器開始調用socket()函數生成的,稱為監聽socket描述字;而accept函數返回的是已連接的socket描述字。一個服務器通常通常僅僅只創建一個監聽socket描述字,它在該服務器的生命周期內一直存在。內核為每個由服務器進程接受的客戶連接創建了一個已連接socket描述字,當服務器完成了對某個客戶的服務,相應的已連接socket描述字就被關閉。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen):(客戶端)
connect函數的第一個參數即為客戶端的socket描述字,第二參數為服務器的socket地址,第三個參數為socket地址的長度。客戶端通過調用connect函數來建立與TCP服務器的連接。
萬事具備只欠東風,至此服務器與客戶已經建立好連接了。可以調用網絡I/O進行讀寫操作了,即實現了網咯中不同進程之間的通信!網絡I/O操作有下面幾組:
- read()/write()
- recv()/send()
- readv()/writev()
- recvmsg()/sendmsg()
- recvfrom()/sendto() (UDP)
C++ 代碼 vs2013 (兩個cpp 分別用vs打開)
1 #include <stdio.h> 2 #include <winsock2.h> 3 #include<WS2tcpip.h> 4 #pragma comment(lib,"ws2_32.lib") 5 6 int main(int argc, char* argv[]) 7 { 8 //初始化WSA windows自帶的socket 9 WORD sockVersion = MAKEWORD(2, 2); 10 WSADATA wsaData; 11 if (WSAStartup(sockVersion, &wsaData) != 0) 12 { 13 return 0; 14 } 15 16 //創建服務端套接字 17 SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 18 if (slisten == INVALID_SOCKET) 19 { 20 printf("socket error !"); 21 return 0; 22 } 23 24 //服務端需要綁定ip和端口 25 sockaddr_in sin; 26 sin.sin_family = AF_INET; 27 sin.sin_port = htons(8888); 28 sin.sin_addr.S_un.S_addr = INADDR_ANY; //監聽任意的地址 29 if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR) //將服務端套接字與上面的ip和端口綁定 30 { 31 printf("bind error !"); 32 } 33 34 //開始監聽 35 if (listen(slisten, 5) == SOCKET_ERROR) //用listen() 監聽前面綁定好的slisten套接字 36 { 37 printf("listen error !"); 38 return 0; 39 } 40 41 //循環接受數據 42 SOCKET sClient; //創建連接的套接字 43 sockaddr_in remoteAddr; 44 int nAddrlen = sizeof(remoteAddr); //用於接受客戶端地址 45 char revData[255]; //存儲接受的數據 46 while (1) 47 { 48 printf("等待連接...\n"); 49 sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen); //和客戶端 connect()對應 50 if (sClient == INVALID_SOCKET) 51 { 52 printf("accept error !"); 53 continue; 54 } 55 char sendBuf[20] = { ‘\0‘ }; 56 printf("接受到一個連接:%s \r\n", inet_ntop(AF_INET, (void*)&remoteAddr.sin_addr, sendBuf, 16)); 57 58 //數據接收 59 int ret = recv(sClient, revData, 255, 0); 60 if (ret > 0) 61 { 62 revData[ret] = 0x00; 63 printf(revData); 64 } 65 66 //發送數據給客戶端 67 const char *sendData = "你好,TCP客戶端! \n"; 68 send(sClient, sendData, strlen(sendData), 0); 69 closesocket(sClient); //關閉已接通的套接字 70 } 71 72 73 closesocket(slisten); //關閉監聽的套接字 74 WSACleanup(); 75 return 0; 76 }Server_demo_TCP
1 #include<WINSOCK2.H> 2 #include<STDIO.H> 3 #include<iostream> 4 #include<cstring> 5 #include <string> 6 #include<WS2tcpip.h> 7 using namespace std; 8 #pragma comment(lib, "ws2_32.lib") 9 int main() 10 { 11 //初始化WSA windows自帶的socket 12 WORD sockVersion = MAKEWORD(2, 2); 13 WSADATA data; 14 if (WSAStartup(sockVersion, &data) != 0) 15 { 16 return 0; 17 } 18 19 //創建客戶端套接字 20 while (true) 21 { 22 SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //客戶端套接字 23 if (sclient == INVALID_SOCKET) 24 { 25 printf("invalid socket!"); 26 return 0; 27 } 28 29 sockaddr_in serAddr; 30 serAddr.sin_family = AF_INET; 31 serAddr.sin_port = htons(8888); 32 inet_pton(AF_INET, "127.0.0.1", (void*)&serAddr.sin_addr.S_un.S_addr); 33 if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR) //與指定IP地址和端口的服務端連接 34 { 35 printf("connect error !"); 36 closesocket(sclient); 37 return 0; 38 } 39 40 //string data; 41 //cin >> data; 42 //const char * sendData; 43 //sendData = data.c_str(); //string轉const char* 44 // char * sendData1 = "你好,TCP服務端,我是客戶端\n"; 45 //send(sclient, sendData1, strlen(sendData1), 0); 46 47 printf("輸入你想傳輸的英文: \n"); 48 string data; 49 cin >> data; 50 const char * sendData2; 51 sendData2 = data.c_str(); //string轉const char* 52 send(sclient, sendData2, strlen(sendData2), 0); 53 54 55 char recData[255]; 56 int ret = recv(sclient, recData, 255, 0); 57 if (ret>0){ 58 recData[ret] = 0x00; 59 printf(recData); 60 } 61 closesocket(sclient); 62 63 } 64 WSACleanup(); 65 return 0; 66 67 }Client_demo_TCP
1 #include <stdio.h> 2 #include <winsock2.h> 3 #include<WS2tcpip.h> 4 #pragma comment(lib,"ws2_32.lib") 5 6 int main(int argc, char* argv[]) 7 { 8 WSADATA wsaData; 9 WORD sockVersion = MAKEWORD(2, 2); 10 if (WSAStartup(sockVersion, &wsaData) != 0) 11 { 12 return 0; 13 } 14 15 SOCKET serSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 16 if (serSocket == INVALID_SOCKET) 17 { 18 printf("socket error !"); 19 return 0; 20 } 21 22 sockaddr_in serAddr; 23 serAddr.sin_family = AF_INET; 24 serAddr.sin_port = htons(8888); 25 serAddr.sin_addr.S_un.S_addr = INADDR_ANY; 26 if (bind(serSocket, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR) 27 { 28 printf("bind error !"); 29 closesocket(serSocket); 30 return 0; 31 } 32 33 sockaddr_in remoteAddr; 34 int nAddrLen = sizeof(remoteAddr); 35 while (true) 36 { 37 char recvData[255]; 38 int ret = recvfrom(serSocket, recvData, 255, 0, (sockaddr*)&remoteAddr, &nAddrLen); 39 if (ret > 0) 40 { 41 recvData[ret] = 0x00; 42 char sendBuf[20] = { ‘\0‘ }; 43 printf("接受到一個連接:%s \r\n", inet_ntop(AF_INET, (void*)&remoteAddr.sin_addr, sendBuf, 16)); 44 printf(recvData); 45 } 46 47 const char * sendData = "一個來自服務端的UDP數據包\n"; 48 sendto(serSocket, sendData, strlen(sendData), 0, (sockaddr *)&remoteAddr, nAddrLen); 49 50 } 51 closesocket(serSocket); 52 WSACleanup(); 53 return 0; 54 }Server_demo_UDP
1 #include <stdio.h> 2 #include <winsock2.h> 3 #include<WS2tcpip.h> 4 #pragma comment(lib,"ws2_32.lib") 5 6 int main(int argc, char* argv[]) 7 { 8 WORD socketVersion = MAKEWORD(2, 2); 9 WSADATA wsaData; 10 if (WSAStartup(socketVersion, &wsaData) != 0) 11 { 12 return 0; 13 } 14 SOCKET sclient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 15 16 sockaddr_in sin; 17 sin.sin_family = AF_INET; 18 sin.sin_port = htons(8888); 19 /*sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");*/ 20 inet_pton(AF_INET, "127.0.0.1", (void*)&sin.sin_addr.S_un.S_addr); 21 22 int len = sizeof(sin); 23 24 const char * sendData = "來自客戶端的數據包.\n"; 25 sendto(sclient, sendData, strlen(sendData), 0, (sockaddr *)&sin, len); 26 27 char recvData[255]; 28 int ret = recvfrom(sclient, recvData, 255, 0, (sockaddr *)&sin, &len); 29 if (ret > 0) 30 { 31 recvData[ret] = 0x00; 32 printf(recvData); 33 } 34 system("pause"); 35 closesocket(sclient); 36 WSACleanup(); 37 return 0; 38 }Client_demo_UDP
主要參考:http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html
http://blog.csdn.net/xiaoquantouer/article/details/58001960
如有侵權,請告知~
Windows socket c++ TCP UDP 簡單客戶端 vs2013