赞
踩
近期由于业务需要,需要与视频会议服务器整合开发,由于会议服务器的控制方式要求采用Websocket方式,并且是WSS(加密Websocket协议), 由于一直采用VB6、VB.Net开发,因此就在网上搜索是否有合适的解决方案,花了两天时间都没有找到合适的。虽然笔者找到一些实现方案,但是要么就是VB实现的,不支持WSS,要么就是C#实现的,不支持VB开发,要么不支持64位Windows程序调用,反正没有找到合适的。因此不得不让笔者花时间来自己实现一个。
笔者,完全按照H5的Websocket对象实现了一个Websocket组件,用于支持VB6、VB.Net与C#等程序语言调用、支持原生64位与32位实现、支持OpenSSL实现安全套接字的WSS协议。Websocket代码完全采用C/C++实现,Socket I/O采用异步高性能调用方式。
Websocket组件完全参照H5的Websocket实现方式,具备的方法、属性与事件如下:
组件名称:VBToolsLib.Websocket,采用Com方式实现,同时编译成x86与x64版本,
以编译的动态库形式发布,需要执行Regsvr32程序来注册这个COM组件,在64位操作系统下建议两个版本都注册。
该组件完全永久免费使用,没有任何功能限制,也没有时间限制,确保不含任何失效时间或者远程下发关闭的后门,可以长期永久免费无任何限制地使用。由于实现源码逻辑复杂,编译复杂,所以不提供组件的实现源码,省得麻烦。如有部分定制需求,可在评论区发言,笔者酌情修补。
VB,VB.NET, C# 都需要添加引用,引用的组件库名为:VBToolsLib,如下图所示:
可以在github里下载这个组件:
https://github.com/wenshui2008/WebsocketVB
属性名称 | 说明 |
---|---|
readyState | 只读属性 readyState 表示连接状态,以下值: |
0 - 表示连接尚未建立。 | |
1 - 表示连接已建立,可以进行通信。 | |
2 - 表示连接正在进行关闭。 | |
3 - 表示连接已经关闭或者连接不能打开。 | |
bufferedAmount | 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本消息条数。每调用一次send,就会在组件的队列里添加一条消息,如果网速不足够快消息不一定会立即发送出去,需要排队等待。 |
方法名称 | 方法说明 |
---|---|
open(sUrl) | 建立连接,输入的sUrl为Websocket的服务地址,例如:ws://192.168.1.200:3000、wss://192.168.1.200:3000 |
send(msg) | 使用连接发送数据,msg为要发送的文本字符串 |
close | 关闭连接 |
setwindowhandle64(hwnd) | 一个窗口句柄,Win32的HWND类型的句柄,C#与VB.Net的form的Handle,VB6的Form的 hWnd,为什么要增加这个方法呢,因为组件采用多线程方式,用单独的线程来处理网络I/O,这个方法就是用来让组件关联主线程、并在主线程上进行事件回调的。这个方法是H5的Websocket对象没有的。 |
setHttpHeader(name, value) | 设置http请求头字段,如果输入的请求头字段不存在,则添加一个;如果存在,则替换存在的头。在open方法调用前此方法设置的头可以有效,否则只能在close调用后重新调用open方法才能有效。这个方法是H5的Websocket对象没有的。 例如:setHttpHeader(“My-Header”,“Some Value”) |
事件名称 | 事件说明 |
---|---|
onopen | 连接成功建立时触发 |
onmessage(data) | 客户端接收到服务端消息数据时触发 |
onerror(errmsg) | 通信发生错误时触发 |
onclose | 连接关闭时触发 |
以下是各种语言调用的样本代码,非常简单,现在一一介绍。
1)在Form的Load事件创建组件,并设置主窗口句柄,调用setwindowhandle64来设置窗口句柄,setwindowhandle为32位版本
2)在Form的Close事件里,注销与窗口句柄的关联,这步不是必须
3)实现接收组件的事件回调方法,websock_onopen、websock_onmessage、websock_onclose与websock_onerror等事件,
4)调用send发送消息,调用close关闭连接
Public Class mainForm Dim WithEvents websock As VBToolsLib.Websocket Delegate Sub appendText(s As String) Dim useSetwindowhandle As Boolean = False Private Sub mainForm_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load Dim count As Integer 'TextBox.CheckForIllegalCrossThreadCalls = False '在窗口的初始化事件里创建Websocket对象 websock = New VBToolsLib.Websocket '接着关联一个主线程窗口句柄便于接收Websocket消息 'setwindowhandle方法用于设置关联窗口句柄(仅用于32位版本),setwindowhandle64为64位版本 '仅需要调用这两个方法中的一个即可,64位版本必须用setwindowhandle64方法 'websock.setwindowhandle(Me.Handle) 'setwindowhandle64 即能够用于Windows 32位又能用于64位 websock.setwindowhandle64(Me.Handle) '因为调用了setwindowhandle64方法,所以设置标志为True useSetwindowhandle = True '任何时间都可以调用bufferedAmount属性获取尚未发送的缓冲数目,这里仅是演示 count = websock.bufferedAmount '在调用open 方法之前可以设置Http 请求头,这些头放在标准的HTTP协议请求头里发送给服务器 websock.setHttpHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36") websock.setHttpHeader("Cookie", "XMPlayer=V1.0; PHPSESSID=3nm364h6bu2i80lp4esik5ki56") urlInput.Text = "wss://192.168.2.134:3000/" If (IntPtr.Size = 8) Then Me.Text &= " (x64)" End If End Sub Private Sub mainForm_FormClosed(sender As System.Object, e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed '在窗口关闭事件里取消窗口句柄关联,不是必须 If useSetwindowhandle Then websock.setwindowhandle(0) End If End Sub Private Sub OpenBtn_Click(sender As System.Object, e As System.EventArgs) Handles OpenBtn.Click Dim url url = urlInput.Text If url <> "" Then '连接服务器,执行的是异步操作,只能通过监听onopen与onerror事件来判断是否成功 websock.open(url) End If End Sub Private Sub CloseBtn_Click(sender As System.Object, e As System.EventArgs) Handles CloseBtn.Click websock.close() End Sub Private Sub SendBtn_Click(sender As System.Object, e As System.EventArgs) Handles SendBtn.Click Dim sMsg As String = msgInputBox.Text If sMsg <> "" Then websock.send(sMsg) End If End Sub Private Sub websock_onopen() Handles websock.onopen Dim sTime As String = DateDiff("s", "1970-01-01 00:00:00", Now) Dim sMsg = "" websock.send("login: " & sTime) sMsg = "OnOpen" & Chr(13) & Chr(10) If useSetwindowhandle Then msgListbox.AppendText(sMsg) Else '如果没有调用setwindowhandle关联窗体句柄,就必须用委托方式,否则可能会有多线程界面死锁问题 Me.Invoke(New appendText(AddressOf AppendTextMethod), sMsg) End If End Sub Private Sub websock_onmessage(ByVal msg As String) Handles websock.onmessage Dim sMsg As String sMsg = msg & Chr(13) & Chr(10) If useSetwindowhandle Then msgListbox.AppendText(sMsg) Else '如果没有调用setwindowhandle(x64),就必须用委托方式,否则会有多线程界面死锁问题 Me.Invoke(New appendText(AddressOf AppendTextMethod), sMsg) End If '这里演示处理消息,具体业务逻辑的实现可能不是这样的,这里仅为演示 If (msg = "[MSG]:close") Then websock.close() ElseIf (msg.Substring(0, 14) = "[MSG]:foreward") Then Dim newmsg = msg.Substring(14) '演示将消息重新转发出去,具体业务逻辑由调用者自己实现,这里仅仅为演示 websock.send("[MSG]:This is a foreward MSG:" & newmsg) End If End Sub Private Sub websock_onclose() Handles websock.onclose Dim sMsg As String sMsg = "Onclose" & Chr(13) & Chr(10) If useSetwindowhandle Then msgListbox.AppendText(sMsg) Else '没有调用setwindowhandle(x64),就必须用委托方式,否则有多线程问题 Me.Invoke(New appendText(AddressOf AppendTextMethod), sMsg) End If End Sub Private Sub websock_onerror(ByVal msg As String) Handles websock.onerror Dim sMsg As String sMsg = "onerror:" & msg & Chr(13) & Chr(10) If useSetwindowhandle Then msgListbox.AppendText(sMsg) Else '调用了 setwindowhandle(x64) 就不用委托方式了,否则会有多线程问题 Me.Invoke(New appendText(AddressOf AppendTextMethod), sMsg) End If End Sub Public Sub AppendTextMethod(msgText As String) msgListbox.AppendText(msgText) End Sub End Class
用VB.NET实现的界面如下:
过程与VB.NET的过程完全一样。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace CSTester { public partial class mainForm : Form { VBToolsLib.Websocket websock; public mainForm() { InitializeComponent(); } private void mainForm_Load(object sender, EventArgs e) { int count; //在窗口的初始化事件里创建Websocket对象 websock = new VBToolsLib.Websocket(); //关联主线程句柄便于接收Websocket消息 //setwindowhandle方法用于关联窗口句柄,setwindowhandle64为64位版本,调用其中一个即可 //websock.setwindowhandle((int)this.Handle); websock.setwindowhandle64((long)this.Handle); // 在调用open 方法之前可以设置Http 请求头,这些头放在标准的HTTP协议请求头里发送给服务器 websock.setHttpHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"); websock.setHttpHeader("Cookie", "XMPlayer=V1.0; PHPSESSID=3nm364h6bu2i80lp4esik5ki56"); urlInput.Text = "wss://192.168.2.134:3000/"; //演示获取属性 count = websock.bufferedAmount; //C#方式的设置事件 websock.onopen += websocket_onopen; websock.onclose += websocket_onclose; websock.onmessage += websocket_onmessage; websock.onerror += websocket_onerror; if (IntPtr.Size == 8) { this.Text += " (x64)"; } } private void mainForm_FormClosed(object sender, FormClosedEventArgs e) { //在窗口关闭事件里取消窗口句柄关联,不是必须 websock.setwindowhandle(0); } private void btnOpen_Click(object sender, EventArgs e) { string sUrl = urlInput.Text; if (sUrl != "") { websock.open(sUrl); } } private void btnClose_Click(object sender, EventArgs e) { websock.close(); } private void btnSend_Click(object sender, EventArgs e) { string sMsg = msgInput.Text; if (sMsg != "") { websock.send(sMsg); } } private void websocket_onopen() { string sTime; string sMsg; TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0); sTime = Convert.ToInt64(ts.TotalSeconds).ToString(); //这里演示给服务器发送一个消息,具体业务逻辑应该在服务器上实现 websock.send("login: " + sTime); sMsg = "OnOpen\r\n"; msgListbox.AppendText(sMsg); } private void websocket_onclose() { string sMsg; sMsg = "Onclose\r\n"; msgListbox.AppendText(sMsg); } private void websocket_onmessage(string msg) { string sMsg; sMsg = msg + "\r\n"; msgListbox.AppendText(sMsg); //这里演示处理消息,具体业务逻辑的实现可能不是这样的,这里仅为演示 if (msg == "[MSG]:close") { websock.close(); } else if (msg.Substring(0, 14) == "[MSG]:foreward") { string newmsg = msg.Substring(14); //演示将消息重新转发出去,具体业务逻辑由调用者自己实现,这里仅仅为演示 websock.send("[MSG]:This is a foreward MSG:" + newmsg); } } private void websocket_onerror(string errmsg) { string sMsg; sMsg = "onerror:" + errmsg + "\r\n"; msgListbox.AppendText(sMsg); } } }
C#的界面如下:
调用过程与以上一样,仅仅是VB6的代码不一样
VB6代码如下:
Dim WithEvents websock As VBToolsLib.Websocket Private Sub Form_Load() Dim count As Integer Set websock = New VBToolsLib.Websocket '设置一个窗口关联句柄,VB6仅支持32位版本即可 websock.setwindowhandle (Me.hWnd) count = websock.bufferedAmount '在调用open 方法之前可以设置Http 请求头,这些头放在标准的HTTP协议请求头里发送给服务器 websock.setHttpHeader "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; VB6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" websock.setHttpHeader "Cookie", "XMPlayer=V1.0; PHPSESSID=3nm364h6bu2i80lp4esik5ki56" urlInput.Text = "wss://192.168.2.134:3000/" End Sub Private Sub Form_Unload(Cancel As Integer) websock.setwindowhandle (0) End Sub Private Sub btnOpen_Click() Dim url As String url = urlInput.Text If url <> "" Then websock.open (url) End If End Sub Private Sub btnClose_Click() websock.Close End Sub Private Sub btnSend_Click() Dim sMsg As String sMsg = msgInput.Text If sMsg <> "" Then websock.send (sMsg) End If End Sub Private Sub websock_onopen() Dim sTime As String Dim sMsg As String sTime = DateDiff("s", "1970-01-01 00:00:00", Now) '演示发送消息,具体业务逻辑由开发者自己实现,这里仅为演示代码 websock.send ("login: " & sTime) sMsg = "OnOpen" & Chr(13) & Chr(10) msgListbox.Text = msgListbox.Text & sMsg End Sub Private Sub websock_onclose() Dim sMsg As String sMsg = "Onclose" & Chr(13) & Chr(10) msgListbox.Text = msgListbox.Text & sMsg End Sub Private Sub websock_onmessage(ByVal msg As String) Dim sMsg As String sMsg = msg & Chr(13) & Chr(10) msgListbox.Text = msgListbox.Text & sMsg '仅为演示处理消息,具体业务逻辑应该由开发者自己实现 If (msg = "[MSG]:close") Then websock.Close ElseIf Mid(msg, 1, 14) = "[MSG]:foreward" Then Dim newmsg newmsg = Mid(msg, 15) websock.send ("[MSG]:This is a foreward MSG:" & newmsg) End If End Sub Private Sub websock_onerror(ByVal errmsg As String) Dim sMsg As String sMsg = "onerror:" & errmsg & Chr(13) & Chr(10) msgListbox.Text = msgListbox.Text & sMsg End Sub Public Sub AppendTextMethod(msgText As String) 'msgListbox.AppendText (msgText) End Sub
注意:由于VB6本身不支持64位代码,因此只能用32位的组件。
该组件用于VB/C#的Websocket服务器端代码暂时没有时间实现,后续再实现。
测试程序的全部源代码在这:
https://github.com/wenshui2008/WebsocketVB
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。