以前都是用一般的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的方式,也可以不用。

java nio.2群发_JAVA NIO TCP SOCKET 聊天群发相关推荐

  1. java udp 工具类_java基于UDP实现图片群发功能

    UDP协议(用户数据报协议)是一种不可靠的网络协议,它在通信实例的两端各建立一个Socket,但是这两个Socket之间并没有虚拟链路,这两个Socket只是发送,接收数据报的对象. UDP的优缺点: ...

  2. java socket5源码_Java利用TCP协议实现客户端与服务器通信【附通信源码】

    进行TCP协议网络程序的编写,关键在于ServerSocket套接字的熟练使用,TCP通信中所有的信息传输都是依托ServerSocket类的输入输出流进行的. TCP协议概念 先来了解一下TCP协议 ...

  3. java nio 写事件_Java NIO

    java Nio Selector 选择器 Buffer 缓冲器 Channel 通道 Selector是NIO的核心,是channel的管理者,通过执行select()阻塞方式,监听是否有chann ...

  4. java channel源码_java nio ServerSocketChannel源码分析

    /** * 创建新实例 * * @param provider * The provider that created this channel */ protected ServerSocketCh ...

  5. java nio 强制关闭_Java NIO服务器:远程主机强迫关闭了一个现有的连接

    Java NIO聊天室 中,若客户端强制关闭,服务器会报"java.io.IOException: 远程主机强迫关闭了一个现有的连接.",并且服务器会在报错后停止运行,错误的意思就 ...

  6. java nio底层实现_Java NIO 底层原理

    一.概念说明 1.内核态(内核空间)和用户态(用户空间)的区别和联系? 用户空间是用户进程所在的内存区域,系统空间是操作系统所在的内存区域.为了保证内核的安全,处于用户态的程序只能访问用户空间,而处于 ...

  7. java nio空轮循_Java nio 空轮询bug到底是什么

    epoll机制是Linux下一种高效的IO复用方式,相较于select和poll机制来说.其高效的原因是将基于事件的fd放到内核中来完成,在内核中基于红黑树+链表数据结构来实现,链表存放有事件发生的f ...

  8. java nio 如何使用_Java NIO 系列教程(转)

    原文中说了最重要的3个概念, Channel 通道 Buffer 缓冲区 Selector 选择器 其中Channel对应以前的流,Buffer不是什么新东西,Selector是因为nio可以使用异步 ...

  9. java selector 源码_Java NIO——Selector机制源码分析---转

    一直不明白pipe是如何唤醒selector的,所以又去看了jdk的源码(openjdk下载),整理了如下: 以Java nio自带demo : OperationServer.java   Oper ...

最新文章

  1. javascript写坦克大战
  2. 4.5 人脸验证与二分类-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
  3. 概率论-1.3 概率的性质(重点:可列与极限之间的互相转换)
  4. 附加数据库失败,版本为655,此服务器支持611及更低版本。不支持降级路径。...
  5. matlab调用cst计算扫频,CST MWS I算法求解单站RCS是否可以进行扫频设置
  6. Hills And Valleys CodeForces - 1467B 思维
  7. php中统计记录条数,使用GROUP BY的时候如何统计记录条数 COUNT(*) DISTINCT
  8. Linux使用进程id跟踪程序,使用linux的pidof命令返回运行程序的进程ID
  9. livebos数据库
  10. php excel 进度,在php中生成Excel文件时显示进度条
  11. [附源码]java毕业设计房屋租赁管理系统
  12. 求职套题2---各大公司
  13. 台式计算机的主流配置,现在台式电脑主流配置是什么配置?
  14. Facebook 的开源静态分析工具Infer
  15. [予纯 · 星火 - 1] 女生用品与物理化学
  16. WPS文件格式如何进行转换 et格式转换word
  17. csdn涨粉秘籍测评
  18. win10内置linux读取u盘raw,处理U盘0MB和RAW格式问题有绝招
  19. 【性能优化】性能优化
  20. amd显卡测试大风车软件md,AMD显卡也能实现光线追踪?靠谱答案在这里!

热门文章

  1. 四面阿里,看看你都会吗
  2. 【Servlet】request对象获取请求头数据和用户数据
  3. 网络协议之:加密传输中的NPN和ALPN
  4. Python基础之:Python中的IO
  5. 区块链系列教程之:比特币中的挖矿
  6. CentOS 6 安装Hadoop 2.6 (三)问题收集
  7. C++学习笔记:(二)函数重载 常量与引用
  8. 算法训练营07-递归使用练习
  9. 10行代码AC——7-2 说反话-加强版 (20 分)——解题报告
  10. 程序员圣诞节相册源码_程序员分享圣诞刷屏源码,这次朋友圈千万不要再@微信官方了!...