1 計算機網絡基礎編程
1.1 OSI參考模型(略)服務器
1.2 TCP/IP協議網絡
傳輸控制協議/網際協議,TCP/IP將網絡分爲四層,從高往低一次爲應用層-》傳輸層-》網絡層-》數據鏈路層。其中,傳輸層協議包括TCP,UDP;網絡層協議包括IP、ARP等;socket
如圖所示:函數
注:圖片來源http://blog.csdn.net/lizhifeng2009/article/details/8820228測試
2 套接字編程基礎ui
2.1 套接字spa
套接字是網絡編程的基礎,最初由加利福尼亞大學爲UNIX開發的網絡通訊編程接口,爲了在WINDOWS 操做系統上使用,微軟與第三方廠商共同制定了一套標準,Winsock.。操作系統
套接字其實是一個指向傳輸提供者的句柄,Winsock中經過該句柄實現網絡通訊與管理。.net
能夠理解爲實現網絡通訊和管理,在應用層與傳輸層之間的操做接口。對於用戶而言,可經過調用SOCKET 相關接口函數,控制傳輸協議、傳輸內容等。
具體如圖所示:
2.1.1 套接字類型
根據套接字做用不一樣,分爲三類:原始套接字、流式套接字和數據包套接字。
原始套接字(SOCK_RAW):可以使程序人員對底層的網絡機制進行控制,接收的數據包頭中含有IP頭。
流式套接字(SOCK_STREAM):提供了雙向、有序、可靠的數據傳輸服務,在通訊前須要雙方創建鏈接,TCP協議採用了該套接字。
數據包套接字(SOCK_DGRAM):與流式套接字相對應,提供雙向數據流,可是不能保證數據傳輸的可靠性、有序性和無重複性。UDP協議採用了該套接字。
//2.2 套接字I/O模型
(暫缺)
2.3 套接字函數
爲了使用套接字進行網絡程序開發,Windows操做系統提供了一組套接字函數,使用這些函數,能夠實現功能強大的網絡編程。經常使用可分爲三大類:初始化庫函數(WSAStartup()、WSACleanup())、通訊時使用函數(socket()、bind()、listen()、accept()、closesocket()、connect()、recv()、send()、select()、ioctlsocket())和地址轉換函數(htons()、htonl()、inet_addr())。其中,TCP的三次握手是經過connect 函數觸發。
其中,WSAStartup()用於初始化動態連接庫函數,WSACleanup()用於釋放動態連接庫初始化時分配的資源。
注:套接字函數一般封裝在Ws2_32.dll動態連接庫中,其頭文件Winsock2.h提供了套接字函數的原型,庫文件Ws2_32.lib提供了Ws2_32.dll文件的輸出節,在使用套接字函數前,需包含頭文件 ,並連接Ws2_32.lib。
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
3 網絡編程步驟
實現網絡間通訊,須要一個服務器端和一個客戶端。這二者之間的具體實現有點不一樣,大致步驟爲:
3.1 服務器端
(1)建立套接字:socket()建立套接字,指明傳輸協議
例如:
sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//sListen表明返回的套接字
//AF_INET爲IPV4地址家族
//SOCK_STREAM爲套接字類型
//IPPROTO_TCP指明傳輸協議
(2)構建本地地址信息
//構建本地地址信息 saServer.sin_family = AF_INET; //地址家族 saServer.sin_port = htons(SERVER_PORT); //注意轉化爲網絡字節序 saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址
(3)bind()函數把一個地址族中的特定地址賦給socket。
//綁定 ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer)); if (ret == SOCKET_ERROR) { printf("bind() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //關閉套接字 WSACleanup(); //return 0; }
(4)監聽:listen()
//偵聽鏈接請求 ret = listen(sListen, 5); if (ret == SOCKET_ERROR) { printf("listen() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //關閉套接字 //return 0; }
(5)等待鏈接:accept()
accept()函數提取出所監聽套接字的等待鏈接隊列中第一個鏈接請求,建立一個新的套接字,並返回指向該套接字的文件描述符;
函數原型爲:int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
3.2 客戶端
(1)建立套接字
(2)構建服務器地址
(3)鏈接服務器connect()
4 實例
下面經過一個簡單的客戶端/服務器端程序,講解通訊編程的具體實現:
//服務器端 // Server.cpp : 定義控制檯應用程序的入口點。 //TCP測試程序的服務器端程序 #include "stdafx.h" #include <stdio.h> #include <winsock2.h> #pragma comment(lib,"ws2_32.lib") #define SERVER_PORT 5208 //偵聽端口 int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; WSADATA wsaData; int ret, nLeft, length; SOCKET sListen, sServer; //偵聽套接字,鏈接套接字 struct sockaddr_in saServer, saClient; //地址信息 char *ptr;//用於遍歷信息的指針 //WinSock初始化 wVersionRequested=MAKEWORD(2, 2); //但願使用的WinSock DLL 的版本 ret=WSAStartup(wVersionRequested, &wsaData); if(ret!=0) { printf("WSAStartup() failed!\n"); //return 0; } //建立Socket,使用TCP協議 sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sListen == INVALID_SOCKET) { WSACleanup(); printf("socket() faild!\n"); //return 0; } //構建本地地址信息 saServer.sin_family = AF_INET; //地址家族 saServer.sin_port = htons(SERVER_PORT); //注意轉化爲網絡字節序 saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址 //綁定 ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer)); if (ret == SOCKET_ERROR) { printf("bind() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //關閉套接字 WSACleanup(); //return 0; } //偵聽鏈接請求 ret = listen(sListen, 5); if (ret == SOCKET_ERROR) { printf("listen() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //關閉套接字 //return 0; } printf("Waiting for client connecting!\n"); printf("Tips: Ctrl+c to quit!\n"); //阻塞等待接受客戶端鏈接 while(1)//循環監聽客戶端,永遠不中止 { length = sizeof(saClient); sServer = accept(sListen, (struct sockaddr *)&saClient, &length); if (sServer == INVALID_SOCKET) { printf("accept() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //關閉套接字 WSACleanup(); return 0; } char sendMessage[]="\nhello client"; //發送信息給客戶端 send(sServer,sendMessage,strlen(sendMessage)+1,0); char receiveMessage[5000]; nLeft = sizeof(receiveMessage); ptr = (char *)&receiveMessage; while(nLeft>0) { //接收數據 ret = recv(sServer, ptr, 5000, 0); //非負,成功;-1,失敗 if (ret == SOCKET_ERROR) { printf("recv() failed!\n"); return 0; } if (ret == 0) //客戶端已經關閉鏈接 { printf("Client has closed the connection\n"); break; } nLeft -= ret; ptr += ret; } printf("receive message:%s\n", receiveMessage);//打印咱們接收到的消息。 } // closesocket(sListen); // closesocket(sServer); // WSACleanup(); return 0; }
//TCP測試程序的客戶端程序 #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <winsock2.h> #pragma comment(lib,"ws2_32.lib") #define SERVER_PORT 5208 //偵聽端口 int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; WSADATA wsaData; //WSADATA爲Windows套接字結構體 int ret; SOCKET sClient; //鏈接套接字 struct sockaddr_in saServer; //服務器地址信息 char *ptr; BOOL fSuccess = TRUE; //--------------------------------------------------------------- //WinSock初始化 //--------------------------------------------------------------- wVersionRequested = MAKEWORD(2, 2); //但願使用的WinSock DLL的版本 ret = WSAStartup(wVersionRequested, &wsaData); //加載套接字庫 if(ret!=0) { printf("WSAStartup() failed!\n"); //return 0; } //確認WinSock DLL支持版本2.2 if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2) { WSACleanup(); //釋放爲該程序分配的資源,終止對winsock動態庫的使用 printf("Invalid WinSock version!\n"); //return 0; } //建立Socket,使用TCP協議 sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sClient == INVALID_SOCKET) { WSACleanup(); printf("socket() failed!\n"); //return 0; } //構建服務器地址信息 saServer.sin_family = AF_INET; //地址家族 saServer.sin_port = htons(SERVER_PORT); //注意轉化爲網絡節序 saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //鏈接服務器 ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer)); if (ret == SOCKET_ERROR) { printf("connect() failed!\n"); closesocket(sClient); //關閉套接字 WSACleanup(); //return 0; } char sendMessage[]="hahaha!!!"; ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0); if (ret == SOCKET_ERROR) { printf("send() failed!\n"); } else printf("client info has been sent!"); char recvBuf[100]; recv(sClient,recvBuf,100,0); printf("%s\n",recvBuf); closesocket(sClient); //關閉套接字 WSACleanup(); //getchar(); 沒啥用,讓你最後在顯示終端能夠輸入一串字符,可是不能發送 //return 0; }