一 概述

  • 使用Java的IO实现聊天室
  • 使用Java的NIO实现聊天室
  • 使用Netty实现聊天室

二 IO聊天室

1 服务器

  1. public class IOServer {
  2. public static void main(String[] args) throws IOException {
  3. ServerSocket serverSocket = new ServerSocket();
  4. serverSocket.setReuseAddress(true);
  5. serverSocket.bind(new InetSocketAddress(8899));
  6. ExecutorService executor = Executors.newCachedThreadPool();
  7. Set<Socket> socketGroup = new HashSet<>();
  8. while (true) {
  9. Socket socket = serverSocket.accept();
  10. socketGroup.add(socket);
  11. executor.execute(() -> {
  12. try (
  13. InputStream in = socket.getInputStream();
  14. InputStreamReader reader = new InputStreamReader(in, "UTF-8");
  15. BufferedReader br = new BufferedReader(reader);
  16. ) {
  17. String line;
  18. while ((line = br.readLine()) != null) {
  19. int port = socket.getPort();
  20. System.out.println("from client:{" + port + "}" + line);
  21. String finalLine = line;
  22. for (Socket client : socketGroup) {
  23. if (client == socket) continue;
  24. try {
  25. OutputStream output = client.getOutputStream();
  26. DataOutputStream out = new DataOutputStream(output);
  27. String s = "client{" + port + "}" + finalLine + "\n";
  28. out.write(s.getBytes());
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. });
  38. }
  39. }
  40. }

2 客户端

  1. public class IOClient {
  2. public static void main(String[] args) throws IOException {
  3. Socket socket = new Socket();
  4. InetSocketAddress address = new InetSocketAddress("localhost", 8899);
  5. socket.connect(address);
  6. try (
  7. OutputStream output = socket.getOutputStream();
  8. DataOutputStream out = new DataOutputStream(output);
  9. Reader rd = new InputStreamReader(socket.getInputStream());
  10. BufferedReader bufferRd = new BufferedReader(rd);
  11. ) {
  12. // 子线程监听输入并发送
  13. new Thread(() -> {
  14. InputStreamReader in = new InputStreamReader(System.in);
  15. BufferedReader reader = new BufferedReader(in);
  16. while (true) {
  17. try {
  18. out.write((reader.readLine() + '\n').getBytes());
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }).start();
  24. // 主线程循环监听接受到的数据并输出
  25. while (true) {
  26. System.out.println(bufferRd.readLine());
  27. }
  28. }
  29. }
  30. }

三 NIO聊天室

1 服务器

  1. public class NIOServer {
  2. public static void main(String[] args) throws IOException {
  3. ServerSocketChannel srvSocketChannel = ServerSocketChannel.open();
  4. srvSocketChannel.configureBlocking(false);
  5. ServerSocket socket = srvSocketChannel.socket();
  6. socket.setReuseAddress(true);
  7. socket.bind(new InetSocketAddress(8899));
  8. Selector selector = Selector.open();
  9. srvSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
  10. Set<SocketChannel> channelGroup = new HashSet<>();
  11. while (selector.select() > 0) {
  12. Set<SelectionKey> keys = selector.selectedKeys();
  13. for (SelectionKey key : keys) {
  14. SocketChannel client;
  15. if (key.isAcceptable()) {
  16. ServerSocketChannel channel = (ServerSocketChannel) key.channel();
  17. client = channel.accept();
  18. client.configureBlocking(false);
  19. client.register(selector, SelectionKey.OP_READ);
  20. channelGroup.add(client);
  21. System.out.println(client.getRemoteAddress());
  22. } else if (key.isReadable()) {
  23. client = (SocketChannel) key.channel();
  24. ByteBuffer buffer = ByteBuffer.allocate(1024);
  25. client.read(buffer);
  26. buffer.flip();
  27. System.out.print(new String(buffer.array()));
  28. channelGroup.forEach(channel -> {
  29. buffer.rewind();
  30. if (channel != client) {
  31. try {
  32. int port = client.socket().getPort();
  33. byte[] array = buffer.array();
  34. String s = "client{" + port + "}:" + new String(array);
  35. channel.write(ByteBuffer.wrap(s.getBytes()));
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. });
  41. }
  42. keys.remove(key);
  43. }
  44. }
  45. }
  46. }

2 客户端

  1. public class NIOClient {
  2. public static void main(String[] args) throws IOException {
  3. SocketChannel socketChannel = SocketChannel.open();
  4. socketChannel.configureBlocking(false);
  5. InetSocketAddress address = new InetSocketAddress("localhost", 8899);
  6. socketChannel.connect(address);
  7. Selector selector = Selector.open();
  8. socketChannel.register(selector, SelectionKey.OP_CONNECT);
  9. while (selector.select() > 0) {
  10. Set<SelectionKey> keys = selector.selectedKeys();
  11. for (SelectionKey key : keys) {
  12. SocketChannel client;
  13. if (key.isConnectable()) {
  14. client = (SocketChannel) key.channel();
  15. if (client.isConnectionPending()) {
  16. client.finishConnect();
  17. client.register(selector, SelectionKey.OP_READ);
  18. new Thread(() -> {
  19. InputStreamReader in = new InputStreamReader(System.in);
  20. BufferedReader reader = new BufferedReader(in);
  21. while (true) {
  22. try {
  23. String line = reader.readLine() + '\n';
  24. client.write(ByteBuffer.wrap(line.getBytes()));
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }).start();
  30. }
  31. } else if (key.isReadable()) {
  32. client = (SocketChannel) key.channel();
  33. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  34. client.read(byteBuffer);
  35. byteBuffer.flip();
  36. while (byteBuffer.hasRemaining()) {
  37. System.out.print((char)byteBuffer.get());
  38. }
  39. }
  40. keys.remove(key);
  41. }
  42. }
  43. }
  44. }

四 Netty聊天室

1 服务器

  • TCPServer.java
  1. public class TCPServer {
  2. public static void main(String[] args) throws InterruptedException {
  3. EventLoopGroup boss = new NioEventLoopGroup();
  4. EventLoopGroup worker = new NioEventLoopGroup();
  5. try {
  6. ServerBootstrap bootstrap = new ServerBootstrap();
  7. bootstrap.group(boss,worker).channel(NioServerSocketChannel.class)
  8. .handler(new LoggingHandler(LogLevel.INFO))
  9. .childHandler(new ServerChannelInitializer());
  10. ChannelFuture channelFuture = bootstrap.bind(8899).sync();
  11. channelFuture.channel().closeFuture().sync();
  12. } finally {
  13. boss.shutdownGracefully();
  14. worker.shutdownGracefully();
  15. }
  16. }
  17. }
  • ServerChannelInitializer.java
  1. public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
  2. @Override
  3. protected void initChannel(SocketChannel ch) throws Exception {
  4. ChannelPipeline pipeline = ch.pipeline();
  5. pipeline.addLast(new DelimiterBasedFrameDecoder(4096,Delimiters.lineDelimiter()));
  6. pipeline.addLast(new StringDecoder(UTF_8));
  7. pipeline.addLast(new StringEncoder(UTF_8));
  8. pipeline.addLast(new ServerHandler());
  9. }
  10. }
  • ServerHandler.java
  1. public class ServerHandler extends SimpleChannelInboundHandler<String> {
  2. private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
  3. @Override
  4. public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
  5. Channel channel = ctx.channel();
  6. group.forEach(ch -> {
  7. ch.writeAndFlush(channel.remoteAddress() + " 上线" + "\n");
  8. });
  9. group.add(channel);
  10. }
  11. @Override
  12. public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
  13. group.forEach(ch -> {
  14. ch.writeAndFlush(ctx.channel().remoteAddress() + " 下线" + "\n");
  15. });
  16. }
  17. @Override
  18. protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
  19. Channel channel = ctx.channel();
  20. group.forEach(ch -> {
  21. if (ch != channel) {
  22. ch.writeAndFlush(channel.remoteAddress() + ":" + msg + "\n");
  23. } else {
  24. ch.writeAndFlush("自己:" + msg + "\n");
  25. }
  26. });
  27. }
  28. @Override
  29. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  30. ctx.close();
  31. }
  32. }

2 客户端

  • TCPClient.java
  1. public class TCPClient {
  2. public static void main(String[] args) throws InterruptedException, IOException {
  3. EventLoopGroup group = new NioEventLoopGroup();
  4. try {
  5. Bootstrap bootstrap = new Bootstrap();
  6. bootstrap.group(group)
  7. .channel(NioSocketChannel.class)
  8. .handler(new ClientChannelInitializer());
  9. Channel channel = bootstrap
  10. .connect("localhost", 8899)
  11. .sync()
  12. .channel();
  13. InputStreamReader in = new InputStreamReader(System.in);
  14. BufferedReader reader = new BufferedReader(in);
  15. while (true) {
  16. channel.writeAndFlush(reader.readLine() + "\n");
  17. }
  18. } finally {
  19. group.shutdownGracefully();
  20. }
  21. }
  22. }
  • ClientChannelInitializer.java
  1. public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
  2. @Override
  3. protected void initChannel(SocketChannel ch) throws Exception {
  4. ChannelPipeline pipeline = ch.pipeline();
  5. pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
  6. pipeline.addLast(new StringDecoder(UTF_8));
  7. pipeline.addLast(new StringEncoder(UTF_8));
  8. pipeline.addLast(new SimpleChannelInboundHandler<String>() {
  9. @Override
  10. protected void channelRead0(ChannelHandlerContext ctx, String msg) {
  11. System.out.println(msg);
  12. }
  13. });
  14. }
  15. }

五 总结

  • Netty实现简单,逻辑清晰,但是隐藏了很多复杂的细节,后续的学习再慢慢剖析吧。
  • IO的线程模型,实现比较通俗易懂。
  • NIO的实现相对比较难懂,需要大家对Selector、Channel和Buffer有比较深刻的理解,不然很容易出错。

注:NIO是Netty的基础,学好NIO对于Netty的学习有重要作用。

三种TCP协议聊天室实现的更多相关文章

  1. 网络编程TCP协议-聊天室

    网络编程TCP协议-聊天室(客户端与服务端的交互); <span style="font-size:18px;">1.客户端发数据到服务端.</span> ...

  2. Rsyslog的三种传输协议简要介绍

    rsyslog的三种传输协议 rsyslog 可以理解为多线程增强版的syslog. rsyslog提供了三种远程传输协议,分别是: 1. UDP 传输协议 基于传统UDP协议进行远程日志传输,也是传 ...

  3. 基于Linux的TCP网络聊天室

    1.实验项目名称:基于Linux的TCP网络聊天室 2.实验目的:通过TCP完成多用户群聊和私聊功能. 3.实验过程: 通过socket建立用户连接并传送用户输入的信息,分别来写客户端和服务器端,利用 ...

  4. TCP多线程聊天室

    TCP协议,一个服务器(ServerSocket)只服务于一个客户端(Socket),那么可以通过ServerSocket+Thread的方式,实现一个服务器服务于多个客户端. 多线程服务器实现原理— ...

  5. windows网络编程-C语言实现简单的TCP协议聊天

    TCP/IP协议(面向连接协议)类似于打电话时,对方一定在手机附近并且此刻都在和对方进行通话.一定保证双方都在线,才能进行数据传输.UDP/IP协议(无连接协议)就像邮箱,不保证对方一定在等你邮件且对 ...

  6. Linux三种SSH协议登陆方式

    在实际工作中遇到了三种不同SSH协议登陆Linux服务器的方式,由简单到复杂分别阐述:一.最简单也是最不安全的root账号密码登陆通过root账号+root密码登陆Linux服务器. 二.普通用户登陆 ...

  7. Java 网络编程 -- 基于TCP 实现聊天室 群聊 私聊

    分析: 聊天室需要多个客户端和一个服务端. 服务端负责转发消息. 客户端可以发送消息.接收消息. 消息分类: 群聊消息:发送除自己外所有人 私聊消息:只发送@的人 系统消息:根据情况分只发送个人和其他 ...

  8. Java网络编程三--基于TCP协议的网络编程

    ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状体 Socket accept():如果接收到客户端的连接请求,该方法返回一个与客户端对应Socket ...

  9. 三、TCP协议

    TCP(Transmission Control Protocol)传输控制协议:顾名思义就是对数据的传输进行控制 TCP报头 序号:相当于编号,当TCP数据包过大的时候会进行分段,分段之后按序号顺序 ...

随机推荐

  1. 留个纪念,过了这么多年,又干回Android了!

    这个博客中的好多Android知识已经老得不像样子了,没想到,还有干回来的一天.怎么说呢,只要坚持一下,总会有机会能做自己爱做的事情的. 加油! ---- 于武汉出差的第5天

  2. VS中卸载Visual Assist X

    Tools=>Extensions and updates=>找到Visual Assist X 卸载:

  3. Qt编写控件属性设计器9-数据库采集

    一.前言 数据库作为数据源,在很多组态软件中使用非常多,指定数据库类型,填写好数据库连接信息,指定对应的数据库表和字段,采集间隔,程序按照采集间隔自动采集数据库数据,绑定到界面上的控件赋值显示即可.使 ...

  4. Ubuntu 16.04 haproxy + keeplive

    WEB架构

  5. SpringMvc+ajax跨域请求时,出现options类型的请求并返回403的解决方案

    在使用 $.ajax({ url:'http://127.0.0.1:8081/rest/ccxxx/xxxx', type:'POST', dataType:"json", co ...

  6. spark报错:invalid token

    启动spark报错,启动container失败,去看yarn的日志,显示invalid token, 经过排查是hadoop子节点的配置和主节点的配置不一致导致的,同步之后,问题解决.

  7. JEECG新建用户不用系统用户表的实现

    首先新增组织机构 和 角色: -- 新增 组织机构 INSERT INTO `t_s_depart` VALUES ('dept001', '你所在的机构', '你所在的机构的描述', null, ' ...

  8. jQuery BlockUI Plugin Demo

    1.Login Form $(document).ready(function() { $('#demo1').click(function() { $.blockUI({ message: $('# ...

  9. localStorage 存储 数组

    let str = JSON.stringify(data.list); localStorage.setItem("options",str); let optionss=loc ...

  10. 添加tomcat8为服务

    跟上一篇添加zookeeper为服务基本类似 脚本如下: #!/bin/bash CATALANA_HOME=/usr/local/tomcat8 export JAVA_HOME=/usr/loca ...