// socket.cpp : 定義控制台應用程序的入口點。 // //服務器端 //SOCKET連接過程 //根據連接啟動的方式以及本地套接字要連接的目標,套接字之間的連接過程可以分為三個步驟:服務器監聽,客戶端請求,連接確認。 //服務器監聽:是服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態。 //客戶端請求:是指由客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。 //為此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然后就向服務器端套接字提出連接請求。 //連接確認:是指當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求,它就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,連接就建立好了。 //而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。 //如何開發一個Server-Client模型的程序 //開發原理: //服務器,使用ServerSocket監聽指定的端口,端口可以隨意指定(由於1024以下的端口通常屬於保留端口,在一些操作系統中不可以隨意使用,所以建議使用大於1024的端口),等待客戶連接請求,客戶連接后,會話產生;在完成會話后,關閉連接。 // 客戶端,使用Socket對網絡上某一個服務器的某一個端口發出連接請求,一旦連接成功,打開會話;會話完成后,關閉Socket。客戶端不需要指定打開的端口,通常臨時的、動態的分配一個1024以上的端口。 // Socket接口是TCP/IP網絡的API,Socket接口定義了許多函數或例程,程序員可以用它們來開發TCP/IP網絡上的應用程序。 //要學Internet上的TCP/IP網絡編程,必須理解Socket接口。Socket接口設計者最先是將接口放在Unix操作系統里面的。如果了解Unix系統的輸入和輸出的話,就很容易了解Socket了。 //網絡的Socket數據傳輸是一種特殊的I/O,Socket也是一種文件描述符。 //Socket也具有一個類似於打開文件的函數調用Socket(),該函數返回一個整型的Socket描述符,隨后的連接建立、數據傳輸等操作都是通過該Socket實現的。 //常用的Socket類型 //有兩種:流式Socket(SOCK_STREAM)和數據報式Socket(SOCK_DGRAM)。 //流式是一種面向連接的Socket,針對於面向連接的TCP服務應用; //數據報式Socket是一種無連接的Socket,對應於無連接的UDP服務應用。 //Socket 阻塞與非阻塞模式 //Windows套接字在阻塞和非阻塞兩種模式下執行I/O操作。 //在阻塞模式下,在I/O操作完成前,執行的操作函數一直等候而不會立即返回,該函數所在的線程會阻塞在這里。 //相反,在非阻塞模式下,套接字函數會立即返回,而不管I/O是否完成,該函數所在的線程會繼續運行。 #pragma comment(lib, "Ws2_32.lib") #include "stdafx.h" #include <WinSock2.h> #include <stdio.h> #include <iostream> #include <WS2tcpip.h> #include <process.h> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested;//typedef unsigned short WORD WSADATA wsaData;//這個結構被用來存儲 被WSAStartup函數調用后返回的 Windows Sockets數據 int err; wVersionRequested = MAKEWORD(1,1); // WORD MAKEWORD( // BYTE bLow, // BYTE bHigh // ); err = WSAStartup(wVersionRequested,&wsaData);//WSAStartup,即WSA(Windows SocKNDs Asynchronous,Windows異步套接字)的啟動命令。 //是Windows下的網絡編程接口軟件Winsock1 或 Winsock2 里面的一個命令 //這個函數是用來加載Winsocket DLL,wVersionRequested是用來存儲你所要申請的Winsocket DLL版本, //可以通過MAKEWORD函數獲取,wVersionRequested的高位代表副版本號,低位代表高版本號 // int WSAStartup( // __in WORD wVersionRequested, // __out LPWSADATA lpWSAData // ); if(err != 0)//返回0表示成功 { return 0; } if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)//高位字節指出副版本(修正)號,低位字節指明主版本號。/* Confirm that the Windows Sockets DLL supports 1.1.*/ { /* Tell the user that we couldn't find a useable winsock.dll. */ WSACleanup();//終止Winsock 2 DLL (Ws2_32.dll) 的使用. return 0; } SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0); //int socket(int domain, int type, int protocol); 該函數如果調用成功就返回新創建的套接字的描述符,如果失敗就返回INVALID_SOCKET SOCKADDR_IN addrSrv;//sockaddr_in和sockaddr是並列的結構 // struct sockaddr_in // { // short int sin_family; /* Address family */ // unsigned short int sin_port; /* Port number */ // struct in_addr sin_addr; /* Internet address */ // unsigned char sin_zero[8]; /* Same size as struct sockaddr */ // }; //該結構體用於指定一個socket的一端【ip+port】 addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //所以local.sin_addr.s_addr是ip地址。作為服務器,你要綁定【bind】到本地的IP地址上進行監聽【listen】, //但是你的機器上可能有多塊網卡,也就有多個IP地址,這時候你要選擇綁定在哪個IP上面,如果指定為INADDR_ANY,那么系統將綁定默認的網卡【即IP地址】。 //作為客戶端,你要連接【connect】到遠端的服務器,也是要指定遠端服務器的(ip, port)對。 //當然,在這種情況下,不可能將IP地址指定為INADDR_ANY,系統會瘋掉的。 addrSrv.sin_family = AF_INET; //Winsock2.h中 #define AF_INET 0 #define PF_INET AF_INET //AF 表示ADDRESS FAMILY 地址族 //PF 表示PROTOCOL FAMILY 協議族 // 但這兩個宏定義是一樣的 //所以使用哪個都沒有關系 addrSrv.sin_port = htons(6000); //htons 是把你機器上的整數轉換成“網絡字節序”, 網絡字節序是 big-endian,也就是整數的高位字節存放在內存的低地址處 bind(sockSrv,(SOCKADDR*) &addrSrv,sizeof(SOCKADDR)); //將套接字綁定於特定地址的特定端口,其中第二個參數可以使用SOCKADDR_IN來代替。 // int bind( // SOCKET s, // const struct sockaddr FAR* name, // int namelen // ); //參數說明: socket:是一個套接字。 //address:是一個sockaddr結構指針,該結構中包含了要結合的地址和端口號。 // address_len:確定address緩沖區的長度。 listen(sockSrv,5);//這個函數一般用於服務器端,這里的第二個參數為請求隊列的最大程度,注意,不是最大連接數目 // int listen( // __in SOCKET s, // __in int backlog // ); SOCKADDR_IN addrClient;//與sockaddr等價的數據結構 int len = sizeof(SOCKADDR); // struct sockaddr // { // unsignedshort sa_family; // char sa_data[14]; // }; while(1) { SOCKET sockConn = accept(sockSrv,(SOCKADDR *) &addrClient,&len);//從連接請求隊列中獲得連接信息,創建新的套接字,並返回該套接字的文件描述符。新創建的套接字用於服務器與客戶機的通信,而原來的套接字仍然處於監聽狀態。 //accept一樣主要用於服務器端,第二個參數同樣可以使用SOCKADDR_IN來替代,但是注意,這里,該參數是用來存儲建立連接時候客戶端的相關信息。 // SOCKET accept( // SOCKET s, // struct sockaddr FAR* addr, // int FAR* addrlen // ); //服務程序調用accept函數從處於監聽狀態的流套接字s的客戶連接請求隊列中取出排在最前的一個客戶請求, //並且創建一個新的套接字來與客戶套接字創建連接通道,如果連接成功,就返回新創建的套接字的描述符, //以后與客戶套接字交換數據的是新創建的套接字;如果失敗就返回 INVALID_SOCKET。 //該函數的第一個參數指定處於監聽狀態的流套接字; //操作系統利用第二個參數來返回所連接的客戶進程的協議地址(由cliaddr指針所指); //操作系統利用第三個參數來返回該地址(參數二)的大小。 //如果我們對客戶協議地址不感興趣,那么可以把cliaddr和addrlen均置為空指針NULL。 char sendbuffer[100]; sprintf(sendbuffer,"welcome %s here",inet_ntoa(addrClient.sin_addr));//將一個IP轉換成一個互聯網標准點分格式的字符串。 原型:char FAR * inet_ntoa( struct in_addr in); send(sockConn,sendbuffer,strlen(sendbuffer)+1,0);//該函數用來相互發送數據,但是需要注意的是,服務器端使用該函數時候,第一個參數為accept函數所返回的socket結構值。 // int send( // SOCKET s, // const char FAR* buf, // int len, // int flags // ); char recvchar[100]; recv(sockConn,recvchar,100,0);//本函數用於已連接的數據報或流式套接口s進行數據的接收 printf("%s\n",recvchar); closesocket(sockConn);//本函數關閉一個套接口。更確切地說,它釋放套接口描述字s,以后對s的訪問均以WSAENOTSOCK錯誤返回。 } return 0; }
// socket_client.cpp : 定義控制台應用程序的入口點。 // #pragma comment(lib, "Ws2_32.lib") #include "stdafx.h" #include <WinSock2.h> #include <stdio.h> #include <iostream> #include <WS2tcpip.h> #include <process.h> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(1,1); err = WSAStartup(wVersionRequested,&wsaData);//加載Winsocket DLL if(err != 0) { return 0; } if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { WSACleanup(); return 0; } SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);//創建套接字 SOCKADDR_IN addrSrv;//socketAddress socket端口 //服務器端口配置 addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); ////作為客戶端,你要連接【connect】到遠端的服務器,也是要指定遠端服務器的(ip, port)對。 addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(6000); connect(sockClient,(SOCKADDR *) &addrSrv,sizeof(SOCKADDR)); char recvBuffer[100]; recv(sockClient,recvBuffer,100,0);//接收服務器數據,存入recvBuffer printf("%s\n",recvBuffer);//打印服務器數據 send(sockClient,"This is Kary",strlen("This is Kary")+1,0);//向服務器發送數據"This is Kary" closesocket(sockClient); WSACleanup(); scanf("%d",&err); return 0; }
出現問題:不加#pragma comment(lib, "Ws2_32.lib")出現錯誤
