【Socket編程】經過Socket實現TCP編程

經過Socket實現TCP編程

Socket通訊 :html

1.TCP協議是面向對象鏈接、可靠的、有序的,以字節流的方式發送數據。編程

2.基於TCP協議實現網絡通訊的類:小程序

  • 客戶端----Socket類
  • 服務器端----ServerSocket類

1、通訊過程(Socket通訊模型)

Socket通訊模型用下圖所示:緩存

 

一、在服務端創建一個ServerSocket,綁定相應的端口,而且在指定的端口進行偵聽,等待客戶端的鏈接。服務器

二、當客戶端建立鏈接Socket而且向服務端發送請求。網絡

三、服務器收到請求,而且接受客戶端的請求信息。一旦接收到客戶端的鏈接請求後,會建立一個連接socket,用來與客戶端的socket進行通訊。 經過相應的輸入/輸出流進行數據的交換,數據的發送接收以及數據的響應等等。多線程

四、當客戶端和服務端通訊完畢後,須要分別關閉socket,結束通訊。socket

Socket通訊實現步驟:測試

瞭解Socket通訊模型後,就能夠簡化出Socket通訊的實現步驟:編碼

1.建立ServerSocket和Socket

2.打開連接到Socket的輸入/輸出流

3.按照協議對Socket進行讀/寫操做

4.關閉輸入輸出流、關閉Socket

2、Socket和ServerSocket經常使用方法

ServerSocket經常使用方法:

  • ServerSocket(int port)——建立並綁定到特定端口的服務器套接字
  • accept()——偵聽並接受到此套接字的鏈接
  • close()——關閉此套接字 getInetAddress()——獲得ServerSocket對象綁定的IP地址。若是ServerSocket對象未綁定IP地址,返回0.0.0.0
  • getLocalPort()——返回此套接字在其上偵聽的端口

Socket經常使用方法:

  • Socket(InetAddress address, int port)——建立一個套接字並將其鏈接到指定ip地址的指定端口號
  • Socket(String host, int port)——建立一個套接字並將其鏈接到指定主機上的指定端口號
  • close()——關閉此套接字
  • getInetAddress()——返回套接字鏈接的地址
  • getInputStream()——返回此套接字的輸入流
  • getOutputStream——返回此套接字的輸出流

3、編程實現基於TCP/IP的用戶登陸小程序

經過寫一個用戶登陸的小程序,來直觀瞭解Socket通訊的工做過程,首先咱們得知道在客戶端和服務器通訊時,二者的運做流程是如何的,先從服務器入手。

服務端:

一、建立ServerSocket對象,綁定監聽端口

二、經過accept()方法監聽客戶端請求

三、鏈接創建後,經過輸入流讀取客戶端發送的請求信息

四、經過輸出流向客戶端發送響應信息

五、關閉相關資源

那麼用戶登陸的測試案例的服務器端類能夠這樣(待完善):

 1         //1.建立一個服務器端的Socket,即ServerSocket,指定綁定的端口
 2         ServerSocket ss=new ServerSocket(8888);
 3         //2.調用accept方法開始監聽,等待客戶端的鏈接
 4         System.out.println("服務器即將啓動,等待客戶端的鏈接...");
 5         Socket so=ss.accept();//accept方法返回Socket實例
 6         //3.獲取一個輸入流,並讀取客戶端信息
 7         InputStream is=so.getInputStream();//字節輸入流
 8         InputStreamReader isr=new InputStreamReader(is);//將字節輸入流包裝成字符輸入流
 9         BufferedReader br=new BufferedReader(isr);//加上緩衝流,提升效率
10         String info=null;
11         while((info=br.readLine())!=null){//循環讀取客戶端信息
12             System.out.println("我是服務器,客戶端說:"+info);
13             
14         }
15         so.shutdownInput();//關閉輸入流
16         //4.關閉資源
17         br.close();
18         isr.close();
19         is.close();
20         so.close();
21         ss.close();

 

接着咱們看一下客戶端的運做過程,其實和服務器端差很少,可是其中的區別仍是要清楚的。

客戶端:

一、建立Socket對象,指明須要鏈接的服務器的地址和端口號

二、鏈接創建後,經過輸出流向服務器端發送請求信息

三、經過輸入流獲取服務器相應的信息

四、關閉相關資源。

那麼用戶登陸的測試案例的客戶端類能夠這樣(待完善):

 1         //1.建立客戶端Socket,指定服務器地址和端口
 2         Socket so=new Socket("localhost", 8888);//端口號要和服務器端相同
 3         //2.獲取輸出流,向服務器端發送登陸的信息
 4         OutputStream os=so.getOutputStream();//字節輸出流
 5         PrintWriter pw=new PrintWriter(os);//字符輸出流
 6         BufferedWriter bw=new BufferedWriter(pw);//加上緩衝流
 7         bw.write("用戶名:admin;密碼:123");
 8         bw.flush();
 9         so.shutdownOutput();//關閉輸出流
10         //3.關閉資源
11         bw.close();
12         pw.close();
13         os.close();
14         so.close();

 

這樣服務端和客戶端就能進行最簡單的通訊了,目前這個還不能實現交互,下面會講二者的交互。

運行一下,看看服務器端是否能接收到客戶端發送的信息了呢...

分別啓動服務器和客戶端,注意必須先啓動服務器再啓動客戶端,不然客戶端找不到資源報錯:


完善用戶登陸之服務器響應客戶端

 在上述的例子中,服務器和客戶端僅僅只是相互能夠通訊,但服務器對於接收到的客戶端信息並無向客戶端響應,客戶端沒有接收到服務器端的任何信息,這明顯是不完善不友好的,在這裏將對剛剛的例子進行完善,完成服務器對客戶端的響應。

修改後的服務器端:

 1     //1.建立一個服務器端的Socket,即ServerSocket,指定綁定的端口
 2             ServerSocket ss= new ServerSocket(8888);
 3             //2.調用accept方法開始監聽,等待客戶端的鏈接
 4             System.out.println("服務器即將啓動,等待客戶端的鏈接...");
 5             Socket so=ss.accept();//accept方法返回Socket實例
 6             //3.獲取一個輸入流,並讀取客戶端信息
 7             InputStream is=so.getInputStream();//字節輸入流
 8             InputStreamReader isr=new InputStreamReader(is);//將字節輸入流包裝成字符輸入流
 9             BufferedReader br=new BufferedReader(isr);//加上緩衝流,提升效率
10             String info=null;
11             while((info=br.readLine())!=null){//循環讀取客戶端信息
12                 System.out.println("我是服務器,客戶端說:"+info);
13                 
14             }
15             so.shutdownInput();//關閉輸入流
16             //4.獲取一個輸出流,向客戶端輸出信息,響應客戶端的請求
17             OutputStream os=so.getOutputStream();//字節輸出流
18             PrintWriter pw=new PrintWriter(os);//字符輸出流
19             BufferedWriter bw=new BufferedWriter(pw);//緩衝輸出流
20             bw.write("歡迎您!");
21             bw.newLine();
22             bw.flush();
23             
24             //5.關閉資源
25             os.close();
26             pw.close();
27             bw.close();
28             br.close();
29             isr.close();
30             is.close();
31             so.close();
32             ss.close();

修改後的客戶端:

 1 //1.建立客戶端Socket,指定服務器地址和端口
 2         Socket so=new Socket("localhost", 8888);//端口號要和服務器端相同
 3         //2.獲取輸出流,向服務器端發送登陸的信息
 4         OutputStream os=so.getOutputStream();//字節輸出流
 5         PrintWriter pw=new PrintWriter(os);//字符輸出流
 6         BufferedWriter bw=new BufferedWriter(pw);//加上緩衝流
 7         bw.write("用戶名:admin;密碼:123");
 8         bw.flush();
 9         so.shutdownOutput();//關閉輸出流
10         //3.獲取輸入流,獲得服務端的響應信息
11         InputStream is=so.getInputStream();
12         InputStreamReader isr=new InputStreamReader(is);
13         BufferedReader br=new BufferedReader(isr);
14         String info=null;
15         while((info=br.readLine())!=null){
16             System.out.println("我是客戶端,服務器說:"+info);
17         }
18                 
19         
20         //4.關閉資源
21         bw.close();
22         pw.close();
23         os.close();
24         so.close();

運行結果:

4、使用多線程實現多客戶端的通訊

 應用多線程來實現服務器與多客戶端之間的通訊。<關於多線程更多的內容之後再總結>

多線程基本步驟:

1.服務器端建立ServerSocket,循環調用accept()等待客戶端鏈接。

2.客戶端建立一個socket並請求和服務器端鏈接。

3.服務器端接收客戶端請求,建立socket與該客戶創建專線鏈接

4.創建鏈接的兩個socket在一個單獨的線程上對話。

5.服務器端繼續等待新的鏈接。

------------------------------------------------------------------------------

下面再將上述的例子修改爲多線程的通訊

新建一個服務器線程處理類ServerThread,該類繼承Thread類

 1 /*
 2  * 服務器線程處理類
 3  */
 4 public class ServerThread extends Thread {
 5     // 和本線程相關的Socket
 6     Socket so = null;
 7 
 8     public ServerThread(Socket socket) {// 初始化與本線程相關的Socket
 9         so = socket;
10     }
11 
12     // 線程執行的操做,響應客戶端的請求
13     public void run() {// 重寫父類的run方法
14         InputStream is = null;
15         InputStreamReader isr = null;
16         BufferedReader br = null;
17         OutputStream os = null;
18         PrintWriter pw = null;
19         BufferedWriter bw = null;
20         try {
21             // 3.獲取一個輸入流,並讀取客戶端信息
22             is = so.getInputStream();// 字節輸入流
23             isr = new InputStreamReader(is);// 將字節輸入流包裝成字符輸入流
24             br = new BufferedReader(isr);// 加上緩衝流,提升效率
25             String info = null;
26             while ((info = br.readLine()) != null) {// 循環讀取客戶端信息
27                 System.out.println("我是服務器,客戶端說:" + info);
28 
29             }
30             so.shutdownInput();// 關閉輸入流
31             // 4.獲取一個輸出流,向客戶端輸出信息,響應客戶端的請求
32             os = so.getOutputStream();// 字節輸出流
33             pw = new PrintWriter(os);// 字符輸出流
34             bw = new BufferedWriter(pw);// 緩衝輸出流
35             bw.write("歡迎您!");
36             bw.newLine();
37             bw.flush();
38 
39         } catch (IOException e) {
40             // TODO Auto-generated catch block
41             e.printStackTrace();
42         } finally {
43             // 5.關閉資源
44             try {
45                 if (os != null)
46                     os.close();
47                 if (pw != null)
48                     pw.close();
49                 if (bw != null)
50                     bw.close();
51                 if (br != null)
52                     br.close();
53                 if (isr != null)
54                     isr.close();
55                 if (is != null)
56                     is.close();
57                 if (!so.isClosed())
58                     so.close();
59             } catch (IOException e) {
60                 // TODO Auto-generated catch block
61                 e.printStackTrace();
62             }
63         }
64 
65     }
66 }

<解 惑>關閉資源爲什麼要加一個判斷條件:不爲空 ???

<回 答>這是一種正確、嚴謹的寫法。 驗證非NULL是編碼中很重要的一環。假如原本就是NULL,這是調用各自的close()方法是會報錯的。 若是在實例化這些對象時出錯致使這些對象爲NULL,或是實例化沒問題但中途出了什麼異常致使這些對象爲NULL,都會在未經驗證非NULL前嘗試調用close()方法關閉時報錯。

<提 示>集中異常處理的快捷鍵:Alt+shift+z

服務器端:

 1 try {
 2             //1.建立一個服務器端的Socket,即ServerSocket,指定綁定的端口
 3             ServerSocket ss= new ServerSocket(8888);
 4             
 5             System.out.println("服務器即將啓動,等待客戶端的鏈接...");
 6             Socket so=null;
 7             //記錄客戶端的數量
 8             int count=0;
 9             //循環偵聽等待客戶端的鏈接
10             while(true){
11                 //2.調用accept方法開始監聽,等待客戶端的鏈接
12                  so=ss.accept();//accept方法返回Socket實例
13                  //建立一個新的線程
14                  ServerThread st=new ServerThread(so);
15                  //啓動線程,執行與客戶端的交互
16                  st.start();//注意是start不是run
17                  count++;
18                  System.out.println("此時客戶端數量爲:"+count);
19                  InetAddress add=so.getInetAddress();
20                  System.out.println("當前客戶端的ip地址爲"+add.getHostAddress());
21             }
22         } catch (IOException e) {
23             // TODO Auto-generated catch block
24             e.printStackTrace();
25         }

多個客戶端的運行結果:

這樣一個簡單的多線程通訊就完成了,這個多線程還不是並行操做的,要實現並行操做能夠按照下面的思路:

主線程負責建立socket

* 一個線程用來讀取

* 一個線程用來寫入,用兩個內部類

* 要用多線程實現,send線程和recived線程同時訪問buff數據區。

* 發送完成後notify,接收線程。接收線程自身wait

* 接收的說,我先wait一下,可能緩存中尚未數據,我會拿到空值得。

* 發送的說Ok,我先寫,寫完了我notifyall你。我就一直鎖着,這樣默契的合做了。

變成並行處理了 每一個客戶端鏈接服務端都會產生一個新的socket。

< 具體代碼還沒寫,你們能夠自行摸索。。。寫完了再更新>

 ---------------點擊查看更多關於Socket信息------------------

相關文章
相關標籤/搜索