閱讀目錄:數據庫
在現今軟件開發中,網絡編程是很是重要的一部分,本文簡要介紹下網絡編程的概念和實踐。
Socket是一種網絡編程接口,它是對傳輸層TCP、UDP通訊協議的一層封裝,經過友好的API暴露出去,方便在進程或多臺機器間進行網絡通訊。編程
在網絡編程中分客戶端和服務端兩種角色,好比經過打開瀏覽器訪問到掛在Web軟件上的網頁,從程序角度上來看,即客戶端(瀏覽器)發起了一個Socket請求到服務器端,服務器把網頁內容返回到瀏覽器解析後展現。在客戶端和服務端數據通訊前,會進行三次確認纔會正式創建鏈接,也便是三次握手。 windows
TCP/IP協議是網絡間通訊的基礎協議,在不一樣編程語言及不一樣操做系統下暴露的Socket接口用法也大同小異,僅是其內部實現有所不一樣,好比Linux下的epoll和windows下的IOCP。數組
IPEndPoint ip = new IPEndPoint(IPAddress.Any, 6389); Socket listenSocket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listenSocket.Bind(ip); listenSocket.Listen(100); listenSocket.Accept();
listen函數中有個int類型參數,它表示最大等待處理鏈接的數量,表示已創建鏈接但還未處理的數量,每調用Accept函數一下即從這個等待隊列中拿出一個鏈接。 一般服務端要服務多個客戶端請求的鏈接,因此會循環從等待隊列中拿出鏈接,進行接收發送。 瀏覽器
while (true) { var accept= listenSocket.Accept(); accept.Receive(); accept.Send(); }
上面的服務端程序處理接收和發送消息都是在當前線程下完成的,這意味着要處理完一個客戶端鏈接後才能去處理下一個鏈接,若是當前鏈接是進行數據庫或者文件讀取寫入等IO操做,那會極大浪費服務器的CPU資源,下降了服務器吞吐量。服務器
while (true) { var accept = listenSocket.Accept(); ThreadPool.QueueUserWorkItem((obj) => { byte[] receive = new byte[100]; accept.Receive(receive); byte[] send = new byte[100]; accept.Send(receive); }); }
如例子中,當監聽到有新鏈接請求過來時,調用Accept()取出當前鏈接的socket,使用新的線程去處理接收和發送信息,這樣服務端就能實現併發處理多個客戶端了。 上述代碼中,在高併發下實際上是有問題的,若是客戶端鏈接請求成千上萬個,那線程數量也會有這麼多,每一個線程的棧空間都須要消耗部份內存,再加上線程上下文切換,容易致使服務器負載太高,吞吐量大大降低,嚴重時會引發宕機。 當前例子中使用系統ThreadPool的話,線程數量會固定在一個數量上,默認是1000,不會無限制開線程,會把處理超出線程數量的請求放到線程池中的隊列上面。
網絡
在unix下相似的實現有2種:多線程
fork一個新進程去處理客戶端的鏈接:併發
var connfd = Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len); var m = fork(); if(m == 0) { //do something }
建立一個新的線程處理限流:socket
var *clientsockfd = accept(serversockfd,(struct sockaddr *)&clientaddress, (socklent *)&clientlen); if(pthreadcreate(&thread, NULL, recdata, clientsockfd)!=0) { //do something }
上述例子中使用的便是該模型,使用起來簡單方便。
while (true) { var accept = listenSocket.Accept(); byte[] receive = new byte[100]; accept.Receive(receive); byte[] send = new byte[100]; accept.Send(receive); }
從調用Receive函數起到接受到客戶端發過來的數據期間,該函數會一直阻塞等待着,這個阻塞期間處理流程以下:
至此處理成功,開始處理下一個鏈接請求。 調用發送函數一樣會阻塞在當前,而後把用戶緩衝區(send字節數組)數據拷貝到內核中TCP發送緩衝區中。 TCP的發送緩衝區也有必定的大小限制,若是發送的數據大於該限制,send函數會一直等待發送緩衝區有空閒時徹底拷貝完纔會返回,繼續處理後續鏈接請求。