java nio.2群发_JAVA NIO TCP SOCKET 聊天群发
以前都是用一般的socket編程,用線程來控制。最近突然用nio來做些東西。
nio的好處我來說一下:第一,讀寫都是基于塊的,效率高。第二,通過引入selector,簡化了網絡編程模型,異步非阻塞。
既然有這么多好處,那就寫個NIO TCP網絡聊天室來練練手吧。
因為沒有寫gui,是基于控制臺的所以沒寫私了的部分,只寫了公共聊天室。(其實,既然是服務器端可以分發(fā)給所有人,分發(fā)給特定人也是很容易實現的。
注意:這里只是為了練手,聯系服務器端分發(fā)消息到各個客戶端。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%的狀態(tài)。所以當連接剛被接受的時候,一定要注冊讀sc.register(selector, SelectionKey.OP_READ);
2. 服務器端寫有兩種方法:一種就是直接寫。第二種是加入ATTACH,然后,通過skey.interestOps(skey.interestOps()|SelectionKey.OP_WRITE);把寫消息寫到interestOps集合中。就出發(fā)了可寫的通知了。注意第二種方式,在接到可寫的通知后,處理完了消息后,還是得恢復只對寫有興趣的interestOps 如key.interestOps(SelectionKey.OP_READ);
3. 得到所有服務器端的連接的方式是key.selector().keys(),但是一定記得,里面有一個是SocketServerChannel注冊的key,這個只可以接受連接,其他的什么都做不了。更不能寫數據給客戶端。所以一定記得剔除這個。不然程序會拋異常。
4. 客戶端比較簡單,所以可以用select的方式,也可以不用。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的java nio.2群发_JAVA NIO TCP SOCKET 聊天群发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java uncked_使用FindBu
- 下一篇: java stream 多次读取_多次从