• 消息推送学习一、原生Socket的使用


      消息推送也是客户端和服务器连接然后进行交互的一种形式,但是不同于HTTP的连接,这种连接需要长时间的进行,当有消息时可以及时推送到客户端。除此之外还有多个用户,可能需要针对其身份进行不同的推送等等要求。而这种连接的形式在Java中可以使用Socket进行实现。

    一、第一版:

      1、首先是服务器部分,重要的操作说明

        ①使用ServerSocket可以开启服务器上的一个端口进行连接监听,类似于服务器监听80端口。

        ②使用accept(),阻塞式的等待客户端的接入。接入成功时返回连接的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+"
    ");
    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 }

      2、对于客户端来说比较简单:

        ①指定服务器和端口来创建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.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                 //传输数据需要以 
     结尾,否则不被认为是结束。客户端和服务器端都一样
    31                 writeToService.write(message+"
    ");
    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 }

      这一版已经成功的进行和客户端和服务器端的连接,但是有一些问题,下面是对其的总结:

        ①服务器端不能主动的向客户端进行消息的传递。

        ②客户端中的只能在发送一条数据之后的服务器响应时才能获取数据,总结为不能及时获取服务器端消息。

      以上两条说明这种操作是完全不能作为一种推送服务的。

        ③服务器端只能处理一个用户的请求,即只能和一个客户端进行连接。

    二、第二版:这是对第一版中存在的问题进行解决

      1、解决①问题,即服务器端不能主动发送消息的问题,思路是通过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+"
    ");
    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("消息发送测试!
    ");
    61                     writer.flush();
    62                 } catch (Exception e) {
    63                     e.printStackTrace();
    64                 }
    65             }
    66         }, 1000, 3000);
    67     }
    68 }
    服务器端修改

      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.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                 //传输数据需要以 
     结尾,否则不被认为是结束。客户端和服务器端都一样
    34                 writeToService.write(message+"
    ");
    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         //1、首先是需要监听,所以是长时间操作,但是不能阻塞主线程的操作,所以使用子线程来进行处理
    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 }
    客户端修改

      3、解决③问题,即服务器只能与一个客户端进行连接的问题,思路是循环调用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+"
    ");
    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("消息发送测试!
    ");
    84                     writer.flush();
    85                 } catch (Exception e) {
    86                     e.printStackTrace();
    87                 }
    88             }
    89         }, 1000, 3000);
    90     }
    91 }
    服务器端再次修改

    到此为止,使用原生的Socket进行消息推送的基本尝试已经完成,但是其还是有很多问题,例如:

      1、网络操作都是阻塞式实现的,所以不得不采用子线程来进行处理,当连接的用户过多时,性能就称为一个极大的瓶颈。

      2、对各种流的处理等等操作。

    在Java1.4版本之后就引入了nio包,即new IO,用来进行改善,但是操作比较麻烦。而已经有了大神对底层操作的封装框架,如Mina和Netty等,下一部分就是对Mina的体验使用。

  • 相关阅读:
    EXTI—外部中断/事件控制器
    启动文件讲解
    SysTick—系统定时器
    USART—串口通讯
    关于引脚复用,不得不提的GPIO_Pin_sources 和GPIO_Pin
    DMA—直接存储区访问
    【LightOJ1282】Leading and Trailing(数论)
    【LightOJ1336】Sigma Function(数论)
    【LightOJ1370】Bi-shoe and Phi-shoe(欧拉函数)
    【BZOJ2440】完全平方数(二分答案,莫比乌斯反演)
  • 原文地址:https://www.cnblogs.com/songfeilong2325/p/5546803.html
Copyright © 2020-2023  润新知