C语言tcp协议的聊天室,三种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 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 + "

";

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() + '

').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 channelGroup = new HashSet<>();

while (selector.select() > 0) {

Set 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 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() + '

';

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 {

@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 {

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() + " 上线" + "

");

});

group.add(channel);

}

@Override

public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

group.forEach(ch -> {

ch.writeAndFlush(ctx.channel().remoteAddress() + " 下线" + "

");

});

}

@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 + "

");

} else {

ch.writeAndFlush("自己:" + msg + "

");

}

});

}

@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() + "

");

}

} finally {

group.shutdownGracefully();

}

}

}

ClientChannelInitializer.java

public class ClientChannelInitializer extends ChannelInitializer {

@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() {

@Override

protected void channelRead0(ChannelHandlerContext ctx, String msg) {

System.out.println(msg);

}

});

}

}

五 总结

Netty实现简单,逻辑清晰,但是隐藏了很多复杂的细节,后续的学习再慢慢剖析吧。

IO的线程模型,实现比较通俗易懂。

NIO的实现相对比较难懂,需要大家对Selector、Channel和Buffer有比较深刻的理解,不然很容易出错。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
里面包含聊天室的客户端和服务器端的源文件和一份完整的设计报告。 一、 系统概要 本系统能实现基于VC++的网络聊天室系统。有单独的客户端、服务器端。 服务器应用程序能够接受来自客户端的广播,然后向客户端发送本机的IP与服务端口,让客户端接入到服务器进行聊天,检测用户名是否合法(重复),服务器责接收来自客户端的聊天信息,并根据用户的需求发送给指定的人或所有人,能够给出上线下线提示。客户端能够发出连接请求,能编辑发送信息,可以指定发给单人或所有人,能显示聊天人数,上线下线用户等。 二、 通信规范的制定 服务请求规范: 服务器端: (1) 创建一个UDP的套接字,接受来自客户端的广播请求,当请求报文内容为“REQUEST FOR IP ADDRESS AND SERVERPORT”时,接受请求,给客户端发送本服务器TCP聊天室的端口号。 (2) 创建一个主要的TCP协议的套接字负责客户端TCP连接 ,处理它的连接请求事件。 (3)在主要的TCP连接协议的套接字里面再创建TCP套接字保存到动态数组里,在主要的套接字接受请求后 ,就用这些套接字和客户端发送和接受数据。 客户端: (1) 当用户按“连接”按钮时,创建UDP协议套接字,给本地计算机发广播,广播内容为“REQUEST FOR IP ADDRESS AND SERVERPORT”。 (2)当收到服务器端的回应,收到服务器发来的端口号后,关闭UDP连接。根据服务器的IP地址和端口号重新创建TCP连接。 故我思考:客户端一定要知道服务器的一个端口,我假设它知道服务器UDP服务的端口,通过发广播给服务器的UDP服务套接字,然后等待该套接字发回服务器TCP聊天室服务的端口号,IP地址用ReceiveForom也苛刻得到。 通信规范 通信规范的制定主要跟老师给出的差不多,并做了一小点增加: (增加验证用户名是否与聊天室已有用户重复,在服务器给客户端的消息中,增加标志0) ① TCP/IP数据通信 --- “聊天”消息传输格式 客户机 - 服务器 (1)传输“用户名” STX+1+用户名+ETX (2) 悄悄话 STX+2+用户名+”,”+内容+ETX (3) 对所有人说 STX+3+内容+ETX 服务器- 客户机 (0)请求用户名与在线用户名重复 //改进 STX+0+用户名+EXT (1)首次传输在线用户名 STX+1+用户名+ETX (2)传输新到用户名 STX+2+用户名+ETX (3)传输离线用户名 STX+3+用户名+ETX (4)传输聊天数据 STX+4+内容+ETX (注:STX为CHR(2),ETX 为CHR(3)) 三、 主要模块的设计分析 四、 系统运行效果 (要求有屏幕截图) 五、 心得与体会
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值