一 概述

  • 使用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. python: 关于解决'\u'开头的字符串转中文的方法

    爬虫爬到的内容是这样的: 如果直接打印出来是这样的: python3的解决办法:字符串.encode('utf-8').decode('unicode_escape') python2:字符串.dec ...

  2. java判断请求是否ajax异步请求

    java判断请求是否ajax异步请求   解决方法: if (request.getHeader("x-requested-with") != null && re ...

  3. 学习TypeScript 笔记

    TypeScript 什么是TypeScript TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准. TypeScript 由微软开发的自由和开源的编程 ...

  4. PAT 甲级 1048 Find Coins (25 分)(较简单,开个数组记录一下即可)

    1048 Find Coins (25 分)   Eva loves to collect coins from all over the universe, including some other ...

  5. Core Data 的线程安全问题

    前言: 很多小的App只需要一个ManagedContext在主线程就可以了,但是有时候对于CoreData的操作要耗时很久的,比如App开启的时候要载入大量数据,如果都放在主线程,毫无疑问会阻塞UI ...

  6. laravel中redis pipeline用法说明

    $res = Redis::pipeline(function($pipe) use($params) { for ($i = 0; $i < 1000; $i++) { $pipe->g ...

  7. 【Leetcode_easy】599. Minimum Index Sum of Two Lists

    problem 599. Minimum Index Sum of Two Lists 题意:给出两个字符串数组,找到坐标位置之和最小的相同的字符串. 计算两个的坐标之和,如果与最小坐标和sum相同, ...

  8. react中,用key值来解决一些奇葩问题

    编辑用户信息,角色信息无法加载到值 改进之后:思路:由于值是设置在state里面的,界面编辑时,会重服务器拉去数据,值也设置在state里面了,但是CheckboxGroup依然不会去渲染选中的值, ...

  9. 【Kail 学习笔记】自用KAIL更新源

    打开 /etc/apt/source.list 添加-保存即可 deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contr ...

  10. 利用socket编程在ESP32上搭建一个TCP客户端

    通过之前http://www.cnblogs.com/noticeable/p/7636582.html中对socket的编程,已经可以知道如何通过socket编程搭建服务器和客户端了,现在,就在ES ...