當(dāng)初寫(xiě)第一個(gè)網(wǎng)絡(luò)程序的時(shí)候,就是通過(guò)搜索,找各種實(shí)例把程序拼湊出來(lái)的,并沒(méi)有進(jìn)行深入的理解。這個(gè)東西用了這么多年,是該來(lái)沉淀一下了,也檢驗(yàn)一下自己對(duì)這塊知識(shí)的掌握程度。
可以說(shuō),一個(gè)典型的網(wǎng)絡(luò)程序是離不開(kāi)socket的,它是系統(tǒng)提供給開(kāi)發(fā)者們進(jìn)行網(wǎng)絡(luò)操作的強(qiáng)大武器。
socket又叫套接字,是一系列網(wǎng)絡(luò)操作的API。它的實(shí)現(xiàn)和表現(xiàn)形式又會(huì)根據(jù)系統(tǒng)平臺(tái)的不同、編程語(yǔ)言的不同而有所區(qū)別,但是實(shí)現(xiàn)的功能都是一致的。
socket的創(chuàng)建:
windows平臺(tái)C版本
SOCKET s = socket(AF_.NET, SOCK_STREAM, IPPROTO_TCP);
C#版本
Socket s=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
上面兩個(gè)套接字都是以TCP協(xié)議創(chuàng)建的,socket中的三個(gè)參數(shù)分別指定了IP層協(xié)議類型(IPV4和IPV6),數(shù)據(jù)交互格式(字節(jié)流,數(shù)據(jù)報(bào)等),傳輸層協(xié)議(TCP和UDP)。
套接字在進(jìn)程中是以文件描述符的形式存在的,其實(shí)就是一個(gè)數(shù)字,用來(lái)唯一標(biāo)識(shí)一個(gè)套接字,以不同的形式提供給開(kāi)發(fā)者使用。
socket的地址和端口:
地址指定了數(shù)據(jù)交互節(jié)點(diǎn)的設(shè)備,如果一臺(tái)設(shè)備有多塊網(wǎng)卡,則地址指定的就是某一塊網(wǎng)卡;端口標(biāo)識(shí)的是設(shè)備上的某個(gè)進(jìn)程。來(lái)自網(wǎng)絡(luò)的數(shù)據(jù)到達(dá)網(wǎng)卡后,向上經(jīng)過(guò)數(shù)據(jù)鏈路層,網(wǎng)絡(luò)層,到達(dá)傳輸層,然后根據(jù)端口確定需要將數(shù)據(jù)交給哪個(gè)進(jìn)程。
數(shù)據(jù)接收:
數(shù)據(jù)接收是由進(jìn)程調(diào)用socket的接收函數(shù)發(fā)起的。該函數(shù)會(huì)向系統(tǒng)提交接收數(shù)據(jù)的IO請(qǐng)求,該請(qǐng)求的內(nèi)容包括應(yīng)用層存放數(shù)據(jù)的緩沖區(qū),請(qǐng)求的數(shù)據(jù)尺寸,在緩沖區(qū)中存放數(shù)據(jù)的偏移值。
C# 發(fā)起數(shù)據(jù)接收請(qǐng)求:
int rcvLen=m_socket.Receive(buf, offset, size, SocketFlags.None);
其中buf為數(shù)據(jù)緩沖區(qū),offset為數(shù)據(jù)接收時(shí)在buf中存放數(shù)據(jù)的起始地址,size為此次請(qǐng)求的字節(jié)大小,rcvLen為此次接收成功后實(shí)際接收到的字節(jié)長(zhǎng)度。
數(shù)據(jù)發(fā)送:
數(shù)據(jù)的發(fā)送是進(jìn)程調(diào)用socket的發(fā)送函數(shù)發(fā)起的。該函數(shù)會(huì)向系統(tǒng)提交發(fā)送數(shù)據(jù)的IO請(qǐng)求,向指定的節(jié)點(diǎn)發(fā)送數(shù)據(jù),請(qǐng)求的內(nèi)容包括發(fā)送的數(shù)據(jù)內(nèi)容,長(zhǎng)度等。
C# 發(fā)起的數(shù)據(jù)發(fā)送請(qǐng)求(TCP):
int sndLen= m_socket.Send(buf);
其中buf為發(fā)送的內(nèi)容字節(jié)碼數(shù)組,sndLen為實(shí)際發(fā)送的字節(jié)長(zhǎng)度。
TCP Socket:
TCP是傳輸層的協(xié)議,該協(xié)議有一定的復(fù)雜性,目的是完成數(shù)據(jù)的可靠傳輸。采用TCP實(shí)現(xiàn)的網(wǎng)絡(luò)應(yīng)用,分為服務(wù)端和客戶端兩種端點(diǎn)類型,每個(gè)端的實(shí)現(xiàn)都對(duì)應(yīng)著不同socket調(diào)用函數(shù)。
服務(wù)端可以接受客戶端的連接請(qǐng)求,連接完成后就可以進(jìn)行雙向通信。
建立服務(wù)端,需要?jiǎng)?chuàng)建服務(wù)端套接字,為該套接字綁定服務(wù)地址和端口,打開(kāi)被動(dòng)監(jiān)聽(tīng),開(kāi)始等待連接。
C#版本的服務(wù)端建立代碼
m_socketServer = CreateSocket_IPV4();
m_socketServer.Bind(CreateEndPoint(port, null));
m_socketServer.Listen(10);
m_conns = new List<IConnection>();
Task.Factory.StartNew(() =>
{
while (m_running)
{
var s = m_socketServer.Accept();
var conn = CreateConnection(s);
m_conns.Add(conn);
OnConnected?.Invoke(conn);
}
});
客戶端的操作相對(duì)簡(jiǎn)單一些,只需要?jiǎng)?chuàng)建套接字然后向服務(wù)端發(fā)起連接請(qǐng)求。
C#版本的客戶端代碼
var s = CreateSocket_IPV4();
s.Connect(IPAddress.Parse(ip), port);
socket涵蓋的內(nèi)容是非常豐富的,我們?cè)诖酥皇悄贸鲆恍┐硇缘膬?nèi)容來(lái)記述。