我们要先想好客户端和服务器端有什么资料要通讯的,定好通讯的格式。我是这样设计的:通讯的资料我就不加密了,格式是01u=aa&xxxx 。前两位是代表了是做什么。u=aa是用户aa 做的操作。xxxx是操作的内容。
00 由服务器提供给客户端(简称S2C,下同),返回当前在线的所有用户。如:00u=a&x=0&y=0&u=b&x=54&y=78
01 C2S 用户登陆 如:01u=a
S2C 告诉其他在线用户有新用户登陆 如:01u=a
02 C2S 用户移动 如:02u=a&x=100&y=50
S2C 告诉其他在线用户某个用户在移动 如:02u=a&x=100&y=50
03 C2S 用户下线 如:03u=a
S2C 告诉其他在线用户某个用户下线了 如:03u=a
04 C2S 用户发言 如:04u=a&内容
S2C 告诉其他在线用户某个用户的发言 如:04u=a&内容
这里要说一下用户下线,用户下线是由用户在客户端点击“下线”完成,也可以是点击关闭FLASH的窗口完成。如果是点“下线”,那就由FLASH发送03过来就可以了。如果是点击关闭窗口的话,那只能由服务器端判断。当收到客户端发送的数据长度为零时,就可以判断客户端下线了。opMsg函数如下:
’处理收到的数据
Public Sub opMsg(ByVal so As StateObject)
Dim username As String
Dim sendmsg As String
Dim keys As IDictionaryEnumerator
’如果发送消息长度为0,断开客户端
If so.len < 1 Then
keys = Me.allRoles.Values().GetEnumerator
While keys.MoveNext
If keys.Current.Equals(so.workSocket) Then
username = CType(keys.Key, String)
End If
End While
so.workSocket.Close()
sendmsg = "03u=" + username + Chr(0)
keys.Reset()
While keys.MoveNext
If Not keys.Current.Equals(so.workSocket) Then
Me.send(keys.Key, sendmsg)
End If
End While
Me.allRoles.Remove(username)
Me.rolesPosition.Remove(username)
Exit Sub
End If
Dim msg As String = System.Text.Encoding.UTF8.GetString(so.buffer, 0, so.len - 1)
Dim msgtype As String = Left(msg, 2)
Select Case msgtype
Case "01" ’登陆
Dim newMsg As String = "00"
Dim isIn As Boolean = False
username = Right(msg, msg.Length - 4)
keys = Me.allRoles.Keys().GetEnumerator
While keys.MoveNext
If keys.Key <> username Then
If newMsg <> "00" Then
newMsg += "&"
End If
newMsg += "u=" + keys.Key + Me.rolesPosition.Item(keys.Key)
Else
isIn = True
End If
End While
newMsg += Chr(0) ’考虑到FLASH里接收SOCKET是用XMLSocket,每个 XML 消息都是一个完整的 XML 文档,以一个零 (0) 字节结束。所以以chr(0)结束。
If Not (isIn) Then
Me.allRoles.Add(username, so.workSocket)
Me.rolesPosition.Add(username, "&x=275&y=200") ’这里我给角色初位置是(275,200),正好是在FLASH场景的中心。
sendmsg = msgtype + "u=" + username + "&x=275&y=200" + Chr(0)
keys = Me.allRoles.Keys().GetEnumerator
While keys.MoveNext
If keys.Key = username Then
Me.send(keys.Current, newMsg)
Else
Me.send(keys.Current, sendmsg)
End If
End While
End If
Case "02" ’移动
username = Right(msg.Split("&")(0), msg.Split("&")(0).Length - 4)
If CType(Me.allRoles.Item(username), Socket).Equals(so.workSocket) Then
Dim xy As String = Right(msg, msg.Length - msg.Split("&")(0).Length)
Me.rolesPosition.Item(username) = xy
keys = Me.allRoles.Keys().GetEnumerator
While keys.MoveNext
If keys.Key <> username Then
Me.send(keys.Current, msg + Chr(0))
End If
End While
End If
Case "03" ’下线
username = Right(msg, msg.Length - 4)
If CType(Me.allRoles.Item(username), Socket).Equals(so.workSocket) Then
keys = Me.allRoles.Values().GetEnumerator
While keys.MoveNext
If Not keys.Current.Equals(so.workSocket) Then
Me.send(keys.Key, msg + Chr(0))
End If
End While
Me.allRoles.Remove(username)
Me.rolesPosition.Remove(username)
so.workSocket.Close()
End If
Case "04" ’聊天
username = Right(msg.Split("&")(0), msg.Split("&")(0).Length - 4)
If CType(Me.allRoles.Item(username), Socket).Equals(so.workSocket) Then
keys = Me.allRoles.Keys().GetEnumerator
While keys.MoveNext
Me.send(keys.Current, msg + Chr(0))
End While
End If
End Select
End Sub
要注意的是,发送的消息要以chr(0)结尾,这样FLASH才知道这是消息的尾,否则FLASH会以为还没完,还在等后边不存在的数据传过来。因为我懒,所以不打算在这个教程里做与数据库相关联的例子,如果要加的话,就在这个函数里的case里加就可以了。正确的做法应该是把每个case里的代码整理成一个个的功能块,方便以后调用。我懒-_-
还要写一个从服务器发消息给客户端的函数:
’向一个用户主动发信息
Public Sub send(ByVal user As Object, ByVal msg As String)
Dim sends As Socket
sends = CType(allRoles.Item(user), Socket)
Dim buffer() As Byte
buffer = System.Text.Encoding.UTF8.GetBytes(msg)
sends.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, AddressOf Me.Send_Callback, sends)
End Sub
Public Sub Send_Callback(ByVal result As IAsyncResult)
CType(result.AsyncState, Socket).EndSend(result)
End Sub
做好这个DLL文件了,做一个面板吧。其时这是在开始写server.vb的时候就应该做的事,刚才给忘了。现在加上。在右边的“解决方案资源管理器”里右击“解决方案”,选择“添加(D)”“添加新项目(N)...”“visual Basic 项目”“Windows 应用程序”,命名为rpg。如图5[5.gif]。在“解决方案资源管理器”里右击“rpg”,选择“项目依赖项”,在弹出窗口里勾选server(刚才做的那个文件的名字)。在“rpg”下一行的“引用”上右击,选择“添加引用”,“浏览”找到用server.vb(server文件夹下有个bin文件夹,如果里边没有,你就先按F5调试生成它)生成的server.dll文件,选中之。在rpg上右击,再选择“设为启动项目(A)”。在rpg的面板上添加一个按钮,加上以下程序:
Private isStarted As Boolean
Private rpgServer As server.ibaiy.Server
Private Sub ServerForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.isStarted = False
End Sub
Private Sub Button_start_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button_start.Click
If Not Me.isStarted Then
Me.rpgServer = New server.ibaiy.Server
Me.rpgServer.startServer(9999)
Me.isStarted = True
End If
End Sub
现在可以调试一下VB的程序了。我做了一个简单的FLASH来测试的。这个FLASH就不说明怎么做了。我在下边有提供。[socket.swf],有兴趣的朋友可以自己看看。使用方法:服务器启动后,点击socket.swf上边的connect按钮,成功或者失败都会在中间显示出来。如果成功,就可以在下边输入要传输入的文字,格式就是上边我们定义好的“01u=a”之类的,当收到服务器传过来的文字也会在中间显示出来。可以开几个看看效果。
图片附件: 5.gif

附件(swf):
socket.rar