Socket的概念:
Socket,即套接字,用於描述地址和端口,是一個通信鏈的句柄。
應用程序通過Socket像網絡發出請求或者回應。
什么事客戶/服務器模式:
在TCP/IP網絡應用中,通信的兩個進程相互作用的主要模式是客戶/服務器模式,即客戶端向服務器發出請求,服務器接收請求后,提供相應的服務。客戶/服務器模式的建立基於以下兩點:
1:建立網絡的起因是網絡中軟硬件資源、運算能力和信息不均等,需要共享,從而造就擁有眾多資源的主機提供服務,資源較少的客戶請求服務這一非對等作用
2:網間進程通信完全是異步的,相互通信的進程間既不存在父子關系,又不共享內存緩沖區
因此需要一種機制為希望通信的進程間建立聯系,為二者的數據交換提供同步,這就是基於客戶/服務端模式的TCP/IP
(這部分內容摘自socket編程原理)
需要做什么:
客戶端:建立Socket,通過端口號和地址確定目標服務器,使用Connect連接到服務器,send發送消息,等待處理,通信完成后調用CloseSocket關閉Socket
服務端:建立Socket,聲明自身的端口號和地址並綁定到Socket,使用Listen打開監聽,然后不斷用Accept去查看是否有連接,如果有,捕獲Socket,並通過Recv獲取消息的內容,通信完成后調用CloseSocket關閉這個對應Accept到的Socket,如果不再需要等待任何客戶端連接,那么用CloseSocket關閉掉自身的Socket
代碼示例
客戶端:這里創建一個CLientNet類,用於表示一個客戶
#pragma once /* ClinetNet.h: 用於表示一個客戶的網絡操作 */ #include <stdio.h> #include<windows.h> #pragma comment(lib, "Ws2_32.lib") class CClientNet { public: //連接上指定服務器 int Connect(int port,const char* address); //發送信息 int SendMsg(const char* msg,int len); //關閉 void Close(); private: SOCKET m_sock; };
/* CLientNet.cpp */ #include "ClientNet.h" int CClientNet::Connect( int port,const char* address ) { int rlt = 0; //用於記錄錯誤信息並輸出 int iErrMsg; //啟動WinSock WSAData wsaData; iErrMsg = WSAStartup(MAKEWORD(1,1),&wsaData); if (iErrMsg != NO_ERROR) //有錯誤 { printf("failed with wsaStartup error : %d\n",iErrMsg); rlt = 1; return rlt; } //創建Socket m_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if (m_sock == INVALID_SOCKET) //創建Socket失敗 { printf("socket failed with error : %d\n",WSAGetLastError()); rlt = 2; return rlt; } //目標服務器數據 sockaddr_in sockaddrServer; sockaddrServer.sin_family = AF_INET; sockaddrServer.sin_port = port; sockaddrServer.sin_addr.s_addr = inet_addr(address); //連接,sock與目標服務器連接 iErrMsg = connect(m_sock,(sockaddr*)&sockaddrServer,sizeof(sockaddrServer)); if (iErrMsg < 0) { printf("connect failed with error : %d\n",iErrMsg); rlt = 3; return rlt; } return rlt; } int CClientNet::SendMsg(const char* msg,int len) { int rlt = 0; int iErrMsg = 0; //發送消息,指定sock發送消息 iErrMsg = send(m_sock,msg,len,0); if (iErrMsg < 0) //發送失敗 { printf("send msg failed with error : %d\n",iErrMsg); rlt = 1; return rlt; } return rlt; } void CClientNet::Close() { closesocket(m_sock); }
#include "ClientNet.h" void main() { CClientNet client; //連接到127.0.0.1(即本地),端口號為8888的服務端 printf("Connect\n"); client.Connect(8888,"127.0.0.1"); //發送消息 printf("send msg\n"); client.SendMsg("hello\0",sizeof("hello\0")); //關閉socket printf("close\n"); client.Close(); system("pause"); }
服務端代碼:
#pragma once /* ServerNet.h 用於表示服務端的網絡操作 */ #include <stdio.h> #include <winsock.h> #pragma comment (lib,"ws2_32.lib") class CServerNet { public: //初始化服務器,返回0表示成功 int Init(const char* address,int port); //更新數據 void Run(); private: SOCKET m_sock; };
/* ServerNet.cpp */ #include "ServerNet.h" int CServerNet::Init( const char* address,int port ) { int rlt = 0; //用於記錄錯誤信息,並輸出 int iErrorMsg; //初始化WinSock WSAData wsaData; iErrorMsg = WSAStartup(MAKEWORD(1,1),&wsaData); if (iErrorMsg != NO_ERROR) { //初始化WinSock失敗 printf("wsastartup failed with error : %d\n",iErrorMsg); rlt = 1; return rlt; } //創建服務端Socket m_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if (m_sock == INVALID_SOCKET) { //創建Socket異常 printf("socket failed with error : %d\n",WSAGetLastError()); rlt = 2; return rlt; } //聲明信息 sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = port; serverAddr.sin_addr.s_addr = inet_addr(address); //綁定 iErrorMsg = bind(m_sock,(sockaddr*)&serverAddr,sizeof(serverAddr)); if (iErrorMsg < 0) { //綁定失敗 printf("bind failed with error : %d\n",iErrorMsg); rlt = 3; return rlt; } return rlt; } void CServerNet::Run() { //公開連接 listen(m_sock,5); sockaddr_in tcpAddr; int len = sizeof(sockaddr); SOCKET newSocket; char buf[1024]; int rval; do { //接收信息 newSocket = accept(m_sock,(sockaddr*)&tcpAddr,&len); if (newSocket == INVALID_SOCKET) { //非可用socket } else { //新socket連接 printf("new socket connect : %d\n",newSocket); //消息處理 do { printf("process\n"); //接收數據 memset(buf,0,sizeof(buf)); rval = recv(newSocket,buf,1024,0); if (rval == SOCKET_ERROR) //這應該是個異常,當客戶端沒有調用closeSocket就直接退出游戲的時候,將會進入這里 printf("recv socket error\n"); if (rval == 0) //recv返回0表示正常退出 printf("ending connection"); else //顯示接收到的數據 printf("recv %s\n",buf); }while(rval != 0); //關閉對應Accept的socket closesocket(newSocket); } } while (1); //關閉自身的Socket closesocket(m_sock); }
#include "ServerNet.h" int main(int argc, char **argv) { CServerNet serverNet; int iRlt = serverNet.Init("127.0.0.1",8888); if (iRlt == 0) { printf("init ok...\n"); serverNet.Run(); } else printf("serverNet init failed with error : %d\n",iRlt); system("pause"); }
#pragma once/*ClinetNet:用於表示一個客戶的網絡操作*/#include <stdio.h>#include<windows.h>#pragma comment(lib, "Ws2_32.lib")class CClientNet{public://連接上指定服務器int Connect(int port,const char* address);//發送信息int SendMsg(const char* msg,int len);//關閉void Close();private:SOCKET m_sock;};