消息推送學習1、原生Socket的使用

  消息推送也是客戶端和服務器鏈接而後進行交互的一種形式,可是不一樣於HTTP的鏈接,這種鏈接須要長時間的進行,當有消息時能夠及時推送到客戶端。除此以外還有多個用戶,可能須要針對其身份進行不一樣的推送等等要求。而這種鏈接的形式在Java中可使用Socket進行實現。java

1、初版:服務器

  一、首先是服務器部分,重要的操做說明網絡

    ①使用ServerSocket能夠開啓服務器上的一個端口進行鏈接監聽,相似於服務器監聽80端口。框架

    ②使用accept(),阻塞式的等待客戶端的接入。接入成功時返回鏈接的Socket對象。經過該對象能夠進行數據的交換。socket

 1 import java.io.BufferedReader;
 2 import java.io.BufferedWriter;
 3 import java.io.InputStreamReader;
 4 import java.io.OutputStreamWriter;
 5 import java.net.ServerSocket;
 6 import java.net.Socket;
 7 
 8 public class Service {
 9     public static void main(String[] args) {
10         Service service=new Service();
11         service.start();
12     }
13 
14     private void start() {
15         ServerSocket socketService=null;
16         Socket socket=null;
17         BufferedReader reader=null;
18         BufferedWriter writer=null;
19         try {
20             //開啓一個Socket服務器,監聽9898端口
21             socketService=new ServerSocket(9898);
22             System.out.println("service start...");
23             //等待客戶端連入,一直阻塞直到接入,返回鏈接Socket對象
24             socket=socketService.accept();
25             System.out.println("client connection...");
26             //如下就是數據交換
27             reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
28             writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
29             String message=null;
30             while(!(message=reader.readLine()).equals("bye")){
31                 System.out.println(message);
32                 writer.write(socket.getLocalAddress()+":"+message+"\n");
33                 writer.flush();
34             }
35         } catch (Exception e) {
36             e.printStackTrace();
37         }finally{
38             try {
39                 reader.close();
40                 writer.close();
41                 socket.close();
42                 socketService.close();
43             } catch (Exception e) {
44                 e.printStackTrace();
45             }
46         }
47     }
48 }

  二、對於客戶端來講比較簡單:ide

    ①指定服務器和端口來建立Socket鏈接,性能

    ②使用該Socket對象來進行數據傳輸便可,這裏是將控制檯上的輸入內容傳遞至服務器。須要注意的是讀取操做是以"\n「爲結束標記的,因此須要傳輸時須要加上,不然不被認爲是結束測試

 1 import java.io.BufferedReader;
 2 import java.io.BufferedWriter;
 3 import java.io.InputStreamReader;
 4 import java.io.OutputStreamWriter;
 5 import java.net.Socket;
 6 
 7 public class Client {
 8     public static void main(String[] args) {
 9         //這裏建立一個Client對象而不是直接在main()操做,是能夠是使用的變量等沒必要是static類型的
10         Client client=new Client();
11         client.start();
12     }
13 
14     private void start() {
15         BufferedReader readFromSys=null;
16         BufferedReader readFromService=null;
17         BufferedWriter writeToService=null;
18         Socket socket=null;
19         try {
20             //傳入地址和端口號,和服務器創建鏈接
21             socket=new Socket("127.0.0.1",9898);
22             //如下是數據傳輸部分
23             readFromSys=new BufferedReader(new InputStreamReader(System.in));
24             readFromService=new BufferedReader(new InputStreamReader(socket.getInputStream()));
25             writeToService=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
26             
27             String message;
28             String responce;
29             while(!(message=readFromSys.readLine()).equals("bye")){
30                 //傳輸數據須要以 \n 結尾,不然不被認爲是結束。客戶端和服務器端都同樣
31                 writeToService.write(message+"\n");
32                 writeToService.flush();
33                 responce=readFromService.readLine();
34                 System.out.println(responce);
35             }
36         } catch (Exception e) {
37             e.printStackTrace();
38         }finally{
39             try {
40                 writeToService.close();
41                 readFromService.close();
42                 readFromSys.close();
43                 socket.close();
44             } catch (Exception e) {
45                 e.printStackTrace();
46             }
47         }
48         
49     }
50 }

  這一版已經成功的進行和客戶端和服務器端的鏈接,可是有一些問題,下面是對其的總結:spa

    ①服務器端不能主動的向客戶端進行消息的傳遞。.net

    ②客戶端中的只能在發送一條數據以後的服務器響應時才能獲取數據,總結爲不能及時獲取服務器端消息。

  以上兩條說明這種操做是徹底不能做爲一種推送服務的。

    ③服務器端只能處理一個用戶的請求,即只能和一個客戶端進行鏈接。

2、第二版:這是對初版中存在的問題進行解決

  一、解決①問題,即服務器端不能主動發送消息的問題,思路是經過Socket鏈接的輸出流進行數據的傳遞便可

 1 import java.io.BufferedReader;
 2 import java.io.BufferedWriter;
 3 import java.io.InputStreamReader;
 4 import java.io.OutputStreamWriter;
 5 import java.net.ServerSocket;
 6 import java.net.Socket;
 7 import java.util.Timer;
 8 import java.util.TimerTask;
 9 
10 public class Service {
11     public static void main(String[] args) {
12         Service service=new Service();
13         service.start();
14     }
15 
16     private void start() {
17         ServerSocket socketService=null;
18         Socket socket=null;
19             BufferedReader reader=null;
20         BufferedWriter writer=null;
21         try {
22             //開啓一個Socket服務器,監聽9898端口
23             socketService=new ServerSocket(9898);
24             System.out.println("service start...");
25             
26                 socket=socketService.accept();
27             System.out.println("client connection..."+socket.hashCode());
28 
29             try {
30                 reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
31                 writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
32 
33                 //!!!服務器向客戶端發送消息
34                 sendMessage(writer);
35                     
36                 String message=null;
37                 while(!(message=reader.readLine()).equals("bye")){
38                     System.out.println(message);
39                     writer.write(socket.getLocalAddress()+":"+message+"\n");
40                     writer.flush();
41                 }
42         } catch (Exception e) {
43             e.printStackTrace();
44         }finally{
45             try {
46                 socket.close();
47                 socketService.close();
48             } catch (Exception e) {
49                 e.printStackTrace();
50             }
51         }
52     }
53 
54     private void sendMessage(final BufferedWriter writer) {
55         //這裏簡單開啓一個定時任務,每一個幾秒向客戶端發送一條消息,進行模擬操做。
56         new Timer().schedule(new TimerTask() {
57             @Override
58             public void run() {
59                 try {
60                     writer.write("消息發送測試!\n");
61                     writer.flush();
62                 } catch (Exception e) {
63                     e.printStackTrace();
64                 }
65             }
66         }, 1000, 3000);
67     }
68 }
服務器端修改

  二、解決②問題,即客戶端不能及時讀取服務器端消息的問題,思路是對鏈接Socket的輸入流進行監聽,一旦有輸入當即進行處理

 1 import java.io.BufferedReader;
 2 import java.io.BufferedWriter;
 3 import java.io.InputStreamReader;
 4 import java.io.OutputStreamWriter;
 5 import java.net.Socket;
 6 
 7 public class Client {
 8     public static void main(String[] args) {
 9         //這裏建立一個Client對象而不是直接在main()操做,是能夠是使用的變量等沒必要是static類型的
10         Client client=new Client();
11         client.start();
12     }
13 
14     private void start() {
15         BufferedReader readFromSys=null;
16         BufferedReader readFromService=null;
17         BufferedWriter writeToService=null;
18         Socket socket=null;
19         try {
20             //傳入地址和端口號,和服務器創建鏈接
21             socket=new Socket("127.0.0.1",9898);
22             //如下是數據傳輸部分
23             readFromSys=new BufferedReader(new InputStreamReader(System.in));
24             readFromService=new BufferedReader(new InputStreamReader(socket.getInputStream()));
25             writeToService=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
26             
27             String message;
28             
29             //用於對服務器端輸入的數據流進行監聽
30             socketInputStreamListener(readFromService);
31             
32             while(!(message=readFromSys.readLine()).equals("bye")){
33                 //傳輸數據須要以 \n 結尾,不然不被認爲是結束。客戶端和服務器端都同樣
34                 writeToService.write(message+"\n");
35                 writeToService.flush();
36             }
37         } catch (Exception e) {
38             e.printStackTrace();
39         }finally{
40             try {
41                 writeToService.close();
42                 readFromService.close();
43                 readFromSys.close();
44                 socket.close();
45             } catch (Exception e) {
46                 e.printStackTrace();
47             }
48         }
49         
50     }
51 
52     private void socketInputStreamListener(final BufferedReader readFromService) {
53         //一、首先是須要監聽,因此是長時間操做,可是不能阻塞主線程的操做,因此使用子線程來進行處理
54         new Thread(new Runnable(){
55             @Override
56             public void run() {
57                 try {
58                     String responce;
59                     while(!(responce=readFromService.readLine()).equals(null)){
60                         System.out.println(responce);
61                     }
62                 } catch (Exception e) {
63                     e.printStackTrace();
64                 }
65             }
66         }).start();
67     }
68 }
客戶端修改

  三、解決③問題,即服務器只能與一個客戶端進行鏈接的問題,思路是循環調用accept(),該方法用於阻塞式的等待客戶端的鏈接,而後將已經創建的鏈接的任務放在子線程中進行處理便可。而這裏的鏈接任務就是數據的傳遞部分。

 1 import java.io.BufferedReader;
 2 import java.io.BufferedWriter;
 3 import java.io.InputStreamReader;
 4 import java.io.OutputStreamWriter;
 5 import java.net.ServerSocket;
 6 import java.net.Socket;
 7 import java.util.Timer;
 8 import java.util.TimerTask;
 9 
10 public class Service {
11     public static void main(String[] args) {
12         Service service=new Service();
13         service.start();
14     }
15 
16     private void start() {
17         ServerSocket socketService=null;
18         Socket socket=null;
19         try {
20             //開啓一個Socket服務器,監聽9898端口
21             socketService=new ServerSocket(9898);
22             System.out.println("service start...");
23             
24             //
25             while(true){
26                 socket=socketService.accept();
27                 System.out.println("client connection..."+socket.hashCode());
28                 exectureConnectRunnable(socket);
29             }
30         } catch (Exception e) {
31             e.printStackTrace();
32         }finally{
33             try {
34                 socket.close();
35                 socketService.close();
36             } catch (Exception e) {
37                 e.printStackTrace();
38             }
39         }
40     }
41 
42     //這裏的工做必須在子線程中工做,由於下面有一段死循環操做,若是不在子線程中則會阻塞主線程,不能和其餘客戶端進行鏈接。
43     private void exectureConnectRunnable(final Socket socket) {
44         new Thread(new Runnable(){
45             @Override
46             public void run() {
47                 BufferedReader reader=null;
48                 BufferedWriter writer=null;
49                 //如下就是數據交換
50                 try {
51                     reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
52                     writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
53 
54                     //服務器向客戶端發送消息
55                     sendMessage(writer);
56                     
57                     String message=null;
58                     while(!(message=reader.readLine()).equals("bye")){
59                         System.out.println(message);
60                         writer.write(socket.getLocalAddress()+":"+message+"\n");
61                         writer.flush();
62                     }
63                 } catch (Exception e) {
64                     e.printStackTrace();
65                 }finally{
66                     try {
67                         reader.close();
68                         writer.close();
69                     } catch (Exception e) {
70                         e.printStackTrace();
71                     }
72                 }
73             }
74         }).start();
75     }
76 
77     private void sendMessage(final BufferedWriter writer) {
78         //這裏簡單開啓一個定時任務,每一個幾秒向客戶端發送一條消息,進行模擬操做。
79         new Timer().schedule(new TimerTask() {
80             @Override
81             public void run() {
82                 try {
83                     writer.write("消息發送測試!\n");
84                     writer.flush();
85                 } catch (Exception e) {
86                     e.printStackTrace();
87                 }
88             }
89         }, 1000, 3000);
90     }
91 }
服務器端再次修改

 

到此爲止,使用原生的Socket進行消息推送的基本嘗試已經完成,可是其仍是有不少問題,例如:

  一、網絡操做都是阻塞式實現的,因此不得不採用子線程來進行處理,當鏈接的用戶過多時,性能就稱爲一個極大的瓶頸。

  二、對各類流的處理等等操做。

在Java1.4版本以後就引入了nio包,即new IO,用來進行改善,可是操做比較麻煩。而已經有了大神對底層操做的封裝框架,如Mina和Netty等,下一部分就是對Mina的體驗使用。

相關文章
相關標籤/搜索