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

一、第一版:

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

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

    ②使用accept(),阻塞式的等待客户端的接入。接入成功时返回连接的Socket对象。通过该对象可以进行数据的交换。

 import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket; public class Service {
public static void main(String[] args) {
Service service=new Service();
service.start();
} private void start() {
ServerSocket socketService=null;
Socket socket=null;
BufferedReader reader=null;
BufferedWriter writer=null;
try {
//开启一个Socket服务器,监听9898端口
socketService=new ServerSocket(9898);
System.out.println("service start...");
//等待客户端连入,一直阻塞直到接入,返回连接Socket对象
socket=socketService.accept();
System.out.println("client connection...");
//以下就是数据交换
reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String message=null;
while(!(message=reader.readLine()).equals("bye")){
System.out.println(message);
writer.write(socket.getLocalAddress()+":"+message+"\n");
writer.flush();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
reader.close();
writer.close();
socket.close();
socketService.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

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

    ①指定服务器和端口来创建Socket连接,

    ②使用该Socket对象来进行数据传输即可,这里是将控制台上的输入内容传递至服务器。需要注意的是读取操作是以"\n“为结束标记的,所以需要传输时需要加上,否则不被认为是结束。

 import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket; public class Client {
public static void main(String[] args) {
//这里创建一个Client对象而不是直接在main()操作,是可以是使用的变量等不必是static类型的
Client client=new Client();
client.start();
} private void start() {
BufferedReader readFromSys=null;
BufferedReader readFromService=null;
BufferedWriter writeToService=null;
Socket socket=null;
try {
//传入地址和端口号,和服务器建立连接
socket=new Socket("127.0.0.1",9898);
//以下是数据传输部分
readFromSys=new BufferedReader(new InputStreamReader(System.in));
readFromService=new BufferedReader(new InputStreamReader(socket.getInputStream()));
writeToService=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); String message;
String responce;
while(!(message=readFromSys.readLine()).equals("bye")){
//传输数据需要以 \n 结尾,否则不被认为是结束。客户端和服务器端都一样
writeToService.write(message+"\n");
writeToService.flush();
responce=readFromService.readLine();
System.out.println(responce);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
writeToService.close();
readFromService.close();
readFromSys.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
} }
}

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

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

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

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

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

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

  1、解决①问题,即服务器端不能主动发送消息的问题,思路是通过Socket连接的输出流进行数据的传递即可。

 import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask; public class Service {
public static void main(String[] args) {
Service service=new Service();
service.start();
} private void start() {
ServerSocket socketService=null;
Socket socket=null;
BufferedReader reader=null;
BufferedWriter writer=null;
try {
//开启一个Socket服务器,监听9898端口
socketService=new ServerSocket(9898);
System.out.println("service start..."); socket=socketService.accept();
System.out.println("client connection..."+socket.hashCode()); try {
reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //!!!服务器向客户端发送消息
sendMessage(writer); String message=null;
while(!(message=reader.readLine()).equals("bye")){
System.out.println(message);
writer.write(socket.getLocalAddress()+":"+message+"\n");
writer.flush();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
socket.close();
socketService.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} private void sendMessage(final BufferedWriter writer) {
//这里简单开启一个定时任务,每个几秒向客户端发送一条消息,进行模拟操作。
new Timer().schedule(new TimerTask() {
@Override
public void run() {
try {
writer.write("消息发送测试!\n");
writer.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}, 1000, 3000);
}
}

服务器端修改

  2、解决②问题,即客户端不能及时读取服务器端消息的问题,思路是对连接Socket的输入流进行监听,一旦有输入立即进行处理。

 import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket; public class Client {
public static void main(String[] args) {
//这里创建一个Client对象而不是直接在main()操作,是可以是使用的变量等不必是static类型的
Client client=new Client();
client.start();
} private void start() {
BufferedReader readFromSys=null;
BufferedReader readFromService=null;
BufferedWriter writeToService=null;
Socket socket=null;
try {
//传入地址和端口号,和服务器建立连接
socket=new Socket("127.0.0.1",9898);
//以下是数据传输部分
readFromSys=new BufferedReader(new InputStreamReader(System.in));
readFromService=new BufferedReader(new InputStreamReader(socket.getInputStream()));
writeToService=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); String message; //用于对服务器端输入的数据流进行监听
socketInputStreamListener(readFromService); while(!(message=readFromSys.readLine()).equals("bye")){
//传输数据需要以 \n 结尾,否则不被认为是结束。客户端和服务器端都一样
writeToService.write(message+"\n");
writeToService.flush();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
writeToService.close();
readFromService.close();
readFromSys.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
} } private void socketInputStreamListener(final BufferedReader readFromService) {
//1、首先是需要监听,所以是长时间操作,但是不能阻塞主线程的操作,所以使用子线程来进行处理
new Thread(new Runnable(){
@Override
public void run() {
try {
String responce;
while(!(responce=readFromService.readLine()).equals(null)){
System.out.println(responce);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}

客户端修改

  3、解决③问题,即服务器只能与一个客户端进行连接的问题,思路是循环调用accept(),该方法用于阻塞式的等待客户端的连接,然后将已经建立的连接的任务放在子线程中进行处理即可。而这里的连接任务就是数据的传递部分。

 import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask; public class Service {
public static void main(String[] args) {
Service service=new Service();
service.start();
} private void start() {
ServerSocket socketService=null;
Socket socket=null;
try {
//开启一个Socket服务器,监听9898端口
socketService=new ServerSocket(9898);
System.out.println("service start..."); //
while(true){
socket=socketService.accept();
System.out.println("client connection..."+socket.hashCode());
exectureConnectRunnable(socket);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
socket.close();
socketService.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} //这里的工作必须在子线程中工作,因为下面有一段死循环操作,如果不在子线程中则会阻塞主线程,不能和其他客户端进行连接。
private void exectureConnectRunnable(final Socket socket) {
new Thread(new Runnable(){
@Override
public void run() {
BufferedReader reader=null;
BufferedWriter writer=null;
//以下就是数据交换
try {
reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //服务器向客户端发送消息
sendMessage(writer); String message=null;
while(!(message=reader.readLine()).equals("bye")){
System.out.println(message);
writer.write(socket.getLocalAddress()+":"+message+"\n");
writer.flush();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
reader.close();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
} private void sendMessage(final BufferedWriter writer) {
//这里简单开启一个定时任务,每个几秒向客户端发送一条消息,进行模拟操作。
new Timer().schedule(new TimerTask() {
@Override
public void run() {
try {
writer.write("消息发送测试!\n");
writer.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}, 1000, 3000);
}
}

服务器端再次修改

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

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

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

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

消息推送学习一、原生Socket的使用的更多相关文章

  1. springboot实现服务器端消息推送(H5原生支持)

    随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功 ...

  2. Windows phone Toast消息推送 学习笔记

    简单介绍: Windows phone平台支持三种形式的推送通知: 1.Tile——也就是在Start屏幕程序平铺图标 2.Toast——创建一个显示在当前屏幕中的Toast弹出窗口 3.Raw——有 ...

  3. HTML5 学习总结(五)——WebSocket与消息推送

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...

  4. HTML5 学习笔记(五)——WebSocket与消息推送

    B/S结构的软件项目中有时客户端需要实时的获得服务器消息,但默认HTTP协议只支持请求响应模式,这样做可以简化Web服务器,减少服务器的负担,加快响应速度,因为服务器不需要与客户端长时间建立一个通信链 ...

  5. java版Web Socket,实现消息推送

    # web socket是什么? WebSocket协议是基于TCP的一种新的网络协议. 它实现了浏览器与服务器全双工(full-duplex)通信,允许服务器主动发送信息给客户端. ## 用途 实时 ...

  6. Java Socket聊天室编程(一)之利用socket实现聊天之消息推送

    这篇文章主要介绍了Java Socket聊天室编程(一)之利用socket实现聊天之消息推送的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 网上已经有很多利用socket实现聊天的例子了 ...

  7. 基于socket.io的实时消息推送

    用户访问Web站点的过程是基于HTTP协议的,而HTTP协议的工作模式是:请求-响应,客户端发出访问请求,服务器端以资源数据响应请求. 也就是说,服务器端始终是被动的,即使服务器端的资源数据发生变化, ...

  8. 【js学习】js连接RabbitMQ达到实时消息推送

    js连接RabbitMQ达到实时消息推送 最近在自己捯饬一个网站,有一个功能是需要后端处理完数据把数据发布到MQ中,前端再从MQ中接收数据.但是前端连接MQ又成了一个问题,在网上搜了下资料,点进去一篇 ...

  9. Socket.io+Notification实现浏览器消息推送

    前言 socket.io: 包含对websocket的封装,可实现服务端和客户端之前的通信.详情见官网(虽然是英文文档,但还是通俗易懂).Notification: Html5新特性,用于浏览器的桌面 ...

随机推荐

  1. WPF 一个空的 WPF 程序有多少个窗口

    原文:WPF 一个空的 WPF 程序有多少个窗口 好多小伙伴说 WPF 的程序有五个窗口,但是我尝试使用了 EnumThreadWindows 去获取的时候居然拿到了 10 多个窗口 在 WPF 内部 ...

  2. HTTP——学习笔记(3)

    HTTP报文:用于HTTP协议交互的信息,客户端的HTTP报文叫做 请求报文,响应端的叫做 响应报文 本质:是由多行(用CR+LF作换行符)数据构成的字符串文本 注:CR:回车,打印针回到行首   L ...

  3. Linux下库文件的设置 (/usr/bin/ld: cannot find -lxxx 的解决办法)

    在软件编译过程中,经常会碰到类似这样的编译错误: /usr/bin/ld: cannot find -lhdf5 这表示找不到库文件 libhdf5.so,若是其它库文件,则是 cannot find ...

  4. Unity 3D本地公布WebPlayer版时"Failed to download data file"解决方式

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGlzZW55YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...

  5. linux 下同步异步,堵塞非堵塞的一些想法

    补充: 发现一个更好的解释样例:同步是一件事我们从头到尾尾随着完毕.异步是别人完毕我们仅仅看结果. 堵塞是完毕一件事的过程中可能会遇到一些情况让我们等待(挂起).非堵塞就是发生这些情况时我们跨过. 比 ...

  6. OCP-1Z0-051-题目解析-第50题

    50. SLS is a private synonym for the SH.SALES table.  The user SH issues the following command:  DRO ...

  7. 利用js与java交互

    为了方便网页和应用的交互,安卓系统WebView提供JavaScript网页脚本调用Java类方法的机制.只要调用addJavascriptInterface方法即可映射一个Java对象到JavaSc ...

  8. 阿里云slb上传证书错误

    阿里云上传证书错误 今天在阿里云给slb上传新买的证书,传的过程中报错了,如下: 网上找了半天没找到,鼠标放在错误哪行行首,会报一个错 大意就是一行最多64个字符,我检查了下,报错这行是68个字符,于 ...

  9. mongodb 的 curd

        增:          db.表名.insert({name:'lisi',age:24});                                                  ...

  10. 与 JSON 相关的一些操作 (项目中用到一部分,后续逐渐完善)

    1.JSON 打印 console.log(JSON.stringify(data, null, 4)); 2.JSON 格式判断 var isjson = typeof(obj) == " ...