三种TCP协议聊天室实现
一 概述
- 使用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协议聊天室实现的更多相关文章
- 网络编程TCP协议-聊天室
网络编程TCP协议-聊天室(客户端与服务端的交互); <span style="font-size:18px;">1.客户端发数据到服务端.</span> ...
- Rsyslog的三种传输协议简要介绍
rsyslog的三种传输协议 rsyslog 可以理解为多线程增强版的syslog. rsyslog提供了三种远程传输协议,分别是: 1. UDP 传输协议 基于传统UDP协议进行远程日志传输,也是传 ...
- 基于Linux的TCP网络聊天室
1.实验项目名称:基于Linux的TCP网络聊天室 2.实验目的:通过TCP完成多用户群聊和私聊功能. 3.实验过程: 通过socket建立用户连接并传送用户输入的信息,分别来写客户端和服务器端,利用 ...
- TCP多线程聊天室
TCP协议,一个服务器(ServerSocket)只服务于一个客户端(Socket),那么可以通过ServerSocket+Thread的方式,实现一个服务器服务于多个客户端. 多线程服务器实现原理— ...
- windows网络编程-C语言实现简单的TCP协议聊天
TCP/IP协议(面向连接协议)类似于打电话时,对方一定在手机附近并且此刻都在和对方进行通话.一定保证双方都在线,才能进行数据传输.UDP/IP协议(无连接协议)就像邮箱,不保证对方一定在等你邮件且对 ...
- Linux三种SSH协议登陆方式
在实际工作中遇到了三种不同SSH协议登陆Linux服务器的方式,由简单到复杂分别阐述:一.最简单也是最不安全的root账号密码登陆通过root账号+root密码登陆Linux服务器. 二.普通用户登陆 ...
- Java 网络编程 -- 基于TCP 实现聊天室 群聊 私聊
分析: 聊天室需要多个客户端和一个服务端. 服务端负责转发消息. 客户端可以发送消息.接收消息. 消息分类: 群聊消息:发送除自己外所有人 私聊消息:只发送@的人 系统消息:根据情况分只发送个人和其他 ...
- Java网络编程三--基于TCP协议的网络编程
ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状体 Socket accept():如果接收到客户端的连接请求,该方法返回一个与客户端对应Socket ...
- 三、TCP协议
TCP(Transmission Control Protocol)传输控制协议:顾名思义就是对数据的传输进行控制 TCP报头 序号:相当于编号,当TCP数据包过大的时候会进行分段,分段之后按序号顺序 ...
随机推荐
- 留个纪念,过了这么多年,又干回Android了!
这个博客中的好多Android知识已经老得不像样子了,没想到,还有干回来的一天.怎么说呢,只要坚持一下,总会有机会能做自己爱做的事情的. 加油! ---- 于武汉出差的第5天
- VS中卸载Visual Assist X
Tools=>Extensions and updates=>找到Visual Assist X 卸载:
- Qt编写控件属性设计器9-数据库采集
一.前言 数据库作为数据源,在很多组态软件中使用非常多,指定数据库类型,填写好数据库连接信息,指定对应的数据库表和字段,采集间隔,程序按照采集间隔自动采集数据库数据,绑定到界面上的控件赋值显示即可.使 ...
- Ubuntu 16.04 haproxy + keeplive
WEB架构
- SpringMvc+ajax跨域请求时,出现options类型的请求并返回403的解决方案
在使用 $.ajax({ url:'http://127.0.0.1:8081/rest/ccxxx/xxxx', type:'POST', dataType:"json", co ...
- spark报错:invalid token
启动spark报错,启动container失败,去看yarn的日志,显示invalid token, 经过排查是hadoop子节点的配置和主节点的配置不一致导致的,同步之后,问题解决.
- JEECG新建用户不用系统用户表的实现
首先新增组织机构 和 角色: -- 新增 组织机构 INSERT INTO `t_s_depart` VALUES ('dept001', '你所在的机构', '你所在的机构的描述', null, ' ...
- jQuery BlockUI Plugin Demo
1.Login Form $(document).ready(function() { $('#demo1').click(function() { $.blockUI({ message: $('# ...
- localStorage 存储 数组
let str = JSON.stringify(data.list); localStorage.setItem("options",str); let optionss=loc ...
- 添加tomcat8为服务
跟上一篇添加zookeeper为服务基本类似 脚本如下: #!/bin/bash CATALANA_HOME=/usr/local/tomcat8 export JAVA_HOME=/usr/loca ...