這幾天博主花了4天時間去學習,整理socket通訊。大致懂了一點,現在我來總結一下關於c#socket通訊的原理
首先我們要知道網路中的程序是如何進行通訊的
在本地可以通過程序pid來唯一標識乙個程序,但是在網路中這是行不通的。其實tcp/ip協議族已經幫我們解決了這個問題,網路層的「ip位址」
可以唯一標識網路中的主機,而
傳輸層的「協議+埠」
可以唯一標識主機中的應用程式(程序)。這樣利用三元組(ip位址,協議,埠)就可以標識網路的程序了,網路中的程序通訊就可以利用這個標誌與其它程序進行互動。
什麼是socket? 通俗一點的說法
網路上的兩個程式通過乙個雙向的通訊連線實現資料的交換,這個連線的一端稱為乙個socket。
建立網路通訊連線至少要一對埠號(socket)。socket本質是程式設計介面(api),對tcp/ip的封裝,tcp/ip也要提供可供程式設計師做網路開發所用的介面,這就是socket程式設計介面;http是轎車,提供了封裝或者顯示資料的具體形式;socket是發動機,提供了網路通訊的能力。
socket的英文原義是「孔」或「插座」。作為bsd unix的
程序通訊機制,取後一種意思。通常也稱作"
套接字",用於描述ip位址和埠,是乙個通訊鏈的控制代碼,可以用來實現不同虛擬機器或不同計算機之間的通訊。在internet上的
主機一般執行了多個服務軟體,同時提供幾種服務。每種服務都開啟乙個socket,並繫結到乙個埠上,不同的埠對應於不同的服務。socket正如其英文原義那樣,像乙個多孔插座。一台主機猶如布滿各種插座的房間,每個插座有乙個編號,有的插座提供220伏交流電, 有的提供110伏交流電,有的則提供有線電視節目。 客戶軟體將插頭插到不同編號的插座,就可以得到不同的服務。
socket有關的函式:
intsocket(int domain, int type, int protocol);
· domain:即協議域,又稱為協議族(family)。常用的協議族有,
af_inet
、af_inet6
、af_local
(或稱af_unix
,unix域socket)、
af_route
等等。協議族決定了socket的位址型別,在通訊中必須採用對應的位址,如af_inet決定了要用ipv4位址(32位的)與埠號(16位的)的組合、af_unix決定了要用乙個絕對路徑名作為位址。
· type:指定socket型別。常用的socket型別有,
sock_stream
、sock_dgram
、sock_raw
、sock_packet
、sock_seqpacket
等等(socket的型別有哪些?)。
· protocol:故名思意,就是指定協議。常用的協議有,
ipproto_tcp
、ipptoto_udp
、ipproto_sctp
、ipproto_tipc
等,它們分別對應tcp傳輸協議、udp傳輸協議、stcp傳輸協議、tipc傳輸協議(這個協議我將會單獨開篇討論!)。
正如上面所說bind()函式把乙個位址族中的特定位址賦給socket。例如對應
af_inet
、af_inet6
就是把乙個ipv4或ipv6位址和埠號組合賦給socket。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:即socket描述字,它是通過socket()函式建立了,唯一標識乙個socket。bind()函式就是將給這個描述字繫結乙個名字。
通常伺服器在啟動的時候都會繫結乙個眾所周知的位址(如ip位址+埠號),用於提供服務,客戶就可以通過它來接連伺服器;而客戶端就不用指定,有系統自動分配乙個埠號和自身的ip位址組合。這就是為什麼通常伺服器端在listen之前會呼叫bind(),而客戶端就不會呼叫,而是在connect()時由系統隨機生成乙個。
如果作為乙個伺服器,在呼叫
socket()
、bind()
之後就會呼叫
listen()
來監聽這個socket,如果客戶端這時呼叫
connect()
發出連線請求,伺服器端就會接收到這個請求。
int
listen
(int sockfd, int backlog);int
connect
(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen函式的第乙個引數即為要監聽的socket描述字,第二個引數為相應socket可以排隊的最大連線個數。socket()函式建立的socket預設是乙個主動型別的,listen函式將socket變為被動型別的,等待客戶的連線請求。
connect函式的第乙個引數即為客戶端的socket描述字,第二引數為伺服器的socket位址,第三個引數為socket位址的長度。客戶端通過呼叫connect函式來建立與tcp伺服器的連線。
tcp伺服器端依次呼叫
socket()
、bind()
、listen()
之後,就會監聽指定的socket位址了。tcp客戶端依次呼叫
socket()
、connect()
之後就向
tcp伺服器傳送了乙個連線請求。tcp伺服器監聽到這個請求之後,就會呼叫
accept()
函式取接收請求,這樣連線就建立好了。之後就可以開始網路i/o操作了,即類同於普通檔案的讀寫i/o操作。
int
accept
(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept函式的第乙個引數為伺服器的socket描述字,第二個引數為指向struct sockaddr *的指標,用於返回客戶端的協議位址,第三個引數為協議位址的長度。如果accpet成功,那麼其返回值是由核心自動生成的乙個全新的描述字,代表與返回客戶的tcp連線。
注意:accept的第乙個引數為伺服器的socket描述字,是伺服器開始呼叫socket()函式生成的,稱為
監聽socket描述字
;而accept函式返回的是
已連線的socket描述字
。乙個伺服器通常通常僅僅只建立乙個監聽socket描述字,它在該伺服器的生命週期內一直存在。核心為每個由伺服器程序接受的客戶連線建立了乙個已連線socket描述字,當伺服器完成了對某個客戶的服務,相應的已連線socket描述字就被關閉。
萬事具備只欠東風,至此伺服器與客戶已經建立好連線了。可以呼叫網路i/o進行讀寫操作了,即實現了網咯中不同程序之間的通訊!網路i/o操作有下面幾組:
· read()/write()
· recv()/send()
· readv()/writev()
· recvmsg()/sendmsg()
· recvfrom()/sendto()
我推薦使用
recvmsg()/sendmsg()函式,
在伺服器與客戶端建立連線之後,會進行一些讀寫操作,完成了讀寫操作就要關閉相應的socket描述字,好比操作完開啟的檔案要呼叫fclose關閉開啟的檔案。
#include int
close
(int fd);
close乙個tcp socket的預設行為時把該socket標記為以關閉,然後立即返回到呼叫程序。該描述字不能再由呼叫程序使用,也就是說不能再作為read或write的第乙個引數。
注意:close操作只是使相應socket描述字的引用計數-1,只有當引用計數為0的時候,才會觸發tcp客戶端向伺服器傳送終止連線請求。
講完了socket有關的函式,相信你對socket有一定的了解了,下面講講臭名昭著的tcp三次握手和tcp四次放手
先上圖
客戶端先傳送乙個syn j 的報文,
伺服器接收 向客戶端傳送乙個確認的 ack j+1 ,並響應乙個syn k的報文,
客戶端接收到 向伺服器端確認乙個ack k+1
從圖中可以看出,當客戶端呼叫
connect
時,觸發了連線請求,向伺服器傳送了syn j包,這時connect進入阻塞狀態;伺服器監聽到連線請求,即收到syn j包,呼叫accept
函式接收請求向客戶端傳送syn k ,ack j+1,這時accept進入阻塞狀態;客戶端收到伺服器的syn k ,ack j+1之後,這時connect返回,並對syn k進行確認;伺服器收到ack k+1時,accept返回,至此三次握手完畢,連線建立。
總結:客戶端的connect在三次握手的第二個次返回,而伺服器端的accept在三次握手的第三次返回。
四次放手圖
某個應用程序首先呼叫
close
主動關閉連線,這時tcp傳送乙個fin m;
另一端接收到fin m之後,執行被動關閉,對這個fin進行確認。它的接收也作為檔案結束符傳遞給應用程序,因為fin的接收意味著應用程序在相應的連線上再也接收不到額外資料;
一段時間之後,接收到檔案結束符的應用程序呼叫
close
關閉它的socket。這導致它的tcp也傳送乙個fin n;
接收到這個fin的源傳送端tcp對它進行確認。
C socket程式設計基礎 理論篇
對於socket在這裡我不想究其歷史,我只想說其時它是一種程序通訊的方式,簡言之就是呼叫這個網路庫的一些api函式就能實現分布在不同主機的相關程序之間的資料交換.socket中首先我們要理解如下幾個定義概念 二是埠號 用來標識本地通訊程序,方便os提交資料.就是說程序指定了對方程序的網路ip,但這個...
C socket程式設計基礎 理論篇
對於socket在這裡我不想究其歷史,我只想說其時它是一種程序通訊的方式,簡言之就是呼叫這個網路庫的一些api函式就能實現分布在不同主機的相關程序之間的資料交換.socket中首先我們要理解如下幾個定義概念 二是埠號 用來標識本地通訊程序,方便os提交資料.就是說程序指定了對方程序的網路ip,但這個...
C Socket通訊例子
建立兩個工程檔案,server和client include include pragma comment lib,ws2 32.lib 靜態加入乙個lib檔案 pragma warning disable 4996 using namespace std intmain 繫結ip和埠 配置監聽位址...