看到 t-io 老谭的 用t-io来写一个网页聊天室或客服是个怎样的体验, 一时手痒, 就打算用 ActFramework 来写一个网页聊天室, 看看是什么体验.
废话少说,撸起袖子就是干。
1. 创建项目
运行下面的命令创建一个新的 ActFramework 应用项目:
mvn archetype:generate -B \
-DgroupId=com.myproj \
-DartifactId=chatroom \
-DarchetypeGroupId=org.actframework \
-DarchetypeArtifactId=archetype-quickstart \
-DarchetypeVersion=1.8.7.3
2. 项目目录结构
运行之后可以看到如下目录结构:
.
├── pom.xml
├── run_dev
├── run_dev.bat
├── run_prod
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── myproj
│ │ └── AppEntry.java
│ └── resources
│ ├── com
│ │ └── myproj
│ ├── conf
│ │ ├── app.properties
│ │ ├── prod
│ │ │ └── app.properties
│ │ └── uat
│ │ └── app.properties
│ ├── logback.xml
│ └── rythm
│ └── com
│ └── myproj
│ └── AppEntry
│ └── home.html
└── test
└── java
└── com
└── myproj
3. pom.xml 文件
生成的项目 pom.xml 很简单, 打开看应该是这样的:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.myproj</groupId>
<artifactId>chatroom</artifactId>
<version>1.0-SNAPSHOT</version>
<name>My Awesome Application</name>
<parent>
<groupId>org.actframework</groupId>
<artifactId>act-starter-parent</artifactId>
<version>1.8.7.1</version>
</parent>
<properties>
<java.version>1.8</java.version>
<app.entry>com.myproj.AppEntry</app.entry>
</properties>
</project>
4. 服务端代码
就我们的聊天室应用来讲, 无需添加任何其他依赖, 直奔 com.myproj.AppEntry 文件:
package com.myproj;
import act.Act;
import act.inject.DefaultValue;
import act.util.Output;
import org.osgl.mvc.annotation.GetAction;
/**
* A simple hello world app entry
*
* Run this app, try to update some of the code, then
* press F5 in the browser to watch the immediate change
* in the browser!
*/
@SuppressWarnings("unused")
public class AppEntry {
/**
* The home (`/`) endpoint.
*
* This will accept a query parameter named `who` and
* render a template (resources/rythm/__package__/AppEntry/home.html),
* where `__package__` corresponding to the package name, e.g.
* if your package is `com.mycomp.myproj`, then `__package__`
* is `com/mycomp/myproj`.
*
* @param who
* request query parameter to specify the hello target.
* default value is `World`.
*/
@GetAction
public void home(@DefaultValue("World") @Output String who) {
}
public static void main(String[] args) throws Exception {
Act.start();
}
}
把不需要的注释去掉, 再将 home
方法稍微改改, 去掉 who
参数, 让这个文件变得干净一点:
package com.myproj;
import act.Act;
import org.osgl.mvc.annotation.GetAction;
public class AppEntry {
/**
* 响应 `/` 请求. 调用 `home.html` 模板生成 HTML 页面响应
*/
@GetAction
public void home() {
}
public static void main(String[] args) throws Exception {
Act.start();
}
}
4.1 Websocket 消息处理方法
下面是肉戏了, 我们需要在 AppEntry
类加上一个方法来处理 websocket 请求, 也就是聊天室发送的即时消息:
@WsAction("msg")
public void onMessage(String message, WebSocketContext context) {
// suppress blank lines
if (S.notBlank(message)) {
context.sendToPeers(message);
}
}
这就好了? 6 行代码搞定了服务器端的 websocket 消息处理? 我读书少所以不骗你, 真的就是这样啊. 是不是很 easy 啊? 到这里很想说 JFinal 波总 的那句 "打完收工" (感觉很酷酷的样子). 不过我们这个聊天室必须还有前端才行, 所以还没有打完, 继续看吧:
5. 前端页面
在上面的目录结构中找到 home.html 文件, 打开看是这样的:
<!DOCTYPE html>
<html lang="en">
@args String who
<head>
<title>Hello World - ActFramework</title>
</head>
<body>
<h1>Hello @who</h1>
<p>
Powered by ActFramework @act.Act.VERSION.getVersion()
</p>
</body>
</html>
我们需要改造一下变成一个简单的聊天室:
<html>
<head>
<title>WebSocket Chat</title>
<script src="/~/asset/js/jquery.js"></script>
<script src="/~/asset/js/jquery.ext.js"></script>
<style type="text/css">
html,body {
width:100%;
height:100%;
background-color: black;
color: beige;
}
body {
height: 80%;
}
input {
padding: 8px 5px;
}
.center {
margin-left: auto;
margin-right: auto;
width: 70%;
}
.chatform {
margin-left: auto;
margin-right: auto;
margin-bottom: 0;
width: 70%;
}
form{
width: 100%;
}
label{
display: inline;
width: 100px;
}
#msg{
display: inline;
width: 100%;
}
</style>
</head>
<body>
<div class="page">
<div class="center" >
<h1>WebSocket @i18n("demo") - @i18n("chatroom")</h1>
<div id="chat" style="height:100%;width: 100%; overflow: auto;">
</div>
<form onsubmit="return false;" class="chatform" action="">
<label for="msg">@i18n("message")</label>
<input type="text" name="message" id="msg" onkeypress="if(event.keyCode==13) { send(this.form.message.value); this.value='' } " />
</form>
</div>
</div>
<script>
var socket;
function connect() {
socket = $.createWebSocket('/msg');
//socket = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/msg");
socket.onmessage = function (event) {
var home = document.getElementById('chat');
home.innerHTML = home.innerHTML + event.data + "<br />";
};
}
if (window.WebSocket) {
connect();
} else {
alert("Your browser does not support Websockets. (Use Chrome)");
}
function send(message) {
if (!window.WebSocket) {
return false;
}
if (socket.readyState === WebSocket.OPEN) {
socket.send(message);
} else {
reconnect(message);
}
return false;
}
function reconnect(message) {
connect();
setTimeout(function() {
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
} else {
alert("The socket is not open.");
}
}, 500);
}
</script>
</body>
</html>
注意上面的 /~/asset/js/jquery.ext.js
这是 ActFramework 自带的对 jQuery 的一点小扩展, 其中就有 $.createWebSocket()
方法用来创建客户端的 websocket client.
6. 运行项目
好了, 现在运行一把试试. 在项目目录下运行:
mvn compile act:run
运行上面的命令之后差不多能看到下面的控制台消息
___ _ _ _ _____ ___ ___ ___ __ __
/ __| | || | /_\ |_ _| | _ \ / _ \ / _ \ | \/ |
| (__ | __ | / _ \ | | | / | (_) | | (_) | | |\/| |
\___| |_||_| /_/ \_\ |_| |_|_\ \___/ \___/ |_| |_|
powered by ActFramework r1.8.7-2f28
version: v1.0-SNAPSHOT-180505_2214
scan pkg: com.myproj
base dir: /home/luog/p/greenlaw110/chatroom
pid: 20757
profile: dev
mode: DEV
zen: Readability counts.
2018-05-05 22:14:54,447 INFO a.Act@[main] - loading application(s) ...
2018-05-05 22:14:54,459 INFO a.a.App@[main] - App starting ....
2018-05-05 22:14:54,639 WARN a.h.b.ResourceGetter@[main] - URL base not exists: META-INF/resources/webjars
2018-05-05 22:14:54,653 WARN a.a.DbServiceManager@[main] - DB service not initialized: No DB plugin found
2018-05-05 22:14:55,379 WARN a.m.MailerConfig@[main] - smtp host configuration not found, will use mock smtp to send email
2018-05-05 22:14:55,831 INFO a.a.App@[main] - App[chatroom] loaded in 1372ms
2018-05-05 22:14:55,837 INFO a.a.ApiManager@[jobs-thread-3] - start compiling API book
2018-05-05 22:14:55,857 INFO o.xnio@[main] - XNIO version 3.3.8.Final
2018-05-05 22:14:55,885 INFO o.x.nio@[main] - XNIO NIO Implementation Version 3.3.8.Final
2018-05-05 22:14:56,003 INFO a.Act@[main] - network client hooked on port: 5460
2018-05-05 22:14:56,004 INFO a.Act@[main] - CLI server started on port: 5461
2018-05-05 22:14:56,005 INFO a.Act@[main] - app is ready at: http://192.168.1.2:5460
2018-05-05 22:14:56,005 INFO a.Act@[main] - it takes 2269ms to start the app
现在是检验成果的时候了, 直奔浏览器, 打开 http://localhost:5460/ 可以开始玩了:
打完收工!
7. 总结
到这里我们可以来总结一下这个项目了. 我们的项目非常简单, 只有一个主程序 AppEntry
, 提供了两个请求处理方法:
home()
- 接受发送到/
的 GET 请求, 加载home.html
模板生成 HTML 响应onMessage()
- 接受发送到/msg
的 WS 请求, 转发消息到所有连接到这个服务端口的客户端
两个方法都非常简单 (可以用波总的极简么?), 全部项目的源代码统计如下:
luog@luog-X510UQR:~/p/greenlaw110/chatroom$ cloc src
7 text files.
7 unique files.
4 files ignored.
http://cloc.sourceforge.net v 1.60 T=0.03 s (99.6 files/s, 8069.1 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
HTML 1 10 17 93
XML 1 17 3 72
Java 1 6 4 21
-------------------------------------------------------------------------------
SUM: 3 33 24 186
-------------------------------------------------------------------------------
这就是 ActFramework 用 21 行代码实现的极简网页聊天室后端. 整个项目源码只能在 gitee 上获取:
https://gitee.com/greenlaw110/act-chatroom/
7. 结束语
这是一个非常简单的网页聊天室应用, 没有谭总的那个功能丰富, 不过写起来貌似要比 t-io 稍微容易一点哦. 以后有时间加上用户登录认证, 就等下回分解了
更多关于 ActFramework 的情况, 请关注