前段时间用bio方式,也就是传统io实现了socket的长连接和心跳,总觉着服务端开启多线程管理socket连接的方式过于消耗资源,数据并发的情况下可能会影响到性能,因此就尝试使用nio改进原来的代码。

然而改进的过程却不像我起初设想的那般容易,可以说一波三折,原因主要是nio读写都是字节流,LZ一开始依然通过ObjectOutputStream.writeObject直接向Socket服务端发送数据,然而问题出现了,每次从ByteBuffer解析出来字节流都不一样,LZ使出浑身解数,一个字节一个字节的读取啊,问题没有了,可是由于是长连接,数据怎么解析啊,查资料,找大神,最后一个网友说有可能是粘包和分包的问题,一时晕菜,LZ网络可是渣渣啊,行吧,恶补一番,想了解的童鞋可以看看这个。http://blog.csdn.net/sunmenggmail/article/details/38952131

实现原理就像很多协议那样,自定义一套传输协议,比如消息长度(int型,4个字节)+消息体的方式,根据解析的消息长度定长解析消息内容,虽然最后证明LZ的问题不是由于粘包和分包造成的,但是LZ就这样歪打正着,给实现了!!!数据不正常的问题后来通过DataOutputStream和DataInputStream的方式也得到了解决。

废话多了,帖代码。

服务端:

package com.feng.test.longconnection1;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.nio.charset.Charset;

import java.util.Arrays;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.concurrent.BlockingQueue;

import java.util.concurrent.LinkedBlockingDeque;

import org.apache.commons.lang.ArrayUtils;

/**

*

* @author songfeng

* @version 1.0

* @since 2015-10-24

* @category com.feng.test.longconnection

*

*/

public class Server

{

private Map heatTimeMap = new HashMap();

public Server(int port)

{

Selector selector = null;

ServerSocketChannel serverChannel = null;

try

{

//获取一个ServerSocket通道

serverChannel = ServerSocketChannel.open();

serverChannel.configureBlocking(false);

serverChannel.socket().bind(new InetSocketAddress(port));

//获取通道管理器

selector = Selector.open();

//将通道管理器与通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,

//只有当该事件到达时,Selector.select()会返回,否则一直阻塞。

serverChannel.register(selector, SelectionKey.OP_ACCEPT);

while (selector.select() > 0)

{

//选择注册过的io操作的事件

Iterator it = selector.selectedKeys().iterator();

while (it.hasNext())

{

SelectionKey readyKey = it.next();

//删除已选key,防止重复处理

it.remove();

if (readyKey.isAcceptable())

{

ServerSocketChannel serverSocketChannel = (ServerSocketChannel) readyKey.channel();

SocketChannel socketChannel = serverSocketChannel.accept();

socketChannel.configureBlocking(false);

// 连接成功后,注册接收服务器消息的事件

socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);

}

else if(readyKey.isReadable())

{

SocketChannel socketChannel = (SocketChannel)readyKey.channel();

Object obj = receiveData(socketChannel);

String msg = "Server back:";

if(obj instanceof String)

{

String id = obj.toString().split(",")[0];

if(heatTimeMap.get(id) != null

&& System.currentTimeMillis() - heatTimeMap.get(id) > 5000)

{

socketChannel.socket().close();

}

else

{

heatTimeMap.put(id, System.currentTimeMillis());

}

long time = System.currentTimeMillis();

msg += time + "\n";

sendData(socketChannel, msg);

}

else if(obj instanceof Pojo)

{

msg += ((Pojo)obj).getName() + "\n";

sendData(socketChannel, msg);

}

}

}

}

}

catch (Exception e)

{

e.printStackTrace();

}

finally

{

try

{

selector.close();

if(serverChannel != null)

{

serverChannel.close();

}

}

catch (Exception e)

{

e.printStackTrace();

}

}

}

private static Object receiveData(SocketChannel socketChannel)

{

Object obj = null;

ByteArrayOutputStream baos = new ByteArrayOutputStream();

ByteBuffer intBuffer = ByteBuffer.allocate(4);

ByteBuffer objBuffer = ByteBuffer.allocate(1024);

int size = 0;

int sum = 0;

int objlen = 0;

byte[] bytes = null;

try

{

while((size = socketChannel.read(intBuffer)) > 0)

{

intBuffer.flip();

bytes = new byte[size];

intBuffer.get(bytes);

baos.write(bytes);

intBuffer.clear();

if(bytes.length == 4)

{

objlen = bytesToInt(bytes,0);

}

if(objlen > 0)

{

byte[] objByte = new byte[0];

while(sum != objlen)

{

size = socketChannel.read(objBuffer);

if(size > 0)

{

objBuffer.flip();

bytes = new byte[size];

objBuffer.get(bytes,0,size);

baos.write(bytes);

objBuffer.clear();

objByte = ArrayUtils.addAll(objByte, bytes);

sum += bytes.length;

}

}

obj = ByteToObject(objByte);

break;

}

}

}

catch (Exception e)

{

e.printStackTrace();

}

finally

{

try

{

baos.close();

}

catch (Exception e)

{

e.printStackTrace();

}

}

return obj;

}

private static void sendData(SocketChannel socketChannel,Object obj)

{

byte[] bytes = ObjectToByte(obj);

ByteBuffer buffer = ByteBuffer.wrap(bytes);

try

{

socketChannel.write(buffer);

}

catch (IOException e)

{

e.printStackTrace();

}

}

/**

* byte数组中取int数值,本方法适用于(低位在前,高位在后)的顺序。

*

* @param ary

* byte数组

* @param offset

* 从数组的第offset位开始

* @return int数值

*/

public static int bytesToInt(byte[] ary, int offset) {

int value;

value = (int) ((ary[offset]&0xFF)

| ((ary[offset+1]<<8) & 0xFF00)

| ((ary[offset+2]<<16)& 0xFF0000)

| ((ary[offset+3]<<24) & 0xFF000000));

return value;

}

public static Object ByteToObject(byte[] bytes)

{

Object obj = null;

try

{

// bytearray to object

ByteArrayInputStream bi = new ByteArrayInputStream(bytes);

ObjectInputStream oi = new ObjectInputStream(bi);

obj = oi.readObject();

bi.close();

oi.close();

}

catch (Exception e)

{

//e.printStackTrace();

}

return obj;

}

public static byte[] ObjectToByte(Object obj)

{

byte[] bytes = null;

try

{

// object to bytearray

ByteArrayOutputStream bo = new ByteArrayOutputStream();

ObjectOutputStream oo = new ObjectOutputStream(bo);

oo.writeObject(obj);

bytes = bo.toByteArray();

bo.close();

oo.close();

}

catch (Exception e)

{

e.printStackTrace();

}

return bytes;

}

public static void main(String[] args)

{

Server server = new Server(55555);

}

}

客户端:

package com.feng.test.longconnection1;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.net.Socket;

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.util.ArrayList;

/**

*

* @author songfeng

* @version 1.0

* @since 2015-10-24

* @category com.feng.test.longconnection

*

*/

public class Client

{

private Socket socket;

private String ip;

private int port;

private String id;

DataOutputStream dos;

DataInputStream dis;

public Client(String ip, int port,String id)

{

try

{

this.ip = ip;

this.port = port;

this.id = id;

this.socket = new Socket(ip, port);

//this.socket.setKeepAlive(true);

dos = new DataOutputStream(socket.getOutputStream());

dis = new DataInputStream(socket.getInputStream());

new Thread(new heartThread()).start();

new Thread(new MsgThread()).start();

}

catch (Exception e)

{

e.printStackTrace();

}

}

public void sendMsg(Object content)

{

try

{

int len = ObjectToByte(content).length;

ByteBuffer dataLenBuf = ByteBuffer.allocate(4);

dataLenBuf.order(ByteOrder.LITTLE_ENDIAN);

dataLenBuf.putInt(0, len);

dos.write(dataLenBuf.array(), 0 , 4);

dos.flush();

dos.write(ObjectToByte(content));

dos.flush();

}

catch (Exception e)

{

e.printStackTrace();

closeSocket();

}

}

public void closeSocket()

{

try

{

socket.close();

dos.close();

dis.close();

}

catch (IOException e)

{

e.printStackTrace();

}

}

public static byte[] ObjectToByte(Object obj)

{

byte[] bytes = null;

try

{

// object to bytearray

ByteArrayOutputStream bo = new ByteArrayOutputStream();

ObjectOutputStream oo = new ObjectOutputStream(bo);

oo.writeObject(obj);

bytes = bo.toByteArray();

bo.close();

oo.close();

}

catch (Exception e)

{

e.printStackTrace();

}

return bytes;

}

public static Object ByteToObject(byte[] bytes)

{

Object obj = null;

try

{

// bytearray to object

ByteArrayInputStream bi = new ByteArrayInputStream(bytes);

ObjectInputStream oi = new ObjectInputStream(bi);

obj = oi.readObject();

bi.close();

oi.close();

}

catch (Exception e)

{

e.printStackTrace();

}

return obj;

}

class heartThread implements Runnable

{

@Override

public void run()

{

while(true)

{

try

{

Thread.sleep(1000);

long time = System.currentTimeMillis();

//System.out.println("client send:" + time);

sendMsg("Client" + id + "," + time);

}

catch (Exception e)

{

e.printStackTrace();

}

}

}

}

class MsgThread implements Runnable

{

@Override

public void run()

{

int temp;

while(true)

{

try

{

if(socket.getInputStream().available() > 0)

{

byte[] bytes = new byte[1024];

int len = 0;

while((char)(temp = dis.read()) != '\n')

{

bytes[len]=(byte)temp;

len++;

}

System.out.println(ByteToObject(bytes));

}

}

catch (Exception e)

{

closeSocket();

}

}

}

}

public static void main(String[] args)

{

Client client1 = new Client("127.0.0.1", 55555, "1");

client1.sendMsg(new Pojo("songfeng", 26, new ArrayList()));

try

{

Thread.sleep(500);

}

catch (InterruptedException e)

{

e.printStackTrace();

}

Client client2 = new Client("127.0.0.1", 55555, "2");

}

}

数据类:

package com.feng.test.longconnection1;

import java.io.Serializable;

import java.util.List;

/**

*

* @author songfeng

* @version 1.0

* @since 2015-10-16

* @category com.feng.test.longconnection

*

*/

public class Pojo implements Serializable

{

/**

* 序列化

*/

private static final long serialVersionUID = -8868529619983791261L;

private String name;

private int age;

private List likeThing;

public Pojo(String name, int age, List likeThing)

{

super();

this.name = name;

this.age = age;

this.likeThing = likeThing;

}

public String getName()

{

return name;

}

public void setName(String name)

{

this.name = name;

}

public int getAge()

{

return age;

}

public void setAge(int age)

{

this.age = age;

}

public List getLikeThing()

{

return likeThing;

}

public void setLikeThing(List likeThing)

{

this.likeThing = likeThing;

}

}

java nio socket长连接_nio实现Socket长连接和心跳相关推荐

  1. Java NIO学习系列二:Channel

    上文总结了Java NIO中的Buffer相关知识点,本文中我们来总结一下它的好兄弟:Channel.上文有说到,Java NIO中的Buffer一般和Channel配对使用,NIO中的所有IO都起始 ...

  2. java前端长连接框架_Java中Spring Boot+Socket实现与html页面的长连接实例详解

    Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码 功能介绍 客户端给所有在线用户发送消息客户端给指定在线用户发送消息服务器 ...

  3. java nio.2群发_JAVA NIO TCP SOCKET 聊天群发

    以前都是用一般的socket编程,用线程来控制.最近突然用nio来做些东西. nio的好处我来说一下:第一,读写都是基于块的,效率高.第二,通过引入selector,简化了网络编程模型,异步非阻塞. ...

  4. 基于Java NIO的Socket通信

    基于Java NIO的Socket通信 Java NIO模式的Socket通信,是一种同步非阻塞IO设计模式,它为Reactor模式实现提供了基础. 下面看看,Java实现的一个服务端和客户端通信的例 ...

  5. Java NIO编写Socket服务器的一个例子

    最近一直在忙着JAVA NIO的知识,花了一下午的时间,总算写出了一个可以运行的程序,废话少说,上代码! Java代码: import java.io.IOException; import java ...

  6. Java NIO操作Socket的用法

    我们都知道TCP是面向连接的传输层协议,一个socket必定会有绑定一个连接,在普通的BIO(阻塞式IO)中,需要有三次握手,然后一般的socket编程就是这样的形式. Socket服务器端流程如下: ...

  7. java nio 传统标准io socket 和nio socket比较与学习

    在计算机系统中,最不可靠的就是网络请求,我们通过服务器端给客户端echo信息(客户端请求什么信息服务端就返回给客户端什么信息).比较两种socket io的优劣. 标准io socket:     服 ...

  8. java nio socket

    2002 年 3 月 12 日 Java 技术平台早就应该提供非阻塞 I/O 机制了.幸运的是,Merlin(JDK 1.4)有一根几乎在各个场合都适用的魔杖,而解除阻塞了的 I/O 的阻塞状态正是这 ...

  9. 详述 Java NIO 以及 Socket 处理粘包和断包方法

    文章目录 Java NIO 通道 缓冲区 代码示例 第一部分 第二部分 选择器 Socket 处理粘包 & 断包问题 第一个问题:对于粘包问题的解决 第二个问题:对于断包问题的解决 示例代码 ...

最新文章

  1. java中的方法在哪里_Java中的本机方法是什么,应在哪里使用?
  2. c语言飞扬的小鸟程序,C语言实现flappy bird游戏
  3. 计算机网络学习笔记(18. 网络应用(层)内容概述)
  4. linux机顶盒线刷教程,机顶盒刷机教程 机顶盒刷机步骤详解与注意事项
  5. 初学CODEBLOCK+wxWidgets
  6. 计算机网络原理实验实验一:使用网络协议分析仪Wireshark
  7. C语言平年闰年该月天数,2. 输入年月日,求它是该年的第几天。注:闰年的2月有29天,平年的2月有28天。(C语言编程题)...
  8. linux 从设备 spi,在Linux 4.9设备树中添加SPI slave设备rapbery pi
  9. 关于win11右键的慢问题的建议
  10. lower_bound( )和upper_bound( )的常见用法
  11. gensim : AttributeError: The vocab attribute was removed from KeyedVector in Gensim 4.0.0.
  12. 如何正确的撰写软文的结尾?
  13. SEO人员,怎么将网站关键词排进SERP前十名?
  14. 《SpringCloud Alibaba 微服务架构》专题(二十一)-Seat简介与安装
  15. 基于向量加权平均算法的函数寻优和工程优化
  16. 西班牙足球联赛体系介绍
  17. 基于讯飞AI接口的物体识别 详细教程
  18. cpc按点击计算怎么算_CPC怎么计算点击次数?
  19. 关于百度快照更新及与权重是否有关联的百度官方答复
  20. 数据结构与算法——经典题目(5)锻造兵器

热门文章

  1. 《Excel 职场手册:260招菜鸟变达人》一第 13 招 利用数据验证给单元格添加注释,不用批注...
  2. COM组件的运行机制
  3. 第四章 大网高级   NSSA
  4. CentOS7.0 安装 tomcat-9.0
  5. [文档].Altera - Avalon接口规范
  6. netflix_Netflix的计算因果推论
  7. 计算机应用基础学生自查报告,计算机应用基础(专科).docx
  8. plsql中导入csvs_在命令行中使用sql分析csvs
  9. 423. 从英文中重建数字
  10. leetcode 81. 搜索旋转排序数组 II(二分查找)