C/S模型之UDP協議
說明:
利用UDP協議,創建一個服務器和一個客戶端。兩者間進行通信。
由客戶端進行輸入內容,而服務器將接受的內容進行再一次返回,並顯示在服務端。
// UDP_Seversock.cpp : 定義控制臺應用程序的入口點。 #include "stdafx.h" #include <WinSock2.h> #include <Windows.h> #pragma comment (lib,"WS2_32.lib") //服務器端 int _tmain(int argc, _TCHAR* argv[]) { //檢測版本 WSADATA wsaData;int nErr = WSAStartup(MAKEWORD(2, 2), &wsaData); if (nErr != 0) { printf("WSAStartup Failed %d", GetLastError()); return -1; } if (HIBYTE(wsaData.wVersion != 2 || wsaData.wVersion != 2)) { printf("WinSock2 version not is WS2_32"); WSACleanup();return -1; } //開始 SOCKET sockSever = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //設置端口號和IP地址,INADDR_ANY是任意IP地址 SOCKADDR_IN sockSeverAddr; sockSeverAddr.sin_family = AF_INET; sockSeverAddr.sin_port = htons(10086); //sockSeverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); sockSeverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //將socket進行綁定 int nBindErr = bind(sockSever, (sockaddr*)&sockSeverAddr, sizeof(SOCKADDR_IN)); if (nBindErr == SOCKET_ERROR) { printf("bind Error!"); closesocket(sockSever); WSACleanup(); } printf("connect sucess!\n"); SOCKADDR_IN SenderAddr; int SenderAddrSize = sizeof (SenderAddr); char pRecvBuf[MAXBYTE] = { 0 }; char pPrintBuf[MAXBYTE] = { 0 }; while (1) { int nResult = recvfrom(sockSever, pRecvBuf, MAXBYTE, 0, (SOCKADDR *)&SenderAddr, &SenderAddrSize); if (nResult == SOCKET_ERROR) { printf("recvfrom failed with error "); } //重置,將IP地址和端口號,內容存放在pPrintBuf中 sprintf_s(pPrintBuf, "%s(%d):%s\n", inet_ntoa(sockSeverAddr.sin_addr), ntohs(sockSeverAddr.sin_port), pRecvBuf); //輸出在窗口 printf("%s\n", pPrintBuf); //客戶端請求退出 if (strcmp(pRecvBuf, "Q") == 0) { break; } int nSendResult = sendto(sockSever, pRecvBuf, MAXBYTE, 0, (sockaddr*)&SenderAddr, SenderAddrSize); if (nSendResult == SOCKET_ERROR) { printf("sendto Error!"); break; } } closesocket(sockSever); WSACleanup(); return 0; }
#include "stdafx.h" #include <WinSock2.h> #include <Windows.h> #pragma comment(lib,"WS2_32.lib") //客戶端 int _tmain(int argc, _TCHAR* argv[]) { //socket版本檢測 WSAData wsaData; int nErr=WSAStartup(MAKEWORD(2, 2), &wsaData); if (nErr != 0) { printf("WSAStartup Failed %d", GetLastError()); return -1; } if (HIBYTE(wsaData.wVersion) != 2 || LOBYTE(wsaData.wVersion) != 2) { printf("WinSock2 version not is WS2_32"); WSACleanup(); return -1; } //程序開始 SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); //設定端口和IP地址,127.0.0.1是本機回環地址 SOCKADDR_IN sockAddr; sockAddr.sin_family = AF_INET; sockAddr.sin_port = htons(10086); sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); int RecvAddrSize = sizeof (SOCKADDR_IN); //int nBindErr=bind(sockClient, (sockaddr*)&sockAddr, sizeof(SOCKADDR_IN)); //if (nBindErr == SOCKET_ERROR) //{ // printf("bind Error!"); // closesocket(sockClient); // WSACleanup(); // return -1; //} printf("connect sucess!\n"); char pSendBuf[MAXBYTE] = { 0 }; //發送服務器的內容 char pRecvBuf[MAXBYTE] = { 0 }; //接受服務器返回的內容 while (1) { gets_s(pSendBuf); //發送給服務器 int nResule = sendto(sockClient, pSendBuf, MAXBYTE, 0, (SOCKADDR *)& sockAddr, RecvAddrSize); if (nResule == SOCKET_ERROR) { printf("sendto Error!"); } //接受服務器返回的內容 int nRecvErr = recvfrom(sockClient, pRecvBuf, MAXBYTE, 0, (SOCKADDR *)& sockAddr, &RecvAddrSize); if (nRecvErr == SOCKET_ERROR) { break; } //服務器返回內容 printf("return from sever:%s\n", pRecvBuf); } closesocket(sockClient); WSACleanup(); return 0; }
註意點;
問題:為什麽UDP協議的客戶端的socket不需要綁定IP地址和端口號?
解析;
書上都是這麽說的,UDP客戶端不用綁定IP和端口,操作系統會給它自動分配端口。。。。
但是雖然沒有顯示綁定,但是操作系統卻似乎做了些隱蔽的事情。
首先,在客戶端,fd = socket(AF_INET, SOCK_DGRAM, 0),然後就想在此fd下進行recvfrom是收不到對方(假設對方就是服務器吧)的消息是辦不到的,
其實想想也很容易明白,這是fd未和任何端口、IP產生關聯要是這樣都能收到消息,那真要亂套了。想要在沒綁定的情況下受到服務器發來的消息,
首先客戶端得通過fd描述符首先向服務器發信息,然後這時在fd下進行阻塞recvfrom就能收到消息了,如果再在客戶端上fd 1= socket(AF_INET, SOCK_DGRAM, 0),
想在fd1 上進行recvfrom依然收不到消息,因為fd和服務器同過信,但fd1沒有,所以fd1收不到,但是從fd1向服務器發消息沒問題!所一總結一下就是,
只有當已通過fd向服務器發送了消息時(並且已經發通了),才能在fd處收到服務器發回來的消息,但是向服務器發送消息就不需要。
所以說操作系統在此做了些隱蔽的事情,當fd首先向服務器發消息時客戶端自動選折IP和一個PORT與該fd關聯了起來,(我覺得相當於背後還是綁定了一樣)。
而後面創建的fd1和之前的fd他們出客戶端的PORT是不同的(我在服務器端檢測了一下),所以通過fd向服務器發了消息但想在新建立的fd1下去recvfrom收不到消息。
另外,只能對一個socket描述符綁定一次,不能綁定多次,除非前面已經將該描述符close了。
反過來一個端口也只能被綁定到同一個socket描述符上,除非他們使用的不同的協議。
註意:如果客戶端綁定了,那麽在服務器端將無法輸出任何東西,在recvfrom()該函數阻塞。
1.每一次都要進行版本檢測。
2.頭文件的包含,#include <WinSock2.h>一定要在#include <Windows.h>的前面。
如:
#include <WinSock2.h>
#include <Windows.h>
C/S模型之UDP協議