C#与Nodejs的WebSocket通讯案例——WebSocket4Net的使用
前言
学了一学期的C#,最后老师要求做一个简单的贪吃蛇,为了实现登录注册这种功能,于是我想要去做一个WebScoket通信的功能。
WebSocket通讯
C#
C#客户端这边我们用了一个非常便捷的第三方包——WebSocket4Net。我这边使用的vs直接使用Nuget包管理工具去下载安装即可。官方的说明非常短,只有一点.正如它提供的Demo,我们来简单解读一下
//引入命名空间
using WebSocket4Net;
//实例化WebSocket对象,指定连接地址
WebSocket websocket = new WebSocket("ws://localhost:2012/");
//给对应的情况委托绑定方法
websocket.Opened += new EventHandler(websocket_Opened);
websocket.Error += new EventHandler<ErrorEventArgs>(websocket_Error);
websocket.Closed += new EventHandler(websocket_Closed);
websocket.MessageReceived += new EventHandler(websocket_MessageReceived);
//建立连接
websocket.Open();
private void websocket_Opened(object sender, EventArgs e){
//发送数据
websocket.Send("Hello World!");
}
node js
回到nodejs服务端,我们通过npm包管理工具来安装
npm install nodejs-websocket
然后就可以通过事件监听来建立监听,例如:
var ws = require('nodejs-websocket');
var server = ws.createServer(function(conn){
console.log("New connection")
//监听文本发送事件
conn.on("text", function (str) {
console.log("Received "+str)
conn.sendText(str.toUpperCase()+"!!!")
})
conn.on("error",function(code,reason){
console.log("Connection error");
})
//监听连接关闭事件
conn.on("close", function (code, reason) {
console.log("Connection closed")
})
}).listen(3030);
这样,就建立起来了监听。
通信测试
客户端
最近在写一个贪吃蛇的通信功能,现在在实现一个登陆功能的通信,这边来贴一下关键代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Big.Manager
{
/// <summary>
/// 单例模式老父亲(此单例模式不可应用于多线程情况)
/// </summary>
class Singleton<T> where T : new ()
{
private static T _instance ;
public static T GetInstance()
{
if (_instance == null)
{
_instance = new T();
}
return _instance;
}
}
}
class WebSocketManager:Singleton<WebSocketManager>{
private WebSocket _socket;
private string _address = "ws://localhost:3030/";
public WebSocketManager() {
_socket = new WebSocket(_address);
//开启连接
_socket.Open();
}
public void Send(string key,string message)
{
_socket.Send(key+":"+message);
}
}
class LoginManager : Singleton<LoginManager>{
//登录通信规则:空格为间隔区别: key 用户名 密码
public void Login(string username,string password) {
WebSocketManager.GetInstance().Send("login",username+" "+password);
}
}
private void LoginBtn_Click(object sender, EventArgs e){
String username = UsernameText.Text;
String password = PasswordText.Text;
LoginManager.GetInstance().Login(username,password);
}
这里记录一下一个比较有趣的前端输入拦截,在Winform的TextBox中,右下角有个事件,给它添加一个事件。
private void UsernameText_KeyPress(object sender, KeyPressEventArgs e)
{
int textLimit = 6;
if (UsernameText.TextLength < textLimit || (e.KeyChar == 8))
{
//ASCII的8意为着退格
if ((e.KeyChar >= '0' && e.KeyChar <= '9')
|| (e.KeyChar >= 'A' && e.KeyChar <= 'Z')
|| (e.KeyChar >= 'a' && e.KeyChar <= 'z')
|| (e.KeyChar == 8) || (e.KeyChar == '_'))
{
e.Handled = false;
}
else
{
MessageBox.Show(" 用户名只能为字母、数字和下划线! ");
e.Handled = true;
}
}
else
{
MessageBox.Show(" 用户名长度不能超过" + textLimit + "个字符 ");
e.Handled = true;
}
}
服务端
var ws = require('nodejs-websocket');
var server = ws.createServer(function(conn){
//监听文本发送事件
conn.on("text", function (str) {
//将收到的信息提取key
var key=str.split(":");
//数据传输标准:key:信息
//通过:分隔从而得到key
switch(key[0]){
case 'login':
console.log("登录信息");
break;
default:
console.log("the key not match");
break;
}
conn.sendText(str.toUpperCase()+"!!!")
})
conn.on("error",function(code,reason){
console.log("Connection error");
})
//监听连接关闭事件
conn.on("close", function (code, reason) {
console.log("Connection closed")
})
}).listen(3030);
效果
关于响应信息的接收我们在下面的贪吃蛇登录示例中应用。
与C#交互示例
登录通信
承接上例,我们来实现真正一个项目的登录效果:
服务端
下面是我们Nodejs的示例代码——index.js:
var ws = require('nodejs-websocket');
var LoginModule = require('./loginModule.js');
var server = ws.createServer(function(conn){
//监听文本发送事件
conn.on("text", function (str) {
//将收到的信息提取key
var key=str.split(":");
//数据传输标准:key:信息
//通过:分隔从而得到key
switch(key[0]){
case 'login':
//登录处理
new LoginModule().LoginHandle(conn,key[1]);
break;
default:
console.log("the key not match");
break;
}
})
conn.on("error",function(code,reason){
console.log("Connection error");
})
//监听连接关闭事件
conn.on("close", function (code, reason) {
console.log("Connection closed")
})
}).listen(3030);
我们将登录的处理封装到了一个Module之中——loginModule.js
//将登陆处理封装到该Login模块中
function Login() {
//userMess的前段为用户名、后段为密码
//解析用户信息,得到用户名和密码
this.parseUserMess = function(userMess){
var mess=userMess.split(" ");
var obj={
username:mess[0],
password:mess[1]
}
return obj;
}
//登录测试,得到登录结果
this.LoginResult = function(userMess) {
var obj=this.parseUserMess(userMess);
var username=obj.username;
var password=obj.password;
if(username=="batman"&&password=="123456"){
return true;
}else{
return false;
}
};
//登录处理,验证登录且进行相应的通信处理
this.LoginHandle=function(conn,userMess){
//登陆成功
if(this.LoginResult(userMess)){
console.log("用户登录成功");
conn.send("login:success");
}else{
console.log("用户登录失败");
conn.send("login:failed");
}
}
};
module.exports = Login;
这样我们就完成了服务端的登录检测逻辑,当然,这里是固定了写法,检测用户名和密码为:batman、123456,后期可以根据需要连接数据库来进行处理。
客户端
我们这里实现了一个单例模式,作为总管理的基类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Big.Manager
{
/// <summary>
/// 单例模式老父亲(此单例模式不可应用于多线程情况)
/// </summary>
class Singleton<T> where T : new ()
{
private static T _instance ;
public static T GetInstance()
{
if (_instance == null)
{
_instance = new T();
}
return _instance;
}
}
}
然后设置了一个WebSocket的总管理:
using System;
using WebSocket4Net;
using System.Collections.Generic;
namespace Big.Manager
{
/// <summary>
/// WebSocket通信管理总类(单例)
/// </summary>
class WebSocketManager:Singleton<WebSocketManager>
{
#region 通信配置属性
private WebSocket _socket;
private string _address = "ws://localhost:3030/";
#endregion
#region 响应事件属性
//value对应的是监听这个事件对应的委托方法们(重点圈住:们)
private Dictionary<string, Action<string>> eventDic
= new Dictionary<string, Action<string>>();
#endregion
#region 初始化和连接
public WebSocketManager() {
_socket = new WebSocket(_address);
//开启连接
_socket.Open();
//绑定方法
this._socket.MessageReceived += WebSocket_MessageReceived;
}
#endregion
#region 发送信息
public void Send(string key,string message)
{
if (_socket != null && _socket.State == WebSocket4Net.WebSocketState.Open)
{
_socket.Send(key + ":" + message);
}
}
#endregion
#region 接收消息
/// <summary>
/// 消息收到总事件分发方法
/// </summary>
void WebSocket_MessageReceived(object o,MessageReceivedEventArgs e)
{
//得到回传数据的key
string key=e.Message.Split(':')[0];
//字典中存在相应的key对应的委托记录
if (eventDic.ContainsKey(key))
{
//调用委托,将传回的参数传过去
eventDic[key](e.Message.Split(':')[1]);
}
//没有对应分发处理的情况
else
{
}
}
/// <summary>
/// 添加事件监听
/// </summary>
public void AddEventListener(string key,Action<string> action) {
//有没有对应的事件监听
//有的情况
if (eventDic.ContainsKey(key))
{
eventDic[key] += action;
}
//没有的情况
else
{
eventDic.Add(key, new Action<string>(action));
}
}
/// <summary>
/// 移除字典中的事件监听
/// </summary>
public void RemoveEventListener(string key, Action<string> action)
{
if (eventDic.ContainsKey(key))
{
//移除这个委托
eventDic[key] -= action;
}
}
/// <summary>
/// 清空所有事件监听(主要用在切换场景时)
/// </summary>
public void Clear()
{
eventDic.Clear();
}
#endregion
}
}
然后我们在form的登录提交按钮对应的响应事件中这样写道:
private void LoginBtn_Click(object sender, EventArgs e)
{
String username = UsernameText.Text;
String password = PasswordText.Text;
LoginManager.GetInstance().Login(username,password);
}
调用到的这个LoginManager中这样处理:
using Big.common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;
using log4net.Config;
[assembly: XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
namespace Big.Manager{
/// <summary>
/// 登录、注册管理
/// </summary>
class LoginManager : Singleton<LoginManager>
{
private static readonly ILog log = LogManager.GetLogger(typeof(LoginManager));
public LoginManager() {
//给webscoket管理器添加事件绑定
WebSocketManager.GetInstance().
AddEventListener(WebSocketKey.Login.ToString(),LoginHandle);
}
~LoginManager() {
WebSocketManager.GetInstance().
RemoveEventListener(WebSocketKey.Login.ToString(), LoginHandle);
}
//登录通信规则:空格为间隔区别: key:用户名 密码
public void Login(string username,string password) {
WebSocketManager.GetInstance().Send(WebSocketKey.Login,username+" "+password);
}
/// <summary>
/// 登录回调处理
/// </summary>
/// <param name="Mess">回调参数</param>
private void LoginHandle(string Mess) {
if (Mess.Equals("success")) {
log.Info("登录成功!");
}
else if (Mess.Equals("failed")) {
log.Info("登录失败!");
}
}
}
}
这里我们进行了日志输出,用到了一个.Net这边的组件,叫做:log4net。如果你不了解,可以参考这篇文章,使用起来非常简单。好了,接下来我们来测试一下。
测试结果
本文由 创作,采用 知识共享署名4.0 国际许可协议进行许可。本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。最后编辑时间为: 2021/05/07 01:41