带 Web Socket 的聊天应用





5.00/5 (2投票s)
尝试创建一个最小的聊天应用程序,使用 Web Socket。
引言
这个应用程序是我尝试使用 WebSockets 创建的一个最小的聊天应用程序。我试图使用最少的代码来展示如何使用 WebSockets。WebSockets 是一种允许您将数据从 Web 服务器推送到 Web 浏览器的技术。
背景
要使用 WebSockets,您的 Web 服务器需要安装 ASP.NET 4.5 或更高版本以及 WebSocket 协议,在 添加角色和功能 > 服务器角色 > Web 服务器 > Web 服务器 > 应用程序开发 > WebSocket 协议 下安装。
Using the Code
要设置部署,请将 WebSocket.zip 解压缩到 Web 服务器上的 C:\inetpub\wwwroot\WebSocket。打开 IIS 控制台 并创建一个名为 WebSocket 的虚拟目录。
接下来,将您的浏览器指向 https:///WebSocket/Chat.aspx.
Web 部署包含两个文件:Handler1.ashx 和 Chat.aspx。Handler1.ashx 处理程序文件处理 WebSockets
请求。它使用 HandleSocketRequest
函数处理 WebSockets 请求。Async
函数在套接字连接打开时循环。它使用 Await
等待消息,然后将消息广播给所有已注册的用户。
Imports System.Web
Imports System.Web.Services
Imports System.Net.WebSockets
Imports System.Web.WebSockets
Imports System.Threading.Tasks
Public Class Handler1
Implements System.Web.IHttpHandler
Private oSockets As New Hashtable
Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
If context.IsWebSocketRequest Then
If context.Application("Sockets") IsNot Nothing Then
oSockets = context.Application("Sockets")
Dim sUserId As String = context.Request.QueryString("user")
If oSockets.ContainsKey(sUserId) Then
context.Response.StatusCode = 500
context.Response.StatusDescription = "User " & _
sUserId & " already logged in"
context.Response.End()
End If
End If
Try
context.AcceptWebSocketRequest(AddressOf HandleSocketRequest)
Catch ex As Exception
context.Response.StatusCode = 500
context.Response.StatusDescription = ex.Message
context.Response.End()
End Try
End If
End Sub
Async Function HandleSocketRequest_
(context As System.Web.WebSockets.AspNetWebSocketContext) As Task
Dim sUserId As String = context.QueryString("user")
Dim oSocket As System.Net.WebSockets.WebSocket = context.WebSocket
'Register user
oSockets(sUserId) = oSocket
context.Application("Sockets") = oSockets
'Send RefreshUsers Msg to all users
Dim oRefreshMsgBuffer As New ArraySegment(Of Byte)_
(Encoding.UTF8.GetBytes("{{RefreshUsers}}"))
For Each oKey As DictionaryEntry In oSockets
Dim oSocket2 As System.Net.WebSockets.WebSocket = oKey.Value
Await oSocket2.SendAsync(oRefreshMsgBuffer, _
WebSocketMessageType.Text, True, Threading.CancellationToken.None)
Next
Const iMaxBufferSize As Integer = 64 * 1024
Dim buffer = New Byte(iMaxBufferSize - 1) {}
While oSocket.State = WebSocketState.Open 'Loop if Socket is open
'Get Msg
Dim result = Await oSocket.ReceiveAsync(New ArraySegment(Of Byte)(buffer),_
Threading.CancellationToken.None)
Dim oBytes As Byte() = New Byte(result.Count - 1) {}
Array.Copy(buffer, oBytes, result.Count)
Dim oFinalBuffer As List(Of Byte) = New List(Of Byte)()
oFinalBuffer.AddRange(oBytes)
'Get Remaining Msg
While result.EndOfMessage = False
result = Await oSocket.ReceiveAsync_
(New ArraySegment(Of Byte)(buffer), Threading.CancellationToken.None)
oBytes = New Byte(result.Count - 1) {}
Array.Copy(buffer, oBytes, result.Count)
oFinalBuffer.AddRange(oBytes)
End While
If result.MessageType = WebSocketMessageType.Text Then
Dim sMsg As String = Encoding.UTF8.GetString(oFinalBuffer.ToArray())
'Send Msg to all users (including self)
sMsg = sUserId & ": " & sMsg
Dim oMsgBuffer As New ArraySegment(Of Byte)_
(Encoding.UTF8.GetBytes(sMsg))
For Each oKey As DictionaryEntry In oSockets
Dim oDestSocket As System.Net.WebSockets.WebSocket = oKey.Value
Await oDestSocket.SendAsync(oMsgBuffer, _
WebSocketMessageType.Text, True, Threading.CancellationToken.None)
Next
End If
End While
'Send RefreshUsers Msg to all users
For Each oKey As DictionaryEntry In oSockets
Dim oSocket2 As System.Net.WebSockets.WebSocket = oKey.Value
Await oSocket2.SendAsync(oRefreshMsgBuffer, _
WebSocketMessageType.Text, True, Threading.CancellationToken.None)
Next
'Close Socket
Await oSocket.CloseAsync(WebSocketCloseStatus.Empty, "", _
Threading.CancellationToken.None)
'Remove Socket from the List
If oSockets.ContainsKey(sUserId) Then
oSockets.Remove(sUserId)
context.Application("Sockets") = oSockets
End If
End Function
ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Chat.aspx 文件允许您登录并向所有连接的用户广播消息。它是一个 ASP.NET Web 表单,用于获取活动用户列表,并使用 JavaScript 将消息发送到 Handler1.ashx。
<%@ Page Language="VB"%>
<%
Dim sUserList As String = ""
Dim sUserList2 As String = ""
If Application("Sockets") IsNot Nothing Then
For Each oSocket As DictionaryEntry In Application("Sockets")
Dim sUser As String = oSocket.Key
sUserList += "<option value=""" & sUser & """>" & _
sUser & "</option>" & vbCrLf
If sUserList2 <> "" Then sUserList2 += vbCrLf
sUserList2 += sUser
Next
End If
If Request.QueryString("getUsers") = "1" Then
Response.Write(sUserList)
Response.End()
ElseIf Request.QueryString("getUsers2") = "1" Then
Response.Write(sUserList2)
Response.End()
ElseIf Request.QueryString("resetUsers") = "1" Then
If Application("Sockets") IsNot Nothing Then
For Each oEntry As DictionaryEntry In Application("Sockets")
Dim oSocket As Object = oEntry.Value
Try
oSocket.CloseOutputAsync_
(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "", _
System.Threading.CancellationToken.None)
oSocket.CloseAsync_
(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "", _
System.Threading.CancellationToken.None)
Catch ex As Exception
'System.Threading.Thread.Sleep(1000)
End Try
Next
Application("Sockets") = Nothing
End If
Response.Write("Users reseted")
Response.End()
End If
%>
<!DOCTYPE html>
<html>
<head>
<title>Chat App</title>
</head>
<body>
<script>
var websocket = null;
function Open() {
var sProtocol = window.location.protocol == "https:" ? "wss" : "ws";
var uri = sProtocol + '://' + window.location.hostname + _
"/WebSocket/Handler1.ashx?user=" + escape(txtUser.value);
websocket = new WebSocket(uri);
websocket.onopen = function () {
//Connected
btnSend.disabled = false;
btnClose.disabled = false;
btnOpen.disabled = true;
spStatus.style.color = "green";
RefreshUsers();
};
websocket.onclose = function () {
if (document.readyState == "complete") {
//Connection lost
btnSend.disabled = true;
btnClose.disabled = true;
btnOpen.disabled = false;
spStatus.style.color = "red";
selOtherUsers.length = 0;
RefreshUsers();
}
};
websocket.onmessage = function (event) {
if (event.data == "{{RefreshUsers}}") {
RefreshUsers();
return;
}
if (txtOutput.value != "") txtOutput.value += "\n";
txtOutput.value += event.data;
};
websocket.onerror = function (event) {
alert('Could not connect. Please try another name.');
};
setTimeout(function () { RefreshUsers() }, 1000);
}
function Send() {
if (txtMsg.value == "") return;
websocket.send(txtMsg.value);
txtMsg.value = "";
}
function Close() {
websocket.close();
}
function RefreshUsers() {
var oHttp = new XMLHttpRequest();
oHttp.open("POST", "?getUsers=1", false);
oHttp.setRequestHeader("Content-Type", _
"application/x-www-form-urlencoded");
oHttp.onreadystatechange = function () _
{ // Call a function when the state changes.
if (this.readyState === XMLHttpRequest.DONE && _
this.status === 200) {
selOtherUsers.innerHTML = oHttp.responseText;
}
}
oHttp.send();
}
</script>
<div>
<label for="txtUser">User Name</label>
<input id="txtUser" value="Jack" />
<button type="button" onclick="Open()" id="btnOpen">Login</button>
<button type="button" onclick="Close()"
id="btnClose" disabled>Log off</button>
<span id="spStatus" style="color:red">⬤</span>
<div style="float: right">
<label for="selOtherUsers">Users</label>
<button type="button" onclick="RefreshUsers()">↻</button>
</div>
</div>
<div>
<table style="width: 100%; height: 300px">
<tr>
<td style="width: 78%; height: 100%">
<textarea id="txtOutput" rows="1"
style="margin-top: 10px; width: 100%;
height: 100%" placeholder="Output"></textarea>
</td>
<td style="width: 20%; height: 100%; padding-left: 10px">
<select id="selOtherUsers" style="width: 100%;
height: 100%" multiple>
<%=sUserList%>
</select>
</td>
</tr>
</table>
</div>
<div>
<textarea id="txtMsg" rows="5" wrap="soft"
style="width: 98%; margin-left: 3px; margin-top: 6px"
placeholder="Input Text"></textarea>
</div>
<button type="button" onclick="Send()" id="btnSend" disabled>Send</button>
</body>
</html>
作为奖励,我创建了一个 Windows 应用程序,它连接到 Handler1.ashx,并以与 Chat.aspx 几乎相同的方式运行,只是使用 VB.NET 代替 JavaScript。
它打开一个 WebSocket
连接并等待消息到达。
Imports System.ComponentModel
Imports System.Net.WebSockets
Public Class Form1
Dim oSocket As System.Net.WebSockets.ClientWebSocket = Nothing
Private Async Sub btnOpen_Click(sender As Object, e As EventArgs) _
Handles btnOpen.Click
System.Net.ServicePointManager.SecurityProtocol =
System.Net.SecurityProtocolType.Ssl3 Or
System.Net.SecurityProtocolType.Tls12 Or
System.Net.SecurityProtocolType.Tls11 Or
System.Net.SecurityProtocolType.Tls
Dim sServer As String = txtURL.Text 'localhost/WebSocket
If sServer = "" Then
MsgBox("Server URL is missing")
Exit Sub
End If
Dim sProtocol As String = "ws"
If chkSSL.Checked Then
sProtocol = "wss"
End If
Dim sUrl As String = sProtocol & "://" & sServer & _
"/Handler1.ashx?user=" & System.Web.HttpUtility.UrlEncode(txtUser.Text)
oSocket = New System.Net.WebSockets.ClientWebSocket
Try
Await oSocket.ConnectAsync(New Uri(sUrl), Nothing)
Catch ex As Exception
MsgBox("Could not connect. Please try another name. " & _
ex.Message & ", URL: " & sUrl)
Exit Sub
End Try
btnOpen.Enabled = False
btnSend.Enabled = True
btnClose.Enabled = True
lbStatus.ForeColor = Color.FromArgb(0, 255, 0)
Const iMaxBufferSize As Integer = 64 * 1024
Dim buffer = New Byte(iMaxBufferSize - 1) {}
While oSocket.State = WebSocketState.Open
'Get Msg
Dim result = Await oSocket.ReceiveAsync_
(New ArraySegment(Of Byte)(buffer), Threading.CancellationToken.None)
If result.MessageType = WebSocketMessageType.Text Then
Dim oBytes As Byte() = New Byte(result.Count - 1) {}
Array.Copy(buffer, oBytes, result.Count)
Dim oFinalBuffer As List(Of Byte) = New List(Of Byte)()
oFinalBuffer.AddRange(oBytes)
'Get Remaining Msg
While result.EndOfMessage = False
result = Await oSocket.ReceiveAsync_
(New ArraySegment(Of Byte)(buffer), _
Threading.CancellationToken.None)
oBytes = New Byte(result.Count - 1) {}
Array.Copy(buffer, oBytes, result.Count)
oFinalBuffer.AddRange(oBytes)
End While
Dim sMsg As String = System.Text.Encoding.UTF8.GetString_
(oFinalBuffer.ToArray())
If sMsg = "{{RefreshUsers}}" Then
RefreshUsers()
Else
If txtOutput.Text <> "" Then txtOutput.Text += vbCrLf
txtOutput.Text += sMsg
End If
End If
End While
LogOff()
End Sub
Private Async Sub btnSend_Click(sender As Object, e As EventArgs) _
Handles btnSend.Click
If IsNothing(oSocket) OrElse oSocket.State <> WebSocketState.Open Then
Exit Sub
End If
Dim sMsg As String = txtMsg.Text
If sMsg = "" Then
Exit Sub
End If
txtMsg.Text = ""
Await oSocket.SendAsync(New ArraySegment(Of Byte)_
(System.Text.Encoding.UTF8.GetBytes(sMsg)), _
System.Net.WebSockets.WebSocketMessageType.Text, _
True, Nothing)
End Sub
Private Sub btnClose_Click(sender As Object, e As EventArgs) Handles btnClose.Click
LogOff()
End Sub
Private Async Sub LogOff()
btnOpen.Enabled = True
btnSend.Enabled = False
btnClose.Enabled = False
lbStatus.ForeColor = Color.FromArgb(255, 0, 0)
selUsers.Items.Clear()
If IsNothing(oSocket) OrElse oSocket.State <> WebSocketState.Open Then
Exit Sub
End If
Await oSocket.CloseAsync(WebSocketCloseStatus.Empty, "", _
Threading.CancellationToken.None)
End Sub
Private Sub btnRefreshUsers_Click(sender As Object, e As EventArgs) _
Handles btnRefreshUsers.Click
RefreshUsers()
End Sub
Private Sub RefreshUsers()
selUsers.Items.Clear()
Dim sServer As String = txtURL.Text 'localhost/WebSocket
If sServer = "" Then
MsgBox("Server URL is missing")
Exit Sub
End If
Dim sProtocol As String = "http"
If chkSSL.Checked Then
sProtocol = "https"
End If
Dim sUrl As String = sProtocol & "://" & sServer & "/Chat.aspx?getUsers2=1"
Dim sUsers As String = GetData(sUrl)
Dim oUsers As String() = sUsers.Split(vbCrLf)
For i = 0 To oUsers.Length - 1
Dim sUser As String = oUsers(i)
selUsers.Items.Add(sUser)
Next
End Sub
Private Function GetData(ByVal sUrl As String) As String
Dim oHttpWebRequest As System.Net.HttpWebRequest
Dim oHttpWebResponse As System.Net.HttpWebResponse
oHttpWebRequest = CType(System.Net.WebRequest.Create(sUrl), _
System.Net.HttpWebRequest)
oHttpWebRequest.Timeout = 1000 * 60 * 60 'Hour
oHttpWebRequest.KeepAlive = False
oHttpWebRequest.Method = "POST"
oHttpWebRequest.ContentLength = 0
Try
oHttpWebResponse = CType(oHttpWebRequest.GetResponse(), _
System.Net.HttpWebResponse)
Catch ex As Exception
Return ex.Message & vbCrLf & ex.StackTrace()
End Try
'Read Request
Dim oStreamReader As IO.StreamReader = _
New IO.StreamReader(oHttpWebResponse.GetResponseStream, _
System.Text.UTF8Encoding.UTF8)
Dim sHTML As String = oStreamReader.ReadToEnd
oStreamReader.Close()
oHttpWebResponse.Close()
oHttpWebRequest.Abort()
Return sHTML
End Function
Private Sub Form1_Closing(sender As Object, e As CancelEventArgs) _
Handles Me.Closing
LogOff()
End Sub
End Class
关注点
下一步将是添加文件上传、JavaScript 通知、音频和视频功能。:)
历史
- 2022 年 8 月 1 日:创建版本 1