三种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数据包过大的时候会进行分段,分段之后按序号顺序 ...
随机推荐
- 【Canvas】绘制几何级数Geometric series曲线 y=1+1/2+1/4+1/8+1/16+1/32+1/64+....
相关资料:https://baike.baidu.com/item/%E5%87%A0%E4%BD%95%E7%BA%A7%E6%95%B0/112584?fr=aladdin 图线: 代码: < ...
- 时间戳 Flume's Memory Consumption
效率 https://www.cnblogs.com/zhujiabin/p/6168671.html?utm_source=itdadao&utm_medium=referral http ...
- Apache 后台服务器(主要处理php及一些功能请求 如:中文url) Nginx 前端服务器(利用它占用系统资源少得优势来处理静态页面大量请求) Lighttpd 图片服务器 总体来说,随着nginx功能得完善将使他成为今后web server得主流。
Apache 后台服务器(主要处理php及一些功能请求 如:中文url) Nginx 前端服务器(利用它占用系统资源少得优势来处理静态页面大量请求) Lighttpd 图片服务器 总体来说,随着ngi ...
- SpringBoot持久层技术
一.Springboot整合mybatis maven中添加对数据库与mybatis的依赖 <dependencies> <dependency> <groupId> ...
- vue.js中如何使用scss
要使用 <style lang="sass"> 請記得要裝相依的套件 $ npm install sass-loader node-sass vue-style-loa ...
- osg::PagedLOD example
int main() { osg::ref_ptr<osgViewer::Viewer> viewer1 = new osgViewer::Viewer; osg::ref_ptr< ...
- osg qt 三维模型加载
osg::ref_ptr<osg::Node> OSG_Qt_::operateMatrix() { osg::ref_ptr<osg::Group> group = new ...
- 关于jmeter+ant+jenkins性能自动化将测试结果文件jtl转换成html文件遇到的问题。
1.ant自身缺陷,返回结果中有特殊字符,乱码字符,无法识别,jtl文件转换时报错. 2.jtl文件过大转换成html文件时出现内存溢出. 针对以上情况:可考虑使用BeenShell Sampler: ...
- WIN 10 看不到SAMBA共享的硬盘
1.SMB1.0/CIFS协议默认被关闭了,之前的勒索病毒就是用的这个协议的漏洞,所以你去“启动和关闭windows功能”下手动勾选启用SMB1.0/CIFS协议 2.管理员身份执行 sc.exe c ...
- activiti的坑
maven配置: <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-e ...