消息推送也是客户端和服务器连接然后进行交互的一种形式,但是不同于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. pandas 1 基本介绍

    import numpy as np import pandas as pd pd.Series() 构造数据 s = pd.Series([1, 3, 5, np.nan, 44, 1]) prin ...

  2. 【BZOJ 1150】[CTSC2007]数据备份Backup

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 选择的连接肯定是相邻的点对. 那么我们处理出来长度为n-1的数组a 其中a[i-1] = dis[i]-dis[i-1] 那么问题就 ...

  3. LaTeX argmin argmax 下标使用方法

    本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/50036001 LaTeX中,使用arg ...

  4. 从一次生产事故说起——linux的单用户模式,救援模式等等

    伴随着今年linux上面最大一个安全漏洞bash漏洞的出现,我们公司也開始了风风火火的漏洞修复工作,机器一多,也就easy出问题,有台64位的linuxserver一不小心就升级了32位 bash 的 ...

  5. Bootstrap组件之输入框组

    .input-group--设置div为输入框组: .input-group-lg..input-group-sm..input-group-xs--改变输入框组的尺寸: .input-group-a ...

  6. IIS Express加入MIME映射

    近期在用Grid Report做Web报表的时候,碰到一件非常挠头的事. 本地用VS2010写的代码,调试的时候Web报表无法显示,用24.248server上VS2013相同仍是无法显示.最后把项目 ...

  7. legend---八、php对象如何转换成js对象

    legend---八.php对象如何转换成js对象 一.总结 一句话总结:a.直接转换:b.通过json对象做中间桥梁 1.为什么传递给父亲构造函数的参数不能写默认值? 这里的第三行的比如$type不 ...

  8. 《剑指offer》反转链表

    一.题目描述 输入一个链表,反转链表后,输出链表的所有元素. 二.输入描述 输入一个链表 三.输出描述 返回逆转后的链表 四.牛客网提供的框架 /* struct ListNode { int val ...

  9. 洛谷P2408 不同子串个数 后缀数组 + Height数组

    ## 题目描述: 给你一个长为 $N$ $(N<=10^5)$ 的字符串,求不同的子串的个数我们定义两个子串不同,当且仅当有这两个子串长度不一样 或者长度一样且有任意一位不一样.子串的定义:原字 ...

  10. mongodb 的 curd

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