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

以前都是用一般的socket编程,用线程来控制。最近突然用nio来做些东西。

nio的好处我来说一下:第一,读写都是基于块的,效率高。第二,通过引入selector,简化了网络编程模型,异步非阻塞。

既然有这么多好处,那就写个NIO TCP网络聊天室来练练手吧。

因为没有写gui,是基于控制台的所以没写私了的部分,只写了公共聊天室。(其实,既然是服务器端可以分发给所有人,分发给特定人也是很容易实现的。

注意:这里只是为了练手,联系服务器端分发消息到各个客户端。TCP 来写聊天室,在现实中是不可取的。IM都是基于UDP来写的。

先上代码吧。

服务器端代码 MySocketServer.java

/*

* To change this template, choose Tools | Templates

* and open the template in the editor.

*/

package com.kevin.nio;

import java.io.IOException;

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.util.Date;

import java.util.Iterator;

import java.util.logging.Level;

import java.util.logging.Logger;

/**

*

* @author kevin

*/

public class MySocketServer implements Runnable{

private boolean running;

private Selector selector;

String writeMsg;

StringBuffer sb=new StringBuffer();

SelectionKey ssckey;

public MySocketServer(){

running=true;

}

public void init(){

try {

selector = Selector.open();

ServerSocketChannel ssc = ServerSocketChannel.open();

ssc.configureBlocking(false);

ssc.socket().bind(new InetSocketAddress(2345));

ssckey=ssc.register(selector, SelectionKey.OP_ACCEPT);

System.out.println("server is starting..."+new Date());

} catch (IOException ex) {

Logger.getLogger(MySocketServer.class.getName()).log(Level.SEVERE, null, ex);

}

}

public static void main(String[] args){

MySocketServer server=new MySocketServer();

new Thread(server).start();

}

public void execute(){

try {

while(running){

int num=selector.select();

if(num>0){

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

while(it.hasNext()){

SelectionKey key=it.next();

it.remove();

if(!key.isValid()) continue;

if(key.isAcceptable()){

System.out.println("isAcceptable");

getConn(key);

}

else if(key.isReadable()){

System.out.println("isReadable");

readMsg(key);

}

else if(key.isValid()&&key.isWritable()){

if(writeMsg!=null){

System.out.println("isWritable");

writeMsg(key);

}

}

else break;

}

}

Thread.yield();

}

} catch (IOException ex) {

Logger.getLogger(MySocketServer.class.getName()).log(Level.SEVERE, null, ex);

}

}

private void getConn(SelectionKey key) throws IOException {

ServerSocketChannel ssc=(ServerSocketChannel)key.channel();

SocketChannel sc=ssc.accept();

sc.configureBlocking(false);

sc.register(selector, SelectionKey.OP_READ);

System.out.println("build connection :"+sc.socket().getRemoteSocketAddress());

}

private void readMsg(SelectionKey key) throws IOException {

sb.delete(0, sb.length());

SocketChannel sc=(SocketChannel)key.channel();

System.out.print(sc.socket().getRemoteSocketAddress()+" ");

ByteBuffer buffer=ByteBuffer.allocate(1024);

buffer.clear();

int len=0;

StringBuffer sb=new StringBuffer();

while((len=sc.read(buffer))>0){

buffer.flip();

sb.append(new String(buffer.array(),0,len));

}

if(sb.length()>0) System.out.println("get from client:"+sb.toString());

if(sb.toString().trim().toLowerCase().equals("quit")){

sc.write(ByteBuffer.wrap("BYE".getBytes()));

System.out.println("client is closed "+sc.socket().getRemoteSocketAddress());

key.cancel();

sc.close();

sc.socket().close();

}

else{

String toMsg=sc.socket().getRemoteSocketAddress()+ "said:"+sb.toString();

System.out.println(toMsg);

writeMsg=toMsg;

/*

Iterator it=key.selector().keys().iterator();

while(it.hasNext()){

SelectionKey skey=it.next();

if(skey!=key&&skey!=ssckey){

SocketChannel client=(SocketChannel) skey.channel();

client.write(ByteBuffer.wrap(toMsg.getBytes()));

}

}

*

*/

/*

key.attach(toMsg);

key.interestOps(key.interestOps()|SelectionKey.OP_WRITE);

*

*/

Iterator it=key.selector().keys().iterator();

while(it.hasNext()){

SelectionKey skey=it.next();

if(skey!=key&&skey!=ssckey){

if(skey.attachment()!=null){

String str=(String) skey.attachment();

skey.attach(str+toMsg);

}else{

skey.attach(toMsg);

}

skey.interestOps(skey.interestOps()|SelectionKey.OP_WRITE);

}

}

selector.wakeup();//可有可无

}

}

public void run() {

init();

execute();

}

private void writeMsg(SelectionKey key) throws IOException {

System.out.println("++++enter write+++");

SocketChannel sc=(SocketChannel) key.channel();

String str=(String) key.attachment();

sc.write(ByteBuffer.wrap(str.getBytes()));

key.interestOps(SelectionKey.OP_READ);

}

}

客户端:MySocketClient.java

/*

* To change this template, choose Tools | Templates

* and open the template in the editor.

*/

package com.kevin.nio;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

import java.util.logging.Level;

import java.util.logging.Logger;

import java.util.Currency.*;

/**

*

* @author kevin

*/

public class MySocketClient implements Runnable{

Selector selector;

boolean running;

SocketChannel sc;

public MySocketClient(){

running=true;

}

public void init() {

try {

sc = SocketChannel.open();

sc.configureBlocking(false);

sc.connect(new InetSocketAddress("localhost", 2345));

} catch (IOException ex) {

Logger.getLogger(MySocketClient.class.getName()).log(Level.SEVERE, null, ex);

}

}

public static void main(String[] args){

MySocketClient client=new MySocketClient();

new Thread(client).start();

}

public void execute(){

int num=0;

try {

while (!sc.finishConnect()) {

}

} catch (IOException ex) {

Logger.getLogger(MySocketClient.class.getName()).log(Level.SEVERE, null, ex);

}

ReadKeyBoard rkb=new ReadKeyBoard();

new Thread(rkb).start();

while(running){

try {

ByteBuffer buffer=ByteBuffer.allocate(1024);

buffer.clear();

StringBuffer sb=new StringBuffer();

Thread.sleep(500);

while((num=sc.read(buffer))>0){

sb.append(new String(buffer.array(),0,num));

buffer.clear();

}

if(sb.length()>0) System.out.println(sb.toString());

if(sb.toString().toLowerCase().trim().equals("bye")){

System.out.println("closed....");

sc.close();

sc.socket().close();

rkb.close();

running=false;

}

} catch (InterruptedException ex) {

Logger.getLogger(MySocketClient.class.getName()).log(Level.SEVERE, null, ex);

} catch (IOException ex) {

Logger.getLogger(MySocketClient.class.getName()).log(Level.SEVERE, null, ex);

}

}

}

public void run() {

init();

execute();

}

class ReadKeyBoard implements Runnable{

boolean running2=true;

public ReadKeyBoard(){

}

public void close(){

running2=false;

}

public void run() {

BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));

while(running2){

try {

System.out.println("enter some commands:");

String str = reader.readLine();

sc.write(ByteBuffer.wrap(str.getBytes()));

} catch (IOException ex) {

Logger.getLogger(ReadKeyBoard.class.getName()).log(Level.SEVERE, null, ex);

}

}

}

}

}

总结:

1. 服务器端一定注意注册需要的操作,不要注册不需要的操作。比如在连接被接受后,其实scoket是可以读和可以写的。这里的注册读的话,那么意味着,只有真的有数据来了,才会接到消息。但是随时可以写,如果这个时候注册读写的话(sc.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);),那么服务器就会进入cpu 100%的状态。所以当连接刚被接受的时候,一定要注册读sc.register(selector, SelectionKey.OP_READ);

2. 服务器端写有两种方法:一种就是直接写。第二种是加入ATTACH,然后,通过skey.interestOps(skey.interestOps()|SelectionKey.OP_WRITE);把写消息写到interestOps集合中。就出发了可写的通知了。注意第二种方式,在接到可写的通知后,处理完了消息后,还是得恢复只对写有兴趣的interestOps 如key.interestOps(SelectionKey.OP_READ);

3. 得到所有服务器端的连接的方式是key.selector().keys(),但是一定记得,里面有一个是SocketServerChannel注册的key,这个只可以接受连接,其他的什么都做不了。更不能写数据给客户端。所以一定记得剔除这个。不然程序会抛异常。

4. 客户端比较简单,所以可以用select的方式,也可以不用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值