1. 程式人生 > >unity3d基礎學習筆記10:socket概述

unity3d基礎學習筆記10:socket概述

對TCP/IP、UDP、Socket程式設計這些詞你不會很陌生吧?隨著網路技術的發展,這些詞充斥著我們的耳朵。那麼我想問:

1. 什麼是TCP/IP、UDP?
2. Socket在哪裡呢?
3. Socket是什麼呢?
4. 你會使用它們嗎?

什麼是TCP/IP、UDP?

TCP/IP(TransmissionControl Protocol/Internet Protocol)即傳輸控制協議/網間協議,是一個工業標準的協議集,它是為廣域網(WANs)設計的。
UDP(User Data Protocol,使用者資料報協議)是與TCP相對應的協議。它是屬於TCP/IP協議族中的一種。
這裡有一張圖,表明了這些協議的關係。



圖1

TCP/IP協議族包括運輸層、網路層、鏈路層。現在你知道TCP/IP與UDP的關係了吧。
Socket在哪裡呢?
在圖1中,我們沒有看到Socket的影子,那麼它到底在哪裡呢?還是用圖來說話,一目瞭然。



圖2

原來Socket在這裡。
Socket是什麼呢?
Socket是應用層與TCP/IP協議族通訊的中間軟體抽象層,它是一組介面。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket介面後面,對使用者來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協議。
你會使用它們嗎?
前人已經給我們做了好多的事了,網路間的通訊也就簡單了許多,但畢竟還是有挺多工作要做的。以前聽到Socket程式設計,覺得它是比較高深的程式設計知識,但是隻要弄清Socket程式設計的工作原理,神祕的面紗也就揭開了。
一個生活中的場景。你要打電話給一個朋友,先撥號,朋友聽到電話鈴聲後提起電話,這時你和你的朋友就建立起了連線,就可以講話了。等交流結束,結束通話電話結束此次交談。 生活中的場景就解釋了這工作原理,也許TCP/IP協議族就是誕生於生活中,這也不一定。

圖3

先從伺服器端說起。伺服器端先初始化Socket,然後與埠繫結(bind),對埠進行監聽(listen),呼叫accept阻塞,等待客戶端連線。在這時如果有個客戶端初始化一個Socket,然後連線伺服器(connect),如果連線成功,這時客戶端與伺服器端的連線就建立了。客戶端傳送資料請求,伺服器端接收請求並處理請求,然後把迴應資料傳送給客戶端,客戶端讀取資料,最後關閉連線,一次互動結束。
在這裡我就舉個簡單的例子,我們走的是TCP協議這條路(見圖2)。例子用MFC編寫,執行的介面如下:



圖4



圖5

在客戶端輸入伺服器端的IP地址和傳送的資料,然後按傳送按鈕,伺服器端接收到資料,然後迴應客戶端。客戶端讀取回應的資料,顯示在介面上。
下面是接收資料和傳送資料的函式:

int Receive(SOCKET fd,char *szText,int len)

{
int cnt;
int rc;
cnt=len;

while(cnt>0)
{
rc=recv(fd,szText,cnt,0);
if(rc==SOCKET_ERROR)
{
return -1;
}

if(rc==0)

return len-cnt;

szText+=rc;

cnt-=rc;

}

return len;

}

int Send(SOCKET fd,char *szText,int len)
{

int cnt;

int rc;

cnt=len;

while(cnt>0)

{

rc=send(fd,szText,cnt,0);

if(rc==SOCKET_ERROR)

{

return -1;

}

if(rc==0)

return len-cnt;

szText+=rc;

cnt-=rc;

}

return len;

}

伺服器端:

在伺服器端,主要是啟動Socket和監聽執行緒。

#define DEFAULT_PORT 2000

void CServerDlg::OnStart()

{

sockaddr_in local;

DWORD dwThreadID = 0;

local.sin_family=AF_INET;

//設定的埠為DEFAULT_PORT。

local.sin_port=htons(DEFAULT_PORT);

//IP地址設定成INADDR_ANY,讓系統自動獲取本機的IP地址。

local.sin_addr.S_un.S_addr=INADDR_ANY;

//初始化Socket

m_Listening = socket(AF_INET,SOCK_STREAM,0);

if(m_Listening == INVALID_SOCKET)

{

return ;

}

//將本地地址繫結到所建立的套接字上

if(bind(m_Listening,(LPSOCKADDR)&local,sizeof(local))== SOCKET_ERROR )

{

closesocket(m_Listening);

return ;

}

//建立監聽執行緒,這樣也能響應介面上操作。

m_hListenThread =::CreateThread(NULL,0,ListenThread,this,0,&dwThreadID);

m_StartBtn.EnableWindow(FALSE);

m_StopBtn.EnableWindow(TRUE);

}

監聽執行緒函式:
DWORD WINAPI CServerDlg::ListenThread(LPVOID lpparam)
{

CServerDlg* pDlg = (CServerDlg*)lpparam;

if(pDlg == NULL)

return 0;

SOCKET Listening = pDlg->m_Listening;

//開始監聽是否有客戶端連線。

if(listen(Listening,40) == SOCKET_ERROR)

{

return 0;

}

char szBuf[MAX_PATH];

//初始化

memset(szBuf,0,MAX_PATH);

while(1)

{

SOCKET ConnectSocket;

sockaddr_in ClientAddr;

int nLen = sizeof(sockaddr);

//阻塞直到有客戶端連線,不然多浪費CPU資源。

ConnectSocket =accept(Listening,(sockaddr*)&ClientAddr,&nLen);

//都到客戶端的IP地址。

char *pAddrname = inet_ntoa(ClientAddr.sin_addr);

pDlg->Receive(ConnectSocket,szBuf,100);

//介面上顯示請求資料。

pDlg->SetRequestText(szBuf);

strcat(szBuf," :我是老貓,收到(");

strcat(szBuf,pAddrname);

strcat(szBuf,")");

//向客戶端傳送迴應資料

pDlg->Send(ConnectSocket,szBuf,100);

}

return 0;

}

伺服器端一直在監聽是否有客戶端連線,如有連線,處理客戶端的請求,給出迴應,然後繼續監聽。

客戶端:

客戶端的傳送函式:

#define DEFAULT_PORT 2000

void CClientDlg::OnSend()

{

DWORD dwIP = 0;

TCHAR szText[MAX_PATH];

memset(szText,0,MAX_PATH);

m_IP.GetWindowText(szText,MAX_PATH);

//把字串形式的IP地址轉成IN_ADDR結構需要的形式。

dwIP = inet_addr(szText);

m_RequestEdit.GetWindowText(szText,MAX_PATH);

sockaddr_in local;

SOCKET socketTmp;

//必須是AF_INET,表示該socket在Internet域中進行通訊

local.sin_family=AF_INET;

//埠號

local.sin_port=htons(DEFAULT_PORT);

//伺服器的IP地址。

local.sin_addr.S_un.S_addr=dwIP;

////初始化Socket

socketTmp=socket(AF_INET,SOCK_STREAM,0);

//連線伺服器

if(connect(socketTmp,(LPSOCKADDR)&local,sizeof(local))< 0)

{

closesocket(socketTmp);

MessageBox("連線伺服器失敗。");

return ;

}

//傳送請求,為簡單隻發100位元組,在伺服器端也規定100位元組。

Send(socketTmp,szText,100);

//讀取伺服器端返回的資料。

memset(szText,0,MAX_PATH);

//接收伺服器端的迴應。

Receive(socketTmp,szText,100);

TCHAR szMessage[MAX_PATH];

memset(szMessage,0,MAX_PATH);

strcat(szMessage,szText);

//介面上顯示迴應資料。

m_ReplyBtn.SetWindowText(szMessage);

closesocket(socketTmp);

}

客戶端就一個函式完成了一次通訊。在這裡IP地址為何用127.0.0.1呢?使用這個IP地址,伺服器端和客戶端就能執行在同一臺機器上,這樣除錯方便多了。當然你可以在你朋友的機器上執行Server程式(本人在區域網中測試過),在自己的機器上執行Client程式,當然輸入的IP地址就該是你朋友機器的IP地址了。