一 概述

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

二 IO聊天室

1 服务器

public class IOServer {

    public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(8899)); ExecutorService executor = Executors.newCachedThreadPool();
Set<Socket> socketGroup = new HashSet<>();
while (true) {
Socket socket = serverSocket.accept();
socketGroup.add(socket); executor.execute(() -> {
try (
InputStream in = socket.getInputStream();
InputStreamReader reader = new InputStreamReader(in, "UTF-8");
BufferedReader br = new BufferedReader(reader);
) {
String line;
while ((line = br.readLine()) != null) {
int port = socket.getPort();
System.out.println("from client:{" + port + "}" + line);
String finalLine = line;
for (Socket client : socketGroup) {
if (client == socket) continue;
try {
OutputStream output = client.getOutputStream();
DataOutputStream out = new DataOutputStream(output);
String s = "client{" + port + "}" + finalLine + "\n";
out.write(s.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
}

2 客户端

public class IOClient {

    public static void main(String[] args) throws IOException {
Socket socket = new Socket();
InetSocketAddress address = new InetSocketAddress("localhost", 8899);
socket.connect(address);
try (
OutputStream output = socket.getOutputStream();
DataOutputStream out = new DataOutputStream(output);
Reader rd = new InputStreamReader(socket.getInputStream());
BufferedReader bufferRd = new BufferedReader(rd);
) {
// 子线程监听输入并发送
new Thread(() -> {
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader reader = new BufferedReader(in);
while (true) {
try {
out.write((reader.readLine() + '\n').getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
// 主线程循环监听接受到的数据并输出
while (true) {
System.out.println(bufferRd.readLine());
}
}
}
}

三 NIO聊天室

1 服务器

public class NIOServer {
public static void main(String[] args) throws IOException {
ServerSocketChannel srvSocketChannel = ServerSocketChannel.open();
srvSocketChannel.configureBlocking(false);
ServerSocket socket = srvSocketChannel.socket();
socket.setReuseAddress(true);
socket.bind(new InetSocketAddress(8899)); Selector selector = Selector.open();
srvSocketChannel.register(selector, SelectionKey.OP_ACCEPT); Set<SocketChannel> channelGroup = new HashSet<>(); while (selector.select() > 0) {
Set<SelectionKey> keys = selector.selectedKeys(); for (SelectionKey key : keys) {
SocketChannel client;
if (key.isAcceptable()) {
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
client = channel.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
channelGroup.add(client);
System.out.println(client.getRemoteAddress());
} else if (key.isReadable()) {
client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer);
buffer.flip();
System.out.print(new String(buffer.array()));
channelGroup.forEach(channel -> {
buffer.rewind();
if (channel != client) {
try {
int port = client.socket().getPort();
byte[] array = buffer.array();
String s = "client{" + port + "}:" + new String(array);
channel.write(ByteBuffer.wrap(s.getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
keys.remove(key);
}
}
}
}

2 客户端

public class NIOClient {

    public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
InetSocketAddress address = new InetSocketAddress("localhost", 8899);
socketChannel.connect(address); Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT); while (selector.select() > 0) {
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
SocketChannel client;
if (key.isConnectable()) {
client = (SocketChannel) key.channel();
if (client.isConnectionPending()) {
client.finishConnect();
client.register(selector, SelectionKey.OP_READ);
new Thread(() -> {
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader reader = new BufferedReader(in);
while (true) {
try {
String line = reader.readLine() + '\n';
client.write(ByteBuffer.wrap(line.getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
} else if (key.isReadable()) {
client = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
client.read(byteBuffer);
byteBuffer.flip();
while (byteBuffer.hasRemaining()) {
System.out.print((char)byteBuffer.get());
}
}
keys.remove(key);
}
}
}
}

四 Netty聊天室

1 服务器

  • TCPServer.java
public class TCPServer {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss,worker).channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ServerChannelInitializer());
ChannelFuture channelFuture = bootstrap.bind(8899).sync();
channelFuture.channel().closeFuture().sync();
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
  • ServerChannelInitializer.java
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {

    @Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(4096,Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder(UTF_8));
pipeline.addLast(new StringEncoder(UTF_8));
pipeline.addLast(new ServerHandler());
} }
  • ServerHandler.java
public class ServerHandler extends SimpleChannelInboundHandler<String> {

    private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
group.forEach(ch -> {
ch.writeAndFlush(channel.remoteAddress() + " 上线" + "\n");
});
group.add(channel);
} @Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
group.forEach(ch -> {
ch.writeAndFlush(ctx.channel().remoteAddress() + " 下线" + "\n");
});
} @Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
Channel channel = ctx.channel();
group.forEach(ch -> {
if (ch != channel) {
ch.writeAndFlush(channel.remoteAddress() + ":" + msg + "\n");
} else {
ch.writeAndFlush("自己:" + msg + "\n");
}
});
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
} }

2 客户端

  • TCPClient.java
public class TCPClient {
public static void main(String[] args) throws InterruptedException, IOException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ClientChannelInitializer());
Channel channel = bootstrap
.connect("localhost", 8899)
.sync()
.channel();
InputStreamReader in = new InputStreamReader(System.in);
BufferedReader reader = new BufferedReader(in);
while (true) {
channel.writeAndFlush(reader.readLine() + "\n");
}
} finally {
group.shutdownGracefully();
}
}
}
  • ClientChannelInitializer.java
public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {

    @Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new DelimiterBasedFrameDecoder(4096, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder(UTF_8));
pipeline.addLast(new StringEncoder(UTF_8));
pipeline.addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println(msg);
}
});
}
}

五 总结

  • 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. php Class 'ZipArchive' not found怎么解决?

      情况1: 服务器php zip模块没有安装 情况2: Php.ini 中Php zlip扩展没有开   文章来源:外星人来地球 欢迎关注,有问题一起学习欢迎留言.评论

  2. vue使用install函数把组件做成插件方便全局调用

    在vue项目中,我们可以自定义组件,像element-ui一样使用Vue.use()方法来使用,具体实现方法: 1.首先新建一个Cmponent.vue文件 // Cmponent.vue <t ...

  3. 不是springboot项目怎么使用内置tomcat

    不是springboot项目怎么使用内置tomcat   解决方法: 1.pom.xml中添加以下依赖 <properties>  <tomcat.version>8.5.23 ...

  4. 沙箱测试环境配置AND证书添加

    支付宝SDK接口项目Demo测试 一.进入支付宝开放中心 下载密钥生成器 https://developers.alipay.com/developmentAccess/developmentAcce ...

  5. 定时杀死warn进程

    6 6 * * * /root/wz/mysqlRestart.sh    #MySQL restart7 6 * * * /bin/sh /home/warn/kill_.sh8 6 * * * / ...

  6. Node.js中使用pipe拷贝大文件不能完全拷贝的解决办法

    原来的代码如下: var readable = fs.createReadStream( filepath ); var writable = fs.createWriteStream( outFil ...

  7. jsplumb实现流程图

    流程图使用工具汇总 jsPlumb,开源软件,推荐使用,参考学习链接: jsplumb学习笔记.基本概念.中文简易教程 jTopo myflow Go.js JointJS,属于商业软件 mxGrap ...

  8. Python3之实现字符反转

    参考:https://www.cnblogs.com/jasmine0627/p/9510296.html 将字符串s="helloworld"反转为‘dlrowolleh’ fa ...

  9. Python3安装后无法使用退格键

    解决办法 # 安装readline模块 yum -y install readline-devel # 进入Python安装目录 cd /usr/python/Python-3.7.2 # 重新安装 ...

  10. Docker - 在CentOS7中安装Docker

    在CentOS 7中安装Docker 1-确认系统信息 # cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) # uname - ...