一般方法
让我们来描述解决该问题的方法。
在服务器端接收数据时,使用通道的远程地址(java.net.SocketAddress Channel.remoteAddress()
方法)来识别客户端。
这种识别可以使用类似于 Map<SocketAddress, Client>
的映射来完成,其中 Client
类或接口应包含适当的客户端连接(通道)相关上下文,包括其 Channel
。请确保及时更新映射:适当地处理“客户端已连接”和“客户端已断开连接”事件。
找到客户端后,您可以使用客户端连接(通道)映射向客户端发送适当的消息,但不包括当前发送消息的客户端。
此外,我建议您找一个使用 Netty 实现的好的聊天应用程序,并仔细研究它。
Netty 特定解决方案
让我们考虑服务器端实现,特别是 ProcessingHandler
类的实现。
它已经通过将活动通道表示为通道组来管理它们:
static final ChannelGroup channels1 =
new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
让通道组保持最新
当前实现处理“通道变为活动状态”事件以保持通道组最新:
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
channels1.add(ctx.channel());
}
但这只是其中的一部分:同样需要对“通道不活跃”事件进行对称处理。实现应该如下:
@Override
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
channels1.remove(ctx.channel());
}
广播:将接收到的消息发送到除当前频道外的所有频道
为了实现所需的行为,只需通过引入以下适当检查来更新实现:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
for (Channel ch : channels1) {
if (ch.equals(ctx.channel())) {
continue;
}
}
}
发送和接收字符串问题
目前,ResponseData
类的功能尚未实现。
为了使客户端和服务器正常工作,需要进行以下草案更改。
The ResponseData
class: the getStringValue
and toString
methods should be corrected:
String getStringValue() {
return this.strValue;
}
@Override
public String toString() {
return intValue + ";" + strValue;
}
The ResponseDataEncoder
class: it should use the string value:
private final Charset charset = Charset.forName("UTF-8");
@Override
protected void encode(final ChannelHandlerContext ctx, final ResponseData msg, final ByteBuf out) throws Exception {
out.writeInt(msg.getIntValue());
out.writeInt(msg.getStringValue().length());
out.writeCharSequence(msg.getStringValue(), charset);
}
The ResponseDataDecoder
class: it should use the string value:
private final Charset charset = Charset.forName("UTF-8");
@Override
protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out) throws Exception {
ResponseData data = new ResponseData();
data.setIntValue(in.readInt());
int strLen = in.readInt();
data.setStringValue(in.readCharSequence(strLen, charset).toString());
out.add(data);
}
The ClientHandler
class: it should correctly receive and handle the message:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
final ResponseData responseData = (ResponseData) msg;
System.out.println("The message sent from the server " + responseData);
update.accept(responseData.getIntValue());
}
参考资料
- Netty文档:SecureChat——基于TLS的聊天服务器,源自Telnet示例。特别是
SecureChatServerHandler
类的实现。
- 《Netty实战》(ISBN-13: 978-1617291470),Norman Maurer, Marvin Allen Wolfthal,《第3部分——网络协议》,子章节《12.2我们的WebSocket应用程序》。涵盖了“基于浏览器的聊天应用程序”的实现。