在之前的Netty相关学习笔记中,学习了如何去实现聊天室的服务段,这里我们来实现聊天室的客户端,聊天室的客户端使用的是Html5和WebSocket实现,下面我们继续学习.

创建客户端

接着第五个笔记说,第五个笔记实现了简单的静态资源服务起,那么我们利用这个静态资源服务起为我们提供页面,创建一个socket.html页面,在这个页面中我们实现Socket连接,连接到我们的Netty搭建的聊天服务器上,因此我们需要创建一个聊天页面和Socket连接,这里我们假定Socket连接地址为 http://localhost:8080/socket 我们首先实现页面的开发和Socket的连接。

客户端代码

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Chat</title>
</head>
<body>
<script type="text/javascript">
var socket;
if (!window.WebSocket) {
window.WebSocket = window.MozWebSocket;
}
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8080/socket");
socket.onmessage = function (event) {
var ta = document.getElementById('chatContent');
ta.value = ta.value + '\n' + event.data
};
socket.onopen = function (event) {
var ta = document.getElementById('chatContent');
ta.value = "连接开启!";
};
socket.onclose = function (event) {
var ta = document.getElementById('chatContent');
ta.value = ta.value + "连接被关闭";
};
} else {
alert("浏览器不支持 WebSocket!");
} function send(message) {
if (!window.WebSocket) {
return;
}
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
} else {
alert("连接没有开启.");
}
}
</script>
<form onsubmit="return false;">
<h1>基于Netty构建的聊天室</h1>
<textarea id="chatContent" style="width: 100%; height: 400px;"/>
<hr>
<input type="text" name="message" style="width: 300px">
<input type="button" value="发送消息" onclick="send(this.form.message.value)">
<input type="button" onclick="javascript:document.getElementById('chatContent').value=''" value="清空记录">
</form>
</body>
</html>

页面效果

启动完成后,输入http://localhost:8080/socket.html 应当可以看到如下的界面,这些方法和第五个笔记的没有什么新得东西,就是新增了一个HTML页面而已.

[

可以看到连接被关闭的字样,这是因为我们的服务端还没有完成,我们将服务端继续改进即可。

完善服务端

在第四个笔记的时候,我们只实现了一个简单的服务端,使用telnet进行测试,我们可以将起拿过来修改一下,为我们所用..

修改内容分析

修改传递类型

第四篇文章中我们实现了基于String类型的数据,这里我们修改为TextWebSocketFrame 当然相应的返回类型和读取也需要对应修改(这里为了演示,移除了时间,不影响整体逻辑,只是界面效果).x修改后的代码如下:

package com.zhoutao123.simpleChat.html;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor; import static com.zhoutao123.simpleChat.utils.DatetimeUtils.getNowDatetime; public class TextWebSocketServiceAdapter extends SimpleChannelInboundHandler<TextWebSocketFrame> { // 创建ChannelGroup 用于保存连接的Channel
public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); // 当有新的Channel增加的时候
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// 获取当前的Channel
Channel channel = ctx.channel();
// 向其他Channel发送上线消息
channelGroup.writeAndFlush(
new TextWebSocketFrame(String.format("[服务器]\t用户:%s 加入聊天室!\n", channel.remoteAddress())));
// 添加Channel到Group里面
channelGroup.add(channel);
// 向新用户发送欢迎信息
channel.writeAndFlush(
new TextWebSocketFrame(String.format("你好,%s欢迎来到Netty聊天室\n", channel.remoteAddress())));
} @Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
// 用户退出后向全部Channel发送下线消息
channelGroup.writeAndFlush(
new TextWebSocketFrame(
String.format("[服务器]\t用户:%s 离开聊天室!\n", ctx.channel().remoteAddress())));
// 移除
channelGroup.remove(ctx.channel());
} @Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
// 服务器接收到新的消息后之后执行
// 获取当前的Channel
Channel currentChannel = ctx.channel();
// 遍历
for (Channel channel : channelGroup) {
String sendMessage = "";
// 如果是当前的用户发送You的信息,不是则发送带有发送人的信息
if (channel == currentChannel) {
sendMessage = String.format("[You]\t%s\n", msg.text());
} else {
sendMessage = String.format("[%s]\t %s\n", currentChannel.remoteAddress(), msg.text());
}
channel.writeAndFlush(new TextWebSocketFrame(sendMessage));
}
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 发送异常的时候通知移除
Channel channel = ctx.channel();
channelGroup.writeAndFlush(
new TextWebSocketFrame(String.format("[服务器]\t 用户 %s 出现异常掉线!\n", channel.remoteAddress())));
ctx.close();
}
}

新增解码器和编码

既然接收和发送的数据类型改变了,那么我们也要相应的修改解码器和编码器,添加以下代码即可.

package com.zhoutao123.simpleChat.html;

import com.waylau.netty.demo.websocketchat.TextWebSocketFrameHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler; /**
* 服务端 ChannelInitializer
*
* @author waylau.com
* @date 2015-3-13
*/
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> { @Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//将请求和应答消息编码或解码为HTTP消息
pipeline.addLast(new HttpServerCodec());
//将HTTP消息的多个部分组合成一条完整的HTTP消息
pipeline.addLast(new HttpObjectAggregator(64 * 1024));
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpServerHandleAdapter());
// 添加以下代码
pipeline.addLast(new WebSocketServerProtocolHandler("/socket"));
pipeline.addLast(new TextWebSocketServiceAdapter());
}
}

测试效果

启动服务器,打开聊天界面测试:

可以看到实现了基本的简单的聊天效果,至于压力测试还没有测试,不知道能承载多少个用户,至此我们从丢弃服务到实现在线群聊的实现,大致对Netty有了基本的了解,后期我们将对Netty实现源码级别的学习和了解.

Netty学习笔记(六) 简单的聊天室功能之WebSocket客户端开发实例的更多相关文章

  1. Netty学习笔记(四) 简单的聊天室功能之服务端开发

    前面三个章节,我们使用了Netty实现了DISCARD丢弃服务和回复以及自定义编码解码,这篇博客,我们要用Netty实现简单的聊天室功能. Ps: 突然想起来大学里面有个课程实训,给予UDP还是TCP ...

  2. 通过WebSocket实现一个简单的聊天室功能

    WebSocket WebSocket是一个协议,它是是基于TCP的一种新的网络协议,TCP协议是一种持续性的协议,和HTTP不同的是,它可以在服务器端主动向客户端推送消息.通过这个协议,可以在建立一 ...

  3. Netty 学习(六):创建 NioEventLoopGroup 的核心源码说明

    Netty 学习(六):创建 NioEventLoopGroup 的核心源码说明 作者: Grey 原文地址: 博客园:Netty 学习(六):创建 NioEventLoopGroup 的核心源码说明 ...

  4. Netty 学习笔记(1)通信原理

    前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始.   Netty 的通信原理 Netty 底层 ...

  5. Netty学习笔记-入门版

    目录 Netty学习笔记 前言 什么是Netty IO基础 概念说明 IO简单介绍 用户空间与内核空间 进程(Process) 线程(thread) 程序和进程 进程切换 进程阻塞 文件描述符 文件句 ...

  6. ASP.NET Signalr 2.0 实现一个简单的聊天室

    学习了一下SignalR 2.0,http://www.asp.net/signalr 文章写的很详细,如果头疼英文,还可以机翻成中文,虽然不是很准确,大概还是容易看明白. 理论要结合实践,自己动手做 ...

  7. java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

    java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...

  8. [SignalR]一个简单的聊天室

    原文:[SignalR]一个简单的聊天室 1.说明 开发环境:Microsoft Visual Studio 2010 以及需要安装NuGet. 2.添加SignalR所需要的类库以及脚本文件: 3. ...

  9. Netty学习笔记(二) 实现服务端和客户端

    在Netty学习笔记(一) 实现DISCARD服务中,我们使用Netty和Python实现了简单的丢弃DISCARD服务,这篇,我们使用Netty实现服务端和客户端交互的需求. 前置工作 开发环境 J ...

随机推荐

  1. Python程序员为什么一定要掌握Linux?

    不少Python新手经常问到学Python到底需不需要学习Linux? Python不是支持Windows和Linux操作系统吗?能在Windows下开发为什么还要学习Linux? 问这样的问题的朋友 ...

  2. [Swift]LeetCode292. Nim游戏 | Nim Game

    You are playing the following Nim Game with your friend: There is a heap of stones on the table, eac ...

  3. [Swift]LeetCode609. 在系统中查找重复文件 | Find Duplicate File in System

    Given a list of directory info including directory path, and all the files with contents in this dir ...

  4. PHP的Memcached简单实现

    Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.也可动态缓存一些实 ...

  5. 12.Git分支-推送(push)、跟踪分支、拉取(pull)、删除远程分支

    1.推送 本地的分支并不会自动与远程仓库同步,你可以显示的向远程仓库推送你的分支.例如你在本地创建了一个dev分支,你想其他的人和你一样在dev之下进行工作,可以使用 git push <rem ...

  6. 通过案例了解Hystrix的各种基本使用方式

    1 通过一些算术题了解系统发生错误的概率 我们一般用每秒查询率(Query Per Second,简称QPS)来衡量一个网站的流量,QPS是指一台服务器在一秒里能处理的查询次数,它可以被用来衡量服务器 ...

  7. @@ITENTITY

    @@identity是表示的是最近一次向具有identity属性(即自增列)的表插入数据时对应的自增列的值,是系统定义的全局变量.一般系统定义的全局变量都是以@@开头,用户自定义变量以@开头.sele ...

  8. hdu:2036.改革春风吹满地

    Problem Description “ 改革春风吹满地,不会AC没关系;实在不行回老家,还有一亩三分地.谢谢!(乐队奏乐)” 话说部分学生心态极好,每天就知道游戏,这次考试如此简单的题目,也是云里 ...

  9. Asp.Net MVC路由生成URL过程

    这次谈一谈Asp.Net MVC中所学到的路由生成URL的相关技术,顺便提一提遇到的一些坑,真的是掉坑掉多了,也就习以为常了,大不了从坑里再爬出来.初学者,包括我,都以为,mvc的核心是模型视图控制器 ...

  10. 在AspNetCore中使用极验做行为认证

    先上效果图 极验的流程 极验官方文档地址 https://docs.geetest.com/install/deploy/server/csharp 简单说明一下极验的验证流程 引用官方的图片 向服务 ...