传统BIO 实现:
服务端代码:
public class ChatServer {
private int port=8888;
private final String quit="quit";
private ServerSocket serverSocket;
private Map<Integer,Writer> connectedClients; //客户端的端口,和写入流。
public ChatServer() {
connectedClients=new HashMap<>();
}
//添加客户端。
public synchronized void addClient(Socket socket) throws IOException{
if(socket!=null) {
int port=socket.getPort();
BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
connectedClients.put(port, writer);
System.out.println("客户端["+port+"] 放入Map成功");
}
}
//移除客户端
public synchronized void removeClient(Socket socket) throws IOException{
if(socket!=null) {
int port=socket.getPort();
if(connectedClients.containsKey(port)) {
//先把流关闭了。
connectedClients.get(port).close();
}
//再从hashmap中移除
connectedClients.remove(port);
System.out.println("移除客户端 ["+port+"] ---从hashMap this is removeClient");
}
}
//把客户端发送过来的消息,进行群发,除了发送者。
public synchronized void forwardMessage(Socket socket,String fwdMsg) throws IOException {
for(Integer id:connectedClients.keySet()) {
if(!id.equals(socket.getPort())) {
System.out.println("向["+id+"] 转发...");
Writer writer=connectedClients.get(id);
writer.write(fwdMsg);
writer.flush();
}
}
}
public boolean ready2Quit(String msg) {
return quit.equals(msg);
}
public synchronized void close() {
if(serverSocket !=null) {
try {
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//绑定端口,进行监听。
public void start() {
try {
serverSocket=new ServerSocket(port);
System.out.println("启动服务器,监听端口为:["+port+"] ");
while(true) {
Socket socket=serverSocket.accept();
System.out.println("连接建立成功:!"+socket.getPort());
//创建CharHandler线程。
new Thread(new ChatHandler(this,socket)).start();;
}
} catch (IOException e) {
e.printStackTrace();
}finally{
close();
}
}
public static void main(String []args) {
ChatServer chatServer=new ChatServer();
chatServer.start();
}
}
public class ChatHandler implements Runnable{
private ChatServer server;
private Socket socket;
public ChatHandler(ChatServer server, Socket socket) {
this.server = server;
this.socket = socket;
}
public void run() {
try {
server.addClient(socket);
//读取用户发送过来的信息;
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg=null;
while((msg=reader.readLine())!=null) {
String fmsg="客户端["+socket.getPort()+"]:"+msg;
System.out.println(fmsg);
//将信息转发给其他的在线的用户;
server.forwardMessage(socket, fmsg);
//检查用户是否准备退出
if(server.ready2Quit(msg))
break;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
server.removeClient(socket);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
客户端:
public class ChatClient {
private final String host="127.0.0.1";
private final int port=8888;
private final String quit="quit";
private Socket socket;
private BufferedReader reader;
private BufferedWriter writer;
//发送消息给服务器
public void send(String msg) throws IOException {
if(!socket.isOutputShutdown()) {
writer.write(msg+"\n");
writer.flush();
}
}
//从服务器接受消息
public String receive() throws IOException {
String msg=null;
// if(!socket.isInputShutdown()) {
msg=reader.readLine();
// }
//System.out.println("msg的值:"+msg);
return msg;
}
//检查用户是否准备退出
public boolean reade2Quit(String msg) {
return quit.equals(msg);
}
public void close() {
if(writer!=null) {
try {
writer.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void start() throws InterruptedException {
try {
socket=new Socket(host,port);
//创建IO流
reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//处理用户的输入
new Thread(new UserInputHandler(this)).start();
//读取服务器转发的消息
String msg=null;
while(true) {
System.out.println("监听 服务端是否返回信息...");
Thread.currentThread().sleep(1000);
msg=receive();
if(msg!=null)
System.out.println("成功返回:"+msg);
if(reade2Quit(msg))
break;
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
close();
}
}
public static void main(String []args) throws InterruptedException {
ChatClient chatClient=new ChatClient();
chatClient.start();
}
}
public class UserInputHandler implements Runnable {
private ChatClient chatClient;
public UserInputHandler(ChatClient chatClient) {
super();
this.chatClient = chatClient;
}
public void run() {
try {
BufferedReader consoleReader=new BufferedReader(new InputStreamReader(System.in));
while(true) {
String input=consoleReader.readLine();
chatClient.send(input);
if(chatClient.reade2Quit(input))
break;
Thread.currentThread().sleep(1000);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
NIO 实现:
客户端实现
public class ChatClient {
private static final String DEFAULT_SERVER_HOST = "127.0.0.1";
private static final int DEFAULT_SERVER_PORT = 8888;
private static final String QUIT = "quit";
private static final int BUFFER = 1024;
private String host;
private int port;
private SocketChannel client;
private ByteBuffer rBuffer = ByteBuffer.allocate(BUFFER);
private ByteBuffer wBuffer = ByteBuffer.allocate(BUFFER);
private Selector selector;
private Charset charset = Charset.forName("UTF-8");
public ChatClient() {
this(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
}
public ChatClient(String host, int port) {
this.host = host;
this.port = port;
}
public boolean readyToQuit(String msg) {
return QUIT.equals(msg);
}
private void close(Closeable closable) {
if (closable != null) {
try {
closable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void start() {
try {
client = SocketChannel.open();
client.configureBlocking(false);
selector = Selector.open();
client.register(selector, SelectionKey.OP_CONNECT);
client.connect(new InetSocketAddress(host, port));
while (true) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for (SelectionKey key : selectionKeys) {
handles(key);
}
selectionKeys.clear();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClosedSelectorException e) {
} finally {
close(selector);
}
}
private void handles(SelectionKey key) throws IOException {
if (key.isConnectable()) {
SocketChannel client = (SocketChannel) key.channel();
if (client.isConnectionPending()) {
client.finishConnect();
new Thread(new UserInputHandler(this)).start();
}
client.register(selector, SelectionKey.OP_READ);
}
else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
String msg = receive(client);
if (msg.isEmpty()) {
close(selector);
} else {
System.out.println(msg);
}
}
}
public void send(String msg) throws IOException {
if (msg.isEmpty()) {
return;
}
wBuffer.clear();
wBuffer.put(charset.encode(msg));
wBuffer.flip();
while (wBuffer.hasRemaining()) {
client.write(wBuffer);
}
if (readyToQuit(msg)) {
close(selector);
}
}
private String receive(SocketChannel client) throws IOException {
rBuffer.clear();
while (client.read(rBuffer) > 0);
rBuffer.flip();
return String.valueOf(charset.decode(rBuffer));
}
public static void main(String[] args) {
ChatClient client = new ChatClient("127.0.0.1", 8888);
client.start();
}
}
用户的输入:
public class UserInputHandler implements Runnable {
private ChatClient chatClient;
public UserInputHandler(ChatClient chatClient) {
this.chatClient = chatClient;
}
@Override
public void run() {
try {
// 绛夊緟鐢ㄦ埛杈撳叆娑堟伅
BufferedReader consoleReader =
new BufferedReader(new InputStreamReader(System.in));
while (true) {
String input = consoleReader.readLine();
// 鍚戞湇鍔″櫒鍙戦�佹秷鎭�
chatClient.send(input);
// 妫�鏌ョ敤鎴锋槸鍚﹀噯澶囬��鍑�
if (chatClient.readyToQuit(input)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端实现:
public class ChatServer {
private int port=8888;
private final String quit="quit";
private ServerSocketChannel server;
private Selector selector;
private ByteBuffer rBuffer=ByteBuffer.allocate(1024);
private ByteBuffer wBuffer=ByteBuffer.allocate(1024);
private Charset charset = Charset.forName("UTF-8");
//把客户端发送过来的消息,进行群发,除了发送者。
public synchronized void forwardMessage(SocketChannel client,String fwdMsg) throws IOException {
for(SelectionKey key:selector.keys()) { //这里才是遍历所有的key
Channel connectedClient =key.channel(); //把key转为channel
if(key.channel() instanceof ServerSocketChannel) { //他不是服务端的监听channel
continue;
}
if(key.isValid()&&!client.equals(key.channel())) { //key是有效的,并且key不是自己发送方的key.
wBuffer.clear(); //把写入流,先清空,避免写入无效数据。
wBuffer.put(charset.encode(getClientName(client) + ":" + fwdMsg)); //写入。
wBuffer.flip(); //翻转,读出。
while(wBuffer.hasRemaining()) { //全部输出完。
((SocketChannel)connectedClient).write(wBuffer);
}
}
}
}
private String getClientName(SocketChannel client) {
return "客户端["+client.socket().getPort()+"]";
}
public boolean ready2Quit(String msg) {
return quit.equals(msg);
}
public synchronized void close(Closeable closeable) {
if(closeable !=null) {
try {
closeable.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//绑定端口,进行监听。
public void start() {
try {
server=ServerSocketChannel.open();
server.configureBlocking(false); //非阻塞调用
server.socket().bind(new InetSocketAddress(port));
selector=Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("启动服务器,监听端口:"+port);
while(true) {
selector.select(); //这个是返回可以操作的数量,它是阻塞的。
Set<SelectionKey> selectionKeys=selector.selectedKeys(); //这个是返回所有的触发的注册的channel;
for(SelectionKey key:selectionKeys) {
handler(key); //处理所有的channel。
}
selectionKeys.clear(); //手动清理掉,这个应该是引用,清理它,就等于清理selector.不过不清空, 在下一次调用select()调用的时候,又会计算进去。
}
}catch(Exception e) {
e.printStackTrace();
}finally {
close(selector);
}
}
private void handler(SelectionKey key) throws IOException {
if(key.isAcceptable()) {
ServerSocketChannel server=(ServerSocketChannel)key.channel(); //通过Key获得channel。
//这个和Bio中的有什么区别呢?
SocketChannel client=server.accept(); //这个是客户端的channel啊。把客户端发送过来的channel注册到channel中,然后进去读操作!
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
System.out.println("客户端["+client.socket().getPort()+"] 连接成功,开始进行信息监听");
}else if(key.isReadable()) {
SocketChannel client=(SocketChannel)key.channel();
String fwdMsg=receive(client);
if(fwdMsg.isEmpty()) {
//客户端异常
key.cancel();
selector.wakeup();
}else {
System.out.println("收到的信息:"+fwdMsg);
forwardMessage(client,fwdMsg);
if(ready2Quit(fwdMsg)) {
key.channel();
selector.wakeup();
System.out.println("客户端["+client.socket().getPort()+"] 退出");
}
}
}
}
private String receive(SocketChannel client) throws IOException {
rBuffer.clear();
while(client.read(rBuffer)>0);
rBuffer.flip();
return String.valueOf(charset.decode(rBuffer));
}
public static void main(String []args) {
ChatServer chatServer=new ChatServer();
chatServer.start();
}
}
--------------------------------
补充一个AIO 版本的:
public class Client {
final String LOCALHOST = "localhost";
final int DEFAULT_PORT = 8888;
AsynchronousSocketChannel clientChannel;
private void close(Closeable closable) {
if (closable != null) {
try {
closable.close();
System.out.println("鍏抽棴" + closable);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void start() {
try {
// 鍒涘缓channel
clientChannel = AsynchronousSocketChannel.open();
Future<Void> future = clientChannel.connect(new InetSocketAddress(LOCALHOST, DEFAULT_PORT));
future.get();
// 绛夊緟鐢ㄦ埛鐨勮緭鍏�
BufferedReader consoleReader =
new BufferedReader(new InputStreamReader(System.in));
while (true) {
String input = consoleReader.readLine();
byte[] inputBytes = input.getBytes();
ByteBuffer buffer = ByteBuffer.wrap(inputBytes);
Future<Integer> writeResult = clientChannel.write(buffer);
writeResult.get();
buffer.flip();
Future<Integer> readResult = clientChannel.read(buffer);
readResult.get();
String echo = new String(buffer.array());
buffer.clear();
System.out.println(echo);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
close(clientChannel);
}
}
public static void main(String[] args) {
Client client = new Client();
client.start();
}
}
public class Server {
final String LOCALHOST = "localhost";
final int DEFAULT_PORT = 8888;
AsynchronousServerSocketChannel serverChannel;
private void close(Closeable closable) {
if (closable != null) {
try {
closable.close();
System.out.println("鍏抽棴" + closable);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void start() {
try {
//绑定监听端口。这里直接使用的是serverChannel而不是serverSocketChannel;
serverChannel = AsynchronousServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(LOCALHOST, DEFAULT_PORT));
System.out.println("启动服务器,监听端口:" + DEFAULT_PORT);
while (true) {
//这是一个异步的,避免过早返回,使用while循环。
serverChannel.accept(null, new AcceptHandler()); //第一个操作是一个辅助操作。不是必须的。
System.in.read(); //小花招,阻塞,避免一直循环浪费系统资源。
}
} catch (IOException e) {
e.printStackTrace();
} finally {
close(serverChannel);
}
}
//自定义类, 实现了异步操作。实现有回调函数的类,
private class AcceptHandler implements
CompletionHandler<AsynchronousSocketChannel, Object>
{
//结果获取成功的时候,调用的函数。 第二个参数是上面第一个附件,辅助类,因为是Null,所以直接给一个Object。
//第一个参数,客户端的channel。
@Override
public void completed(AsynchronousSocketChannel result, Object attachment) {
//让服务器端,收到结果后,继续监听,继续接受结果。
if (serverChannel.isOpen()) {
serverChannel.accept(null, this);
}
//这个就是一个简单的引用。
AsynchronousSocketChannel clientChannel = result;
if (clientChannel != null && clientChannel.isOpen()) {
//客户端的处理器创建。 同样也是一个异步的独写操作。
ClientHandler handler = new ClientHandler(clientChannel);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Map<String, Object> info = new HashMap<>();
info.put("type", "read");
info.put("buffer", buffer);
//放入操作。
clientChannel.read(buffer, info, handler);
}
}
//结果获取失败的时候,调用的函数
@Override
public void failed(Throwable exc, Object attachment) {
// 澶勭悊閿欒
}
}
private class ClientHandler implements
CompletionHandler<Integer, Object>
{
private AsynchronousSocketChannel clientChannel;
public ClientHandler(AsynchronousSocketChannel channel) {
this.clientChannel = channel;
}
@Override
public void completed(Integer result, Object attachment) {
Map<String, Object> info = (Map<String, Object>) attachment;
String type = (String) info.get("type");
if ("read".equals(type)) {
ByteBuffer buffer = (ByteBuffer) info.get("buffer");
buffer.flip();
info.put("type", "write");
clientChannel.write(buffer, info, this);
buffer.clear();
} else if ("write".equals(type)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
info.put("type", "read");
info.put("buffer", buffer);
clientChannel.read(buffer, info, this);
}
}
@Override
public void failed(Throwable exc, Object attachment) {
// 澶勭悊閿欒
}
}
public static void main(String[] args) {
Server server = new Server();
server.start();
}
}