//網絡編程服務器/*服務器端編程的步驟:1:加載套接字庫,創建套接字(WSAStartup() / socket());2:綁定套接字到一個ip地址和一個端口上(bind());3:將套接字設置為監聽模式等待連接請求(listen());4:請求到來后,接受連接請求,返回一個新的對應于此次連接的套接字(accept());5:用返回的套接字和客戶端進行通信(send() / recv());6:返回,等待另一連接請求;7:關閉套接字,關閉加載的套接字庫(closesocket() / WSACleanup())。*/#include <WinSock2.h>#include <stdio.h>#include <stdlib.h>#PRagma comment(lib, "ws2_32.lib")int main(){ WSADATA wsaData; int port = 5099; //自定義的鏈接端口號,大于1024就行 char buf[] = "I am a Server"; //判斷是否可以使用window的網絡編程庫,初始化sock資源 if (WSAStartup(MAKEWord(2, 2), &wsaData) != 0) { printf("Faied to Load winsork"); return 0; } //socket函數指定通信協議類型,套接字類型 //int socket(int family, int type, int protocol); // 成功返回非負描述符,出錯-1 //family指定協議族,type指定套接字類型,protocol指定某個協議類型常值,或者設為0。 /*family的值有: AF_INET IPv4協議 AF_INET6 Ipv6協議 AF_LOCAL Unix協議域 AF_ROUTE 路由套接字 AF_KEY 秘鑰套接字 type的值有: SOCK_STREAM 字節流套接字 SOCK_DGRAM 數據報套接字 SOCK_SEQPACKET 有序分組套接字 SOCK_RAW 原始套接字 protocol的值有: IPPROTO_CP TCP傳輸協議 IPPROTO_UDP UDP傳輸協議 IPPROTO_SCTP SCTP傳輸協議 socket函數在成功時返回一個小的非負整數值,與文件描述符類似,成為套接字描述符,為了得到這個描述符,需要指定協議族和套接字類型,但是并沒有指定本地協議地址和遠端協議地址。 */ //創建套接字 SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0); //IPv4 IP協議,字節流傳輸(TCP傳輸協議) //套接字結構體 SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); //4個函數來完成主機字節序和網絡字節序之間的轉換,h表示host,n表示net,s表示short,l表示long //#include <netinet/in.h> //uint16_t htons(uint16_t host16bitvalue); //uint32_t htonl(uint32_t host32bitvalue); //uint16_t ntohs(uint16_t net16bitvalue); //uint32_t ntohl(uint32_t net32bitvalue); addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //INADDR_ANY數值為0.0.0.0,表示任何地址都行 /*bind函數把一個本地協議地址賦予一個套接字,它只是把一個協議地址賦予一個套接字,至于協議地址的含義則取決于協議本身。 第二個參數指向協議地址結構的指針,第三個參數是協議地址的長度,對于TCP,調用bind函數可以指定一個端口號,或指定一個IP地址,或兩者都指定,也可以兩者都不指定。 bind函數綁定特定的IP地址必須屬于其所在主機的網絡接口之一,服務器在啟動時綁定它們眾所周知的端口,如果一個TCP客戶端或服務端未曾調用bind綁定一個端口, 當調用connect或listen時,內核就要為響應的套接字選擇一個臨時端口。讓內核選擇臨時端口對于TCP客戶端來說是正常的額,然后對于TCP服務端來說確實罕見的, 因為服務端通過他們眾所周知的端口被大家認識的 */ int retval = bind(sockSrv, (LPSOCKADDR)&addr, sizeof(SOCKADDR_IN)); if (retval == SOCKET_ERROR) { printf("Failed bind:%d/n", WSAGetLastError()); return 0; } /*socket創建一個套接字時,它被假設為一個主動套接字,也就是說,它是一個將調用connect發起連接的一個客戶套接字。 listen函數把一個未連接的套接字轉換為一個被動套接字,指示內核應接受指向該套接字的連接請求,調用listen函數將導致套接字從CLOSEE狀態轉換到LISTEN狀態。 第二個參數規定了內核應為相應套接字排隊的最大連接個數。*/ if (listen(sockSrv,10) == SOCKET_ERROR) { printf("Failed to listen"); } SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); while (1) { //等待客戶端請求 //accept函數由TCP服務器調用,用于從已完成隊列中列頭返回下一個已完成連接,如果已完成隊列為空,則進程被投入睡眠(如果該套接字為阻塞方式的話)。 //如果accept成功,那么其返回值是由內核自動生成的一個全新套接字,代表與返回客戶的TCP連接,函數的第一個參數為監聽套接字,返回值為已連接套接字 SOCKET sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len); //連接失敗 if (sockConn == SOCKET_ERROR) { printf("Accept Failed"); break; } //連接成功 printf("Accept client IP:[%s]/n", inet_ntoa(addrClient.sin_addr)); //inet_addr函數是舊函數庫的函數,編譯時會報錯,解決辦法,打開項目->屬性->配置屬性->c/c++ ->SDL檢測 將是改為否 //開始收發數據 //發數據 int iSend = send(sockConn, buf, sizeof(buf), 0); if (iSend == SOCKET_ERROR) { printf("Send Failed"); break; } //接數據 char recvBuf[100] = {}; recv(sockConn,recvBuf,sizeof(recvBuf),0); //輸出接到的數據 printf("%s/n", recvBuf); closesocket(sockConn); } //關閉開通的套接字接口 closesocket(sockSrv); //釋放資源 WSACleanup(); system("pause"); return 0;}
新聞熱點
疑難解答
圖片精選