| |
|
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
| -> 网络协议 -> Netty 实现socket 连接和WebSocket 同时连接,并相互通信 -> 正文阅读 |
|
|
[网络协议]Netty 实现socket 连接和WebSocket 同时连接,并相互通信 |
1、maven 依赖<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.65.Final</version> </dependency> 2、Server 端public class HttpServer {
private int port;
public HttpServer(int port) {
this.port = port;
}
public void bind() {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap()
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new NettyServerChannelInitializer())
.option(ChannelOption.SO_BACKLOG, 500)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定端口,开始接收进来的连接
ChannelFuture future = bootstrap.bind(port).sync();
//关闭channel和块,直到它被关闭
future.channel().closeFuture().sync();
} catch (Exception e) {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
NettyServerChannelInitializer public class NettyServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline().addLast("socketChoose",new SocketChooseHandle());
channel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
channel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
channel.pipeline().addLast("commonhandler",new WebSocketHandler());
}
}
SocketChooseHandle public class SocketChooseHandle extends ByteToMessageDecoder {
/** 默认暗号长度为23 */
private static final int MAX_LENGTH = 23;
/** WebSocket握手的协议前缀 */
private static final String WEBSOCKET_PREFIX = "GET /";
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
String protocol = getBufStart(in);
if (protocol.startsWith(WEBSOCKET_PREFIX)) {
ctx.pipeline().addBefore("commonhandler","http-codec",new HttpServerCodec());
// HttpObjectAggregator:将HTTP消息的多个部分合成一条完整的HTTP消息
ctx.pipeline().addBefore("commonhandler","aggregator",new HttpObjectAggregator(65535));
// ChunkedWriteHandler:向客户端发送HTML5文件,文件过大会将内存撑爆
ctx.pipeline().addBefore("commonhandler","http-chunked",new ChunkedWriteHandler());
ctx.pipeline().addBefore("commonhandler","WebSocketAggregator",new WebSocketFrameAggregator(65535));
//用于处理websocket, /ws为访问websocket时的uri
ctx.pipeline().addBefore("commonhandler","ProtocolHandler", new WebSocketServerProtocolHandler("/ws"));
????????????// 此次要移除socket 相关的编码
ctx.pipeline().remove(StringDecoder.class);
ctx.pipeline().remove(StringEncoder.class);
}
in.resetReaderIndex();
ctx.pipeline().remove(this.getClass());
}
private String getBufStart(ByteBuf in){
int length = in.readableBytes();
if (length > MAX_LENGTH) {
length = MAX_LENGTH;
}
// 标记读位置
in.markReaderIndex();
byte[] content = new byte[length];
in.readBytes(content);
return new String(content);
}
}
WebSocketHandler public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {
private static Map<String, Channel> map = new ConcurrentHashMap<>();
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object object) throws Exception {
String msg = "";
if (object instanceof TextWebSocketFrame){
TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame)object;
msg = textWebSocketFrame.text();
}else {
msg = String.valueOf(object);
}
System.out.println("msg:"+msg);
for (String key: map.keySet()) {
if (key.equals(ctx.channel().id().toString())){
continue;
}
Channel channel = map.get(key);
ChannelFuture channelFuture = channel.writeAndFlush(msg);
if (!channelFuture.isSuccess()) {
channel.writeAndFlush(new TextWebSocketFrame(msg));
}
}
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerAdded::"+ctx.channel().id().asLongText());
InetSocketAddress socketAddress = (InetSocketAddress)ctx.channel().remoteAddress();
String hostAddress = socketAddress.getAddress().getHostAddress();
logger.info("IP:{}",hostAddress);
String clientId = ctx.channel().id().toString();
map.put(clientId,ctx.channel());
logger.info("map:{}",map.toString());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerRemoved::"+ctx.channel().id().asLongText());
String clientId = ctx.channel().id().toString();
map.remove(clientId);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("exceptionCaught::"+cause.getMessage());
String clientId = ctx.channel().id().toString();
map.remove(clientId);
ctx.close();
}
}
最后通过spring 的CommandLineRunner 实现服务启动 @Component
@Order(1)
public class TaskRunner implements CommandLineRunner {
private HttpServer httpServer = new HttpServer(10050);
@Override
public void run(String... args) throws Exception {
httpServer.bind();
}
}
3、Client 端? ? ? ? 3.1 websocket client ? ? ? ? ? ? ? ? hello.html代码如下 直接游览器允许 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
var webSocket;
if (window.WebSocket) {
webSocket = new WebSocket("ws://localhost:10050/ws");
webSocket.onmessage = function (ev) {
console.log("=====");
var res = document.getElementById("response");
res.value = res.value + "\n" + ev.data;
}
webSocket.onopen = function (ev) {
var req = document.getElementById("response");
req.value = "链接开启...."+"\n";
}
webSocket.onclose = function (ev) {
var req = document.getElementById("response");
req.value += "链接关闭...."+"\n";
}
function sendMessage(msg) {
//alert("send?");
if (!window.WebSocket){
return;
}
if (webSocket.readyState === WebSocket.OPEN) {
//alert("send!");
webSocket.send(msg);
document.getElementById("requestComment").value='';
}else {
alert("链接未开启!");
}
}
} else {
alert("浏览器不支持websocket")
}
</script>
<form οnsubmit="false">
<textarea style="height: 300px;width: 300px" id="requestComment" name="reqestcom"></textarea>
<button type="button" οnclick="sendMessage(this.form.reqestcom.value)">发送</button>
<textarea style="height: 300px;width: 300px" id="response"></textarea>
<button type="button" οnclick="document.getElementById('response').value=''">清空</button>
</form>
</body>
</html>
? ? ? ? 3.2 socket client public class TestClient {
private final String host;
private final int port;
public TestClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run() throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap().group(bossGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline().addLast("decoder",new StringDecoder(CharsetUtil.UTF_8));
channel.pipeline().addLast("encoder",new StringEncoder(CharsetUtil.UTF_8));
channel.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture future = bootstrap.connect(host, port).sync();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
String msg = scanner.nextLine();
future.channel().writeAndFlush(msg);
}
}finally {
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args)throws Exception {
new TestClient("127.0.0.1",10050).run();
}
}
ClientHandler /**
* @author xiaocg
*/
public class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
System.out.println(s.trim());
}
}
4、最终测试截图websocket 端发送
socket client 接收
socket client 发送
websocket 接收
代码工程详见 https://gitee.com/xiaochangg/study.git?下的data-transfer 工程 |
|
|
| 网络协议 最新文章 |
| 使用Easyswoole 搭建简单的Websoket服务 |
| 常见的数据通信方式有哪些? |
| Openssl 1024bit RSA算法---公私钥获取和处 |
| HTTPS协议的密钥交换流程 |
| 《小白WEB安全入门》03. 漏洞篇 |
| HttpRunner4.x 安装与使用 |
| 2021-07-04 |
| 手写RPC学习笔记 |
| K8S高可用版本部署 |
| mySQL计算IP地址范围 |
|
|
| 上一篇文章 下一篇文章 查看所有文章 |
|
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
| 360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年12日历 | -2025/12/8 12:06:14- |
|
| 网站联系: qq:121756557 email:121756557@qq.com IT数码 |