近期项目中需要实现服务器端实时向客户端推送消息,客户端接收消息做完处理以后再返回服务端,也就是要实现服务器端和客户端实时的双向通信。
使用有线的双向推送:
基于USB HID通信协议:
需要在PC端和安卓端商定好传输数据格式:
在PC端运行发送和接受文件的驱动,实现可通过网页调用PC端的驱动;
在Android端需要打开USB 和HID设备,打开USB和HID,打开接收和发送的接口,关闭通道。
- 打开设备
public boolean open() {
File fd = new File("/dev/hidg0");
try {
outHid = new FileOutputStream(fd);
inHid = new FileInputStream(fd);
return true;
} catch (FileNotFoundException e2) {
Log.i("lbl", "HidOpenFill");
e2.printStackTrace();
return false;
}
}
- 发送数据
public void sendOneFrame(byte sendBuf[]) {
try {
byte[] send = new byte[1024];
System.arraycopy(sendBuf, 0, send, 0, sendBuf.length);
this.outHid.write(send);
} catch (IOException e) {
e.printStackTrace();
}
}
- 读取报文
public int readReport(byte RxBuf[]) {
int ret = -1;
if (null != this.inHid) {
try {
for (int i = 0; i < 1024; i++) {
RxBuf[i] = 0;
}
ret = this.inHid.read(RxBuf, 0, 1024);
} catch (Exception e) {
e.printStackTrace();
}
}
return ret;
}
4.关闭文件流
public boolean close() {
try {
if (outHid != null)
outHid.close();
if (inHid != null)
inHid.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
基于无线网络的双向推送:
同一个网段中可以使用
Socket:
接受文件的一方作为socketServer,接受socketClient发送的文件。
接受到文件后,解析出socketClient的IP地址,将处理完成的文件通过HTTP的形式传输到PC端。
try {
Socket socket = new Socket("192.168.10.147",11900);
OutputStream os = (OutputStream) socket.getOutputStream();//字节输出流
PrintWriter pw =new PrintWriter(os);//将输出流包装成打印流
pw.write("用户名:admin;密码:admin");
pw.flush();
socket.shutdownOutput();
pw.close();
os.close();
socket.close();
System.out.println("send ok");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ServerSocket serverSocket = null;
try {
int port = 11900;
InetSocketAddress address;
address = new InetSocketAddress(port);
serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(address);
} catch (Exception e) {
e.printStackTrace();
}
while (true) {
BufferedReader in = null;
Socket s = null;
try {
s = serverSocket.accept();
InetAddress clienAdr = s.getInetAddress();
System.out.println(clienAdr.getHostAddress());
in = new BufferedReader(new InputStreamReader(
s.getInputStream()));
StringBuffer result = new StringBuffer();
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
s.shutdownInput();
in.close();
in = null;
s.close();
s = null;
System.out.println(result.toString());
} catch (Exception e) {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
in = null;
}
if (s != null) {
try {
s.close();
} catch (IOException e1) {
e1.printStackTrace();
}
s = null;
}
}
}
Netty
可以设置通信协议和通信通道,可实现根据不同的协议实现数据的传输;
public class NettyClient {
private String host;
private int port;
private String msg;
private static final int HEARTBEAT_INTERVAL = 5; // 心跳间隔时间,单位:秒
public NettyClient(String host, int port,String msg) {
this.host = host;
this.port = port;
this.msg = msg;
}
public void run() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(host, port)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
public void initChannel(NioSocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new IdleStateHandler(0, 0, HEARTBEAT_INTERVAL, TimeUnit.SECONDS)); // 添加心跳处理器
ch.pipeline().addLast(new NettyClientHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
channelFuture.channel().writeAndFlush(msg).sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new NettyClient("192.168.10.160", 11900,"hello,server").run();
}
}
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("你好,服务器!",CharsetUtil.UTF_8));
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf= (ByteBuf) msg;
System.out.println("接收到服务端响应:"+byteBuf.toString(CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
// 用来触发特殊事件
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception{
IdleStateEvent event = (IdleStateEvent) evt;
// 触发了读空闲事件
if (event.state() == IdleState.READER_IDLE) {
ctx.channel().close();
}
}
}
public class NettyServer implements Runnable {
private int port;
private SendMessage sendMessage;
private Handler handler;
public NettyServer(int port,Handler handler, SendMessage sendMessage) {
this.port = port;
this.sendMessage = sendMessage;
this.handler = handler;
}
public NettyServer() {
}
public void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.childOption(ChannelOption.SO_KEEPALIVE, true);
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
// .childHandler(new MyChannelInitializer(sendMessage,handler));
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
public void initChannel(NioSocketChannel channel){
channel.pipeline().addLast(new StringDecoder());
//添加心跳检查包
channel.pipeline().addLast(new IdleStateHandler(5,0,0, TimeUnit.SECONDS));
channel.pipeline().addLast(new MyServerHandler(handler,sendMessage));
}
});
b.bind(port).sync().channel().closeFuture().sync();
} catch (Exception e){
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
public class MyServerHandler extends ChannelInboundHandlerAdapter {
private SendMessage sendMessage;
private Handler handler;
public MyServerHandler(Handler handler,SendMessage sendMessage) {
this.sendMessage = sendMessage;
this.handler = handler;
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception{
IdleStateEvent event = (IdleStateEvent) evt;
// 触发了读空闲事件
if (event.state() == IdleState.READER_IDLE) {
ctx.channel().close();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println(msg);
String res = handMsg(msg);
ctx.writeAndFlush(res);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//发送消息给客户端
ctx.writeAndFlush(Unpooled.copiedBuffer("服务端已收到消息,并给你发送一个问号?", CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
private String handMsg(Object msg){
String revdata = msg.toString();
String outbytes = "";
if (sendMessage != null ) {
outbytes = sendMessage.SendByteData(handler,revdata);
}
return outbytes;
}
}
服务器和APP处于不同的网段,可以使用MPush实现实时的通讯。