最近闲来无事想做个小型的聊天工具,客户端用android已经做好了,跟服务器的交互不想使用Http的方式,改用socket长连接,这样的话消息能及时送达就避免了http不断轮询的消耗。
网上关于Java NIO大部分都是些基础的例子和结构,关于长连接的实在是很少,
现在已经能正常监听请求和读写内容,但是没办法让服务器发送消息到指定的一台或若干台客户端,老的JavaIO可以通过保存Socket,但是nio每次事件都是一个新的SelectionKey,保存了也是旧的(目前的理解),请教各位,如何才能保存下跟每个客户端的会话呢,如下是部分代码public class SeekServer extends Thread{
private final int ACCPET_PORT = 55555;
private final int TIME_OUT = 3000;
private Selector mSelector = null;
private ServerSocketChannel mSocketChannel = null;
private ServerSocket mServerSocket = null;
private InetSocketAddress mAddress = null;
public SeekServer() {
long sign = System.currentTimeMillis();
try {
mSocketChannel = ServerSocketChannel.open();
if(mSocketChannel == null) {
System.out.println("can't open server socket channel");
}
mServerSocket = mSocketChannel.socket();
mAddress = new InetSocketAddress(ACCPET_PORT);
mServerSocket.bind(mAddress);
Log.i("server bind port is " + ACCPET_PORT);
mSelector = Selector.open();
mSocketChannel.configureBlocking(false);
SelectionKey key = mSocketChannel.register(mSelector, SelectionKey.OP_ACCEPT);
key.attach(new Acceptor());
Log.i("Seek server startup in " + (System.currentTimeMillis() - sign) + "ms!");
} catch (ClosedChannelException e) {
Log.e(e.getMessage());
} catch (IOException e) {
Log.e(e.getMessage());
}
}
public void run() {
Log.i("server is listening...");
while(!Thread.interrupted()) {
try {
if(mSelector.select(TIME_OUT) > 0) {
Log.i("find a new selection key");
Set<SelectionKey> keys = mSelector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
SelectionKey key = null;
while(iterator.hasNext()) {
key = iterator.next();
Runnable at = (Runnable) key.attachment();
if(at != null) {
at.run();
}
keys.clear();
}
}
} catch (IOException e) {
Log.e(e.getMessage());
}
}
} class Acceptor implements Runnable{ public void run(){
try {
SocketChannel sc = mSocketChannel.accept();
new Handler(mSelector, sc);//开始处理这个Channel
} catch (ClosedChannelException e) {
Log.e(e);
} catch (IOException e) {
Log.e(e);
}
}
}
}
网上关于Java NIO大部分都是些基础的例子和结构,关于长连接的实在是很少,
现在已经能正常监听请求和读写内容,但是没办法让服务器发送消息到指定的一台或若干台客户端,老的JavaIO可以通过保存Socket,但是nio每次事件都是一个新的SelectionKey,保存了也是旧的(目前的理解),请教各位,如何才能保存下跟每个客户端的会话呢,如下是部分代码public class SeekServer extends Thread{
private final int ACCPET_PORT = 55555;
private final int TIME_OUT = 3000;
private Selector mSelector = null;
private ServerSocketChannel mSocketChannel = null;
private ServerSocket mServerSocket = null;
private InetSocketAddress mAddress = null;
public SeekServer() {
long sign = System.currentTimeMillis();
try {
mSocketChannel = ServerSocketChannel.open();
if(mSocketChannel == null) {
System.out.println("can't open server socket channel");
}
mServerSocket = mSocketChannel.socket();
mAddress = new InetSocketAddress(ACCPET_PORT);
mServerSocket.bind(mAddress);
Log.i("server bind port is " + ACCPET_PORT);
mSelector = Selector.open();
mSocketChannel.configureBlocking(false);
SelectionKey key = mSocketChannel.register(mSelector, SelectionKey.OP_ACCEPT);
key.attach(new Acceptor());
Log.i("Seek server startup in " + (System.currentTimeMillis() - sign) + "ms!");
} catch (ClosedChannelException e) {
Log.e(e.getMessage());
} catch (IOException e) {
Log.e(e.getMessage());
}
}
public void run() {
Log.i("server is listening...");
while(!Thread.interrupted()) {
try {
if(mSelector.select(TIME_OUT) > 0) {
Log.i("find a new selection key");
Set<SelectionKey> keys = mSelector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
SelectionKey key = null;
while(iterator.hasNext()) {
key = iterator.next();
Runnable at = (Runnable) key.attachment();
if(at != null) {
at.run();
}
keys.clear();
}
}
} catch (IOException e) {
Log.e(e.getMessage());
}
}
} class Acceptor implements Runnable{ public void run(){
try {
SocketChannel sc = mSocketChannel.accept();
new Handler(mSelector, sc);//开始处理这个Channel
} catch (ClosedChannelException e) {
Log.e(e);
} catch (IOException e) {
Log.e(e);
}
}
}
}
SocketChannel channel = (SocketChannel) key.channel();
channel.register(ioSelector, SelectionKey.OP_WRITE, "这是传回的数据.");
其中第三个参数就是你要转给客户端的数据封装,接着你只需要在写事件的响应方法中写输出逻辑就行了.这里简单用了字符串.
private void writeable(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
byte[] data = ((String) key.attachment()).getByte("utf8); int len = 0;
ByteBuffer buff = ByteBuffer.wrap(data,0,data.length);
len = socketChannel.write(buff); if (len == EOF_FLAG) {
//认为客户端已经被关闭了
throw new ConnectException("close channel.");
} //判断数据没有输出完的处理
}
这里有一点要注意,因为是异步的IO所以数据不能保证一次write就能输出完.所以记录下已经输出的字节数,然后保存下
剩下的数据以便下一次写事件时输出.最后数据完全输出完毕后建议将写事件从这个管道中去掉,不然一直有写就绪的事件浪费CPU.
key.interestOps(SelectionKey.OP_READ);
这句话就会接收读就绪事件了.
比如有1-10个用户,我想把消息发送给2,3,4 我就得事先保存下SocketChannel,然后去这个集合里找2,3,4对应的SocketChannel,是不是切换读写事件就ok了呢?
对 项目里下一步就会有心跳包,但首先我要把这些Channel存起来,然后去给这些Channel写数据,比较不理解的是每次都要切换read和write,不知道这样写会不会有什么问题
我也做过,但我在服务器端保存客户端会话,而且是nio
你是需要对指定的SocketChannel发送数据,当然你保存的时候必须对这些SocketChannel进行标识。比如对每一个连接会产生一个唯一的ID,再利用一个Map来持有。
是应该定义一个全局的map存储链接标识以及对应的SocketChannel。
连Mina底层的实现都不知道的话就直接去使用,感觉有点不充实
传送门
以前貌似听说过Tomcat7有这种功能哈,有相关的例子吗跪求
帮忙顶一下~~
帮忙顶一下~~
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
import java.nio.channels.SelectableChannel;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetSocketAddress;
import java.util.Iterator;public class Server {
public static final int PORT=8033;
public static void main(String [] args) throws IOException{
Server s=new Server();
s.go();
}
private static ByteBuffer buff=ByteBuffer.allocateDirect(1024);
public void go() throws IOException {
ServerSocketChannel serverChannel=ServerSocketChannel.open();
ServerSocket serverSocket=serverChannel.socket();
serverSocket.bind(new InetSocketAddress(PORT));
System.out.println("server start to listen port"+PORT);
Selector selector =Selector.open();
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true){
int n=selector.select();
if(n==0){
continue;
}
else{
// System.out.println(n+"complicated");
Iterator it =selector.selectedKeys().iterator();
while( it.hasNext()){
SelectionKey key=(SelectionKey) it.next();
if(key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel=server.accept();
registerChannel(selector, channel, SelectionKey.OP_READ);
sayHello(channel);
}
if (key.isReadable())
{
readDataFromSocket(key);
}
it.remove();
}
}
}
}
private void readDataFromSocket(SelectionKey key) throws IOException {
SocketChannel channel=(SocketChannel)key.channel();
int count =0;
buff.clear();
while((count=channel.read(buff))>0){
buff.flip();
while(buff.hasRemaining()){
channel.write(buff);
}
buff.clear();
}
if(count<0)
channel.close();
}private void sayHello(SocketChannel channel) throws IOException {
buff.clear();
buff.put("hello\r\n".getBytes());
buff.flip();
channel.write(buff);
}private void registerChannel(Selector selector, SocketChannel channel,
int opRead) throws IOException {
if(channel==null)
return;
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
}}
获得 SocketChannel 直接 注册感兴趣的事件就是了
[email protected] 公司不让用扣扣,你懂的