1. 程式人生 > >socket網路程式設計之多執行緒阻塞IO例項

socket網路程式設計之多執行緒阻塞IO例項

先介紹一下網路層次結構、socket與TCP/UDP之間的關係。同步、非同步,阻塞、非阻塞的區別。

網路由下往上分為物理層、資料鏈路層、網路層、傳輸層、會話層、表示層和應用層。

IP 協議對應於網路層,
TCP協議對應於傳輸層,
HTTP協議對應於應用層,
三者從本質上來說沒有可比性,
socket則是對TCP/IP協議的封裝和應用。
可以說,TPC/IP協議是傳輸層協議,主要解決資料如何在網路中傳輸,而HTTP是應用層協議,主要解決如何包裝資料

socket是對TCP/IP協議的封裝,Socket本身並不是協議,而是一個呼叫介面(API),
通過Socket,我們才能使用TCP/IP協議。
實際上,Socket跟TCP/IP協議沒有必然的聯絡。Socket程式設計介面在設計的時候,就希望也能適應其他的網路協議。

所以說,Socket的出現只是使得程式設計師更方便地使用TCP/IP協議棧而已,是對TCP/IP協議的抽象,
從而形成了我們知道的一些最基本的函式介面,比如create、 listen、connect、accept、send、read和write等等

實際上,傳輸層的TCP是基於網路層的IP協議的,而應用層的HTTP協議又是基於傳輸層的TCP協議的,
而Socket本身不算是協議,就像上面所說,它只是提供了一個針對TCP或者UDP程式設計的介面

TCP連線的三次握手:
第一次握手:客戶端傳送syn包(syn=j)到伺服器,並進入SYN_SEND狀態,等待伺服器確認; 
第二次握手:伺服器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也傳送一個SYN包(syn=k),即SYN+ACK包,此時伺服器進入SYN_RECV狀態;

第三次握手:客戶端收到伺服器的SYN+ACK包,向伺服器傳送確認包ACK(ack=k+1),此包傳送完畢,客戶端和伺服器進入ESTABLISHED狀態,完成三次握手。   
握手過程中傳送的包裡不包含資料,三次握手完畢後,客戶端與伺服器才正式開始傳送資料
斷開連線時伺服器和客戶端均可以主動發起斷開TCP連線的請求,斷開過程需要經過“四次握手”

TCP是面向連結的,雖然說網路的不安全不穩定特性決定了多少次握手都不能保證連線的可靠性,
但TCP的三次握手在最低限度上(實際上也很大程度上保證了)保證了連線的可靠性;
而UDP不是面向連線的,UDP傳送資料前並不與對方建立連線,對接收到的資料也不傳送確認訊號,
傳送端不知道資料是否會正確接收,當然也不用重發,所以說UDP是無連線的、不可靠的一種資料傳輸協議

也正由於上面的特點,使得UDP的開銷更小資料傳輸速率更高,因為不必進行收發資料的確認,所以UDP的實時性更好。

同步/非同步主要針對C端: 
同步:
      所謂同步,就是在c端發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。也就是必須一件一件事做,等前一件做完了才能做下一件事。
例如普通B/S模式(同步):提交請求->等待伺服器處理->處理完畢返回 這個期間客戶端瀏覽器不能幹任何事


非同步:
      非同步的概念和同步相對。當c端一個非同步過程呼叫發出後,呼叫者不能立刻得到結果。實際處理這個呼叫的部件在完成後,通過狀態、通知和回撥來通知呼叫者。
     例如 ajax請求(非同步): 請求通過事件觸發->伺服器處理(這是瀏覽器仍然可以作其他事情)->處理完畢


阻塞/非阻塞主要針對S端:


阻塞
     阻塞呼叫是指呼叫結果返回之前,當前執行緒會被掛起(執行緒進入非可執行狀態,在這個狀態下,cpu不會給執行緒分配時間片,即執行緒暫停執行)。函式只有在得到結果之後才會返回。


     有人也許會把阻塞呼叫和同步呼叫等同起來,實際上他是不同的。對於同步呼叫來說,很多時候當前執行緒還是啟用的,只是從邏輯上當前函式沒有返回而已。 例如,我們在socket中呼叫recv函式,如果緩衝區中沒有資料,這個函式就會一直等待,直到有資料才返回。而此時,當前執行緒還會繼續處理各種各樣的訊息。
   快遞的例子:比如到你某個時候到A樓一層(假如是核心緩衝區)取快遞,但是你不知道快遞什麼時候過來,你又不能幹別的事,只能死等著。但你可以睡覺(程序處於休眠狀態),因為你知道快遞把貨送來時一定會給你打個電話(假定一定能叫醒你)。


非阻塞
      非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函式不會阻塞當前執行緒,而會立刻返回。
     還是等快遞的例子:如果用忙輪詢的方法,每隔5分鐘到A樓一層(核心緩衝區)去看快遞來了沒有。如果沒來,立即返回。而快遞來了,就放在A樓一層,等你去取。

服務端和客戶端之間通過Socket建立連線,之後它們就可以進行通訊了。首先ServerSocket將在服務端監聽某個埠,當發現客戶端有Socket來試圖連線它時,它會acceptSocket的連線請求,同時在服務端建立一個對應的Socket與之進行通訊。這樣就有兩個Socket了,客戶端和服務端各一個。

下面寫一個阻塞IO下客戶端與服務端非同步通訊的例子,每次ServerSocket接收到一個新的Socket連線請求後都會新起一個執行緒來跟當前Socket進行通訊。

服務端程式碼

public class server {
public static void main(String args[]) throws IOException{
//為了簡單起見,所有的異常資訊都往外拋  
 int port = 8899;  
     //定義一個ServerSocket監聽在埠8899上  
     ServerSocket server = new ServerSocket(port);  


     while(true){
   
    //server嘗試接收其他Socket的連線請求,server的accept方法是阻塞式的  
    Socket socket=server.accept();
    
    //每接收到一個Socket就建立一個新的執行緒來處理它  
    new Thread(new Task(socket)).start();
     }
}
static class Task implements Runnable{
//靜態內部類
 
private Socket socket;

public Task(Socket socket){
this.socket=socket;
}


@Override
public void run() {
try{
handleSocket();
}
catch(Exception e){
e.printStackTrace();
}
}

private void handleSocket() throws Exception{
//跟客戶端建立好連線之後,我們就可以獲取socket的InputStream,並從中讀取客戶端發過來的資訊了。  
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
        StringBuilder sb = new StringBuilder();  
        String temp;  
        int index;  
        while ((temp=br.readLine()) != null) {  
        //使用BufferedReader來一次讀一行
           if ((index = temp.indexOf("eof")) != -1) {//遇到eof時就結束接收  
            sb.append(temp.substring(0, index));  
               break;  
           }  
           sb.append(temp);  
        } 
        //socket.setSoTimeout(20*1000);
        System.out.println("from client: " + sb);  
        //讀完後寫一句  
        Writer writer = new OutputStreamWriter(socket.getOutputStream());  
        writer.write("Hello Client.");  
        writer.write("eof\n");  
        writer.flush();  
        writer.close();  
        br.close();  
        socket.close();  
}


}


}

客戶端程式碼

public class client {
private static List<Queue> queueCache = new LinkedList<Queue>();
public static void main(String args[]) throws Exception {  
 
     //為了簡單起見,所有的異常都直接往外拋  
     String host = "192.168.1.55";  //要連線的服務端IP地址  
     int port = 8899;   //要連線的服務端對應的監聽埠  
     //與服務端建立連線  
     Socket client = new Socket(host, port);  
     //建立連線後就可以往服務端寫資料了  
     Writer writer = new OutputStreamWriter(client.getOutputStream());
     writer.write("Hello Server.");  
     writer.write("eof\n"); 
     writer.flush();//寫完後要記得flush
     BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));  
     StringBuffer sb = new StringBuffer();  
     String temp;  
     int index;  
     while ((temp=br.readLine()) != null) {  
        if ((index = temp.indexOf("eof")) != -1) {  
           sb.append(temp.substring(0, index));  
           break;  
        }  
        sb.append(temp);  
     }  
     System.out.println("from server: " + sb);  


     br.close();
     writer.close();  
     client.close();  
  }  
}

BufferedReaderreadLine方法是一次讀一行的,這個方法是阻塞的,直到它讀到了一行資料為止程式才會繼續往下執行,那麼readLine什麼時候才會讀到一行呢?直到程式遇到了換行符或者是對應流的結束符readLine方法才會認為讀到了一行,才會結束其阻塞,讓程式繼續往下執行。所以我們在使用BufferedReaderreadLine讀取資料的時候一定要記得在對應的輸出流裡面一定要寫入換行符(流結束之後會自動標記為結束,readLine可以識別),寫入換行符之後一定記得如果輸出流不是馬上關閉的情況下記得flush一下,這樣資料才會真正的從緩衝區裡面寫入。

Socket為我們提供了一個setSoTimeout()方法來設定接收資料的超時時間,單位是毫秒。當設定的超時時間大於0,並且超過了這一時間Socket還沒有接收到返回的資料的話,Socket就會丟擲一個SocketTimeoutException

相關推薦

socket網路程式設計執行阻塞IO例項

先介紹一下網路層次結構、socket與TCP/UDP之間的關係。同步、非同步,阻塞、非阻塞的區別。 網路由下往上分為物理層、資料鏈路層、網路層、傳輸層、會話層、表示層和應用層。 IP 協議對應於網路層,TCP協議對應於傳輸層,HTTP協議對應於應用層,三者從本質上來說沒有可

銀行業務系統(c/s架構、socket網路程式設計執行

1、功能要求 包括兩類使用者:管理人員和普通使用者(本文只寫了普通使用者程式) 普通使用者功能:登入登出、存取款、轉賬、查詢餘額 2、技術要求 要求用到多程序多執行緒 要求同時允許多個使用者操作(因為沒有註冊賬號功能,且只初始化了兩個賬號資訊,所以同時只能允許兩個賬號線上)

Java網路程式設計4.UDP網路程式設計執行優化

UDP網路程式設計之多執行緒優化——DatagramSocket類 1、UDP網路程式設計之多執行緒優化的思想 (1)一個執行緒實現客戶端——傳送資料 (2)一個執行緒實現伺服器端——接收資料

Java網路程式設計執行Client-Server

前面廢話過了,現在就直接看程式碼吧! ThreadedClient.java package exercise01; import java.io.*; import java.net.*; public class ThreadedClient { privat

Python網路程式設計執行

多執行緒 多執行緒舉例: import threading from time import sleep,ctime def sing(): for i in range(3): print("正在唱歌...%d"%i)

Linux 下 C 網路程式設計 執行通訊 例項

簡單示例,有不對的地方,歡迎指點。 伺服器端 /* ============================================================================ Name : sockThreadServer

網路程式設計執行——GIL全域性直譯器鎖

網路程式設計之多執行緒——GIL全域性直譯器鎖 一、引子 定義: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python b

Linux下socket程式設計執行TCP伺服器

程式碼如下: thread_server.c #include<string.h> #include<stdlib.h> #include<stdio.h> #include<sys/types.h> #i

網路程式設計執行的應用--基於socket udp編寫一個簡單聊天程式

void CChatDlg::OnBnClickedButtonSend() {// TODO: Add your control notification handler code here//獲取對方IPCIPAddressCtrl* pIPAddress = ((CIPAddressCtrl*)GetD

Liunx C 程式設計執行Socket

多執行緒 pthread.h是linux特有的標頭檔案,POSIX執行緒(POSIX threads),簡稱Pthreads,是執行緒的POSIX標準。該標準定義了建立和操縱執行緒的一整套API。在類Unix作業系統(Unix、Linux、Mac OS X等)中,都使用Pthreads作為作業系統的執行緒。

併發程式設計執行執行安全

什麼是執行緒安全? 為什麼有執行緒安全問題? 當多個執行緒同時共享,同一個全域性變數或靜態變數,做寫的操作時,可能會發生資料衝突問題,也就是執行緒安全問題。但是做讀操作是不會發生資料衝突問題。 案例: 需求現在有100張火車票,有兩個視窗同時搶火車票,請使用多執行緒模擬搶票效果。 p

併發程式設計執行基礎

執行緒與程序區別 每個正在系統上執行的程式都是一個程序。每個程序包含一到多個執行緒。執行緒是一組指令的集合,或者是程式的特殊段,它可以在程式裡獨立執行。也可以把它理解為程式碼執行的上下文。所以執行緒基本上是輕量級的程序,它負責在單個程式裡執行多工。通常由作業系統負責多個執行緒的排程和執行。

Python併發程式設計執行使用

目錄 一 開啟執行緒的兩種方式 二 在一個程序下開啟多個執行緒與在一個程序下開啟多個子程序的區別 三 練習 四 執行緒相關的其他方法 五 守護執行緒 六 Python GIL(Global Interpreter Lock) 七 同步鎖 八 死鎖現象

併發程式設計執行基礎-Thread和Runnable的區別及聯絡(二)

上篇文章講述了建立執行緒的常用方式 本篇主要分析一下Thread和Runnable兩種方式建立執行緒的區別及聯絡 聯絡: ▶Thread類實現了Runable介面。 ▶都需要重寫裡面Run方法。 區別: ▶Thread方式不支援多繼承,Runnable方式支援多個實現 ▶Runnable更容易實

併發程式設計執行基礎-執行五種狀態(三)

原文地址:https://www.cnblogs.com/wangyichuan/p/5990821.html 執行緒從建立、執行到結束總是處於下面五個狀態之一:新建狀態、就緒狀態、執行狀態、阻塞狀態及死亡狀態。     1.新建狀態(New):         當用new操作符建立一個執行緒時,

Python學習【第24篇】:死鎖,遞迴鎖,訊號量,Event事件,執行Queue python併發程式設計執行2------------死鎖與遞迴鎖,訊號量等

python併發程式設計之多執行緒2------------死鎖與遞迴鎖,訊號量等 一、死鎖現象與遞迴鎖 程序也是有死鎖的 所謂死鎖: 是指兩個或兩個以上

Python學習【第23篇】:利用threading模組開執行 python併發程式設計執行1

python併發程式設計之多執行緒1 一多執行緒的概念介紹 threading模組介紹 threading模組和multiprocessing模組在使用層

併發程式設計執行

一、什麼是執行緒 在傳統作業系統中,每個程序有一個地址空間,而且預設就有一個控制執行緒 執行緒顧名思義,就是一條流水線工作的過程,一條流水線必須屬於一個車間,一個車間的工作過程是一個程序 車間負責把資源整合到一起,是一個資源單位,而一個車間內至少有一個流水線  流水線的工作需要電源,電源就相當於cpu 所以,

PHP 高階程式設計執行-訊息佇列

1. 多執行緒環境安裝 1.1. PHP 5.5.9 安裝PHP 5.5.9 https://github.com/oscm/shell/blob/master/php/5.5.9.sh ./configure --prefix=/srv/php-5.5.9 \ --wi

網路程式設計三---執行/程序解決併發問題

#include <netinet/in.h> #include <sys/socket.h> #include <errno.h> #include <stdio.h> #include <string.h> #include <unistd