基于playit.gg的VB6 Winsock多人游戏服务器联机问题咨询
项目背景
我是联机开发方向的新手,此前使用过GameRanger联机平台,希望开发一款更轻量化的联机工具。
目前我基于Winsock控件编写了支持多连接的VB6服务端与客户端程序,客户端通过playit.gg提供的公网IP:端口已可正常跨互联网连接到我本地运行的服务端,服务端地址配置如下:
.RemoteHost = "write-mechanical.at.playit.gg" .RemotePort = "55224"
现有核心代码
聊天客户端连接核心代码
Option Explicit Private Sub cmdConnect_Click() 'Check input. txtServer.Text = Trim$(txtServer.Text) txtPort.Text = Trim$(txtPort.Text) txtNickname.Text = Trim$(txtNickname.Text) If Len(txtServer.Text) = 0 Or Len(txtPort.Text) = 0 Or _ Len(txtNickname.Text) = 0 Then MsgBox "Please fill in all fields!", vbCritical Exit Sub ElseIf Not IsNumeric(txtPort.Text) Then MsgBox "Invalid port value!", vbCritical Exit Sub End If 'Done with that... strMyNickname = txtNickname.Text With frmChat.sckClient .Close bolRecon = False .RemoteHost = txtServer.Text .RemotePort = txtPort.Text .Connect End With Me.Hide frmChat.Show AddStatusMessage frmChat.rtbChat, RGB(128, 128, 128), "> Connecting to " & txtServer.Text & ":" & txtPort.Text & "..." End Sub Private Sub txtPort_KeyPress(KeyAscii As Integer) 'Number only. If Not IsNumeric(Chr$(KeyAscii)) And Not KeyAscii = 8 Then KeyAscii = 0 End Sub
服务端连接接受核心代码
With frmChat .sckServer(0).Close .sckServer(0).LocalPort = CInt(txtPort.Text) .sckServer(0).Listen 'Opens the winsock control. End With Dim strNotify Dim strPacket As String Option Explicit 'JUST USED FOR FORM RESIZING. Private Type RECT rctLeft As Long rctTop As Long rctRight As Long rctBottom As Long End Type 'JUST USED FOR FORM RESIZING. Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long 'JUST USED FOR FORM RESIZING. Private udtMyRect As RECT Private Sub sckServer_Close(Index As Integer) sckServer(Index).Close Dim strPacket As String strPacket = "LEA" & Chr$(2) & udtUsers(Index).strNickname & Chr$(4) SendGlobalData strPacket AddUserEntersLeaves rtbChat, udtUsers(Index).strNickname, False RemoveListItem lstUsers, udtUsers(Index).strNickname With udtUsers(Index) .strBuffer = vbNullString .strIP = vbNullString .strNickname = vbNullString End With End Sub 'A client is attempting to connect. '---------------------------------- 'Another computer is trying to connect to the server. 'Find a socket we can use to handle the connection. 'Then load a slot in the udtUsers() array for this user. Private Sub sckServer_ConnectionRequest(Index As Integer, ByVal requestID As Long) Dim intNext As Integer intNext = ModChat.NextOpenSocket If intNext > 0 Then 'Found a socket to use; accept connection. sckServer(intNext).Accept requestID 'Check if there is a slot open for this connection 'in the users array. If UBUsers < intNext Then 'There isn't, load one. ReDim Preserve udtUsers(intNext) As CHAT_USER End If '(Re)set this client's info. With udtUsers(intNext) .strIP = sckServer(intNext).RemoteHostIP .strNickname = vbNullString End With 'We haven't received the user's nickname yet. 'That will happen in the DataArrival event :) 'Once it does, we will need to let everyone know that this person joined the room. AddStatusMessage rtbChat, RGB(0, 0, 128), "> " & sckServer(intNext).RemoteHostIP & " connected!" End If End Sub Private Sub sckServer_DataArrival(Index As Integer, ByVal bytesTotal As Long) Dim strData As String, strPackets() As String Dim strTrunc As String, bolTrunc As Boolean Dim lonLoop As Long, lonTruncStart As Long Dim lonUB As Long 'Get the received data. sckServer(Index).GetData strData, vbString, bytesTotal With udtUsers(Index) 'Append it to the buffer. .strBuffer = .strBuffer & strData strData = vbNullString 'Check if the last byte is the packet delimiter (Chr$(4)). 'If it is, there are no truncated packets. 'If it isn't, the last packet got split up. >:( If Right$(.strBuffer, 1) <> Chr$(4) Then 'Get all data to the right of the last Chr$(4) which is the truncated packet. bolTrunc = True 'Find position of last packet delimiter. lonTruncStart = InStrRev(.strBuffer, Chr$(4)) 'Check if it was found. If lonTruncStart > 0 Then 'Extract out the truncated part. strTrunc = Mid$(.strBuffer, lonTruncStart + 1) End If End If 'We checked if the data was truncated. 'If it was, we put that part away for now and set the Truncated flag to TRUE (bolTrunc). 'Split up the data buffer into individual packets 'in case we received more than 1 at a time. 'Process them individually. If InStr(1, .strBuffer, Chr$(4)) > 0 Then strPackets() = Split(.strBuffer, Chr$(4)) 'Now all of the individual packets are in strPackets(). 'Loop through all of them. lonUB = UBound(strPackets) 'Get number of packets. 'If the data is truncated, don't process the last one 'because it isn't complete. If bolTrunc Then lonUB = lonUB - 1 'Start looping through all packets. For lonLoop = 0 To lonUB 'Check length of packet. 'Each packet has a command/header, 'the packet must be at least that length. 'In this example, all headers are 3 bytes/characters long. If Len(strPackets(lonLoop)) > 3 Then 'Look at the header and process the packet accordingly. Select Case Left$(strPackets(lonLoop), 3) 'Packet is a chat message. Case "MSG" 'Process message. ParseChatMessage Index, strPackets(lonLoop) 'User is connecting (sending nickname). Case "CON" 'Process connection. ParseConnection Index, strPackets(lonLoop) 'Add your own here! :) 'Case "XXX" 'Do something. 'Case "YYY" 'Do something. End Select End If Next lonLoop End If 'We're done processing all packets. Erase strPackets 'Now we can erase all the data we just processed from the buffer. 'Otherwise, it will just keep growing in size and the same data 'will be processed over and over (which might actually be kinda cool?). .strBuffer = vbNullString If bolTrunc Then 'Still have a piece of a packet left over because the data was truncated. 'Erase the buffer then put just the truncated part back in. .strBuffer = strTrunc End If strTrunc = vbNullString End With End Sub
问题说明
我在本地计算机运行上述服务端,其他异地用户通过playit.gg分配的公网地址与端口可正常连接到我的服务端程序,在所有联机用户均安装相同版本目标游戏的前提下,游戏内的多人联机界面是否可以自动识别到其他已连接用户的IP、实现联机大厅玩家展示功能,是否还需要进行额外的配置或功能开发?
解答
不可能自动识别,必须做额外开发,没有捷径。
- 你现在写的只是独立的聊天服务端/客户端,和游戏本身没有任何数据交互。绝大多数支持局域网联机的老游戏,是靠局域网广播包发现房间、直连其他玩家IP建立连接的,你这套Winsock程序和游戏的联机模块完全是两套独立运行的东西,游戏根本读不到你服务端存储的玩家IP列表,自然不可能自动在原生联机界面显示其他玩家。
- 你现在服务端拿到的用户IP是playit.gg的隧道入口IP,不是玩家真实的公网IP,就算你把这个IP发给其他玩家,对方也没法直接用这个IP连到对应玩家的游戏端口——所有玩家的流量都是先到playit.gg隧道再转到你本地服务端的,玩家之间默认没有直连通道。
- 要实现你要的联机大厅功能,至少要补三块核心开发:
- 实现游戏流量的转发逻辑。要么让你的服务端做中转节点,把所有玩家的游戏数据包在服务端转发给对应目标玩家;要么实现UDP打洞,给玩家之间建立直连P2P通道,这部分必须对应你要适配的游戏的联机端口、通信协议来做适配。
- 补全大厅信息同步逻辑。玩家连接到你的服务端后,服务端要维护完整的在线玩家列表、房间列表,主动把列表状态推送给所有在线客户端,你需要自己做独立的大厅界面展示这些信息——别指望游戏原生界面能直接读取你程序的数据,除非你逆向修改游戏的联机发现逻辑,开发成本极高,远不如自己实现大厅界面划算。
- 做游戏启动拉起逻辑。玩家在你的大厅点击加入房间时,你的客户端要自动拉起对应游戏,把要连接的隧道IP/端口、房间信息通过启动参数或者内存写入的方式传给游戏,代替玩家手动输入IP连接房间的操作。
- 额外配置注意:如果继续用playit.gg做隧道,你需要给每个要转发的游戏端口单独配置隧道映射,不然游戏的联机流量根本走不通隧道。如果做P2P直连就不需要依赖隧道,但打洞成功率受玩家所在网络环境影响,一般要预留服务端中转变道做兜底。
内容的提问来源于stack exchange,提问作者hans
相关产品推荐
相关产品推荐

