[.net] [VB.NET] Asychronous Multi-Client TCP Server [solved]

Started by
1 comment, last by paulcardo 16 years ago
//Edit fixed don't worry about it: //I figured it out. It wasn't actually too much a problem of my code. It was //just how flash handled security files. It closed the socket afterwards on me I made a multi-client TCP server a while ago, but it is riddled with bugs and is all together unstable due to my ignorance. I recently started working again on the project, but I want to do it right this time. This is for a small multiplayer game server and all I need is a tutorial or something that shows how to correctly create and manage asynchronous sockets. Along with that I need the tutorial to have error checking code. (needless to say my old server just had random Try Catch statements everywhere hoping to catch an error). Basically something that show 100% how I'm so supposed to setup the listening socket and have a call back to a connection request function that connects that client to another socket. I've been googling hoping I didn't have to make a post, but I haven't found anything that show 100% how it's supposed to be setup. Did I miss something in the MSDN documentation about asynchronous multi-client TCP servers? Any help with this would be greatly appreciated. [Edited by - Sirisian on July 20, 2007 1:57:43 AM]
Advertisement
Update: I code this with the help of some msdn information:

Imports SystemImports System.NetImports System.Net.SocketsImports System.TextImports System.ThreadingImports Microsoft.VisualBasic' State object for reading client data asynchronouslyPublic Class StateObject    ' Client  socket.    Public workSocket As Socket = Nothing    ' Size of receive buffer.    Public Const BufferSize As Integer = 1024    ' Receive buffer.    Public buffer(BufferSize) As Byte    'To-Do: Make a reference to some playerdataEnd Class 'StateObjectPublic Class AsynchronousSocketListener    ' Thread signal.    Public Shared allDone As New ManualResetEvent(False)    'Mutex    Private Shared mut As New Mutex()    'Clients    Public Shared clients As New List(Of StateObject)    'Log    Public Shared log As TextBox    Public mainThread As Thread    Delegate Sub WriteToLogDelegate(ByVal entry As String)    Public Sub New(ByRef log_ As TextBox)        log = log_        log.Text = "Asynchronous server socket initialized"        mainThread = New Thread(AddressOf Main)        mainThread.Start()    End Sub    'Delegate method to write to the data log    Public Shared Sub WriteToLogMethod(ByVal entry As String)        'log.Text.Insert(log.Text.Length - 2, Chr(13) & entry)        log.Text = log.Text & vbNewLine & entry    End Sub    'Simplified Subroutine to write to the data log    Public Shared Sub WriteToLog(ByVal entry As String)        Dim logDelegate As New WriteToLogDelegate(AddressOf WriteToLogMethod)        If log.InvokeRequired() Then            log.Invoke(logDelegate, entry)        Else            logDelegate(entry)        End If    End Sub    ' This server waits for a connection and then uses  asychronous operations to    ' accept the connection, get data from the connected client    Public Shared Sub Main()        'mut.WaitOne()        'log.Text = "Started"        'mut.ReleaseMutex()        ' Data buffer for incoming data.        Dim bytes() As Byte = New [Byte](1023) {}        ' Establish the local endpoint for the socket.        Dim ipHostInfo As IPHostEntry = Dns.GetHostEntry(Dns.GetHostName())        Dim ipAddress As IPAddress = ipHostInfo.AddressList(0)        Dim localEndPoint As New IPEndPoint(ipAddress, 30303)        ' Create a TCP/IP socket.        Dim listener As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)        ' Bind the socket to the local endpoint and listen for incoming connections.        listener.Bind(localEndPoint)        listener.Listen(100)        While True            ' Set the event to nonsignaled state.            allDone.Reset()            ' Start an asynchronous socket to listen for connections.            WriteToLog("Waiting for a connection...")            listener.BeginAccept(New AsyncCallback(AddressOf AcceptCallback), listener)            ' Wait until a connection is made and processed before continuing.            allDone.WaitOne()        End While    End Sub 'Main    Public Shared Sub AcceptCallback(ByVal ar As IAsyncResult)        ' Get the socket that handles the client request.        Dim listener As Socket = CType(ar.AsyncState, Socket)        ' End the operation.        Dim handler As Socket = listener.EndAccept(ar)        ' Create the state object for the async receive.        Dim state As New StateObject        state.workSocket = handler        state.workSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)        'Add the client socket to a list for easy reference: Note: Not sure if the mutex is necessary        mut.WaitOne()        clients.Add(state)        mut.ReleaseMutex()        WriteToLog("Connection")        'I HAVE NO IDEA WHERE TO PUT THIS allDone.Set()        'Signal the main thread to continue.        allDone.Set()    End Sub 'AcceptCallback    Public Shared Sub ReceiveCallback(ByVal ar As IAsyncResult)        Dim content As String = String.Empty        ' Retrieve the state object and the handler socket        ' from the asynchronous state object.        Dim state As StateObject = CType(ar.AsyncState, StateObject)        Dim handler As Socket = state.workSocket        ' Read data from the client socket.         If (handler.Connected()) Then            Dim bytesRead As Integer = handler.EndReceive(ar)            If bytesRead > 0 Then                ' There  might be more data, so store the data received so far.                content = Encoding.ASCII.GetString(state.buffer, 0, bytesRead)                'Send the Security Policy Data If Flash 9 Requests it                Dim policyFileNeeded As Boolean = False                If content.Length >= 22 Then                    If content.Substring(0, 22) = "<policy-file-request/>" Then                        WriteToLog("Policy File Sent")                        '<?xml version=" & Chr(34) & "1.0" & Chr(34) & "?><!DOCTYPE cross-domain-policy SYSTEM " & Chr(34) & "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd" & Chr(34) & ">                        Send(handler, "<cross-domain-policy><allow-access-from domain=" & Chr(34) & "*" & Chr(34) & "to-ports=" & Chr(34) & "30303" & Chr(34) & "/></cross-domain-policy>")                        policyFileNeeded = True                    End If                End If                If Not policyFileNeeded Then                    WriteToLog("Data Received: " & content)                    Send(handler, "Data Received")                End If            End If            'Begin waiting to receive on the socket            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)        End If    End Sub 'ReceiveCallback        Public Shared Sub Send(ByVal handler As Socket, ByVal data As String)        ' Convert the string data to byte data using ASCII encoding.        Dim byteData As Byte() = Encoding.ASCII.GetBytes(Data)        ' Begin sending the data to the remote device.        handler.BeginSend(byteData, 0, byteData.Length, 0, New AsyncCallback(AddressOf SendCallback), handler)    End Sub 'Send    Private Shared Sub SendCallback(ByVal ar As IAsyncResult)        ' Retrieve the socket from the state object.        Dim handler As Socket = CType(ar.AsyncState, Socket)        ' Complete sending the data to the remote device.        Dim bytesSent As Integer = handler.EndSend(ar)        Console.WriteLine("Sent {0} bytes to client.", bytesSent)        WriteToLog("Data Sent Bytes: " & bytesSent)        'handler.Shutdown(SocketShutdown.Both)        'handler.Close()        'I HAVE NO IDEA WHERE TO PUT THIS allDone.Set()        'Signal the main thread to continue.        'allDone.Set()    End Sub 'SendCallback    Public Sub PingAllClients()        'Not sure if the mutex is necessary        mut.WaitOne()        For i As UInteger = 0 To clients.Count - 1            Send(clients(i).workSocket, "ping")        Next        mut.ReleaseMutex()    End Sub 'PingAllClientsEnd Class 'AsynchronousSocketListener


Then on my form I just have
Public Class Main    Public SocketServer As AsynchronousSocketListener    Private Sub Main_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load        SocketServer = New AsynchronousSocketListener(txtLog)    End Sub    Private Sub btnPingAllClients_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPingAllClients.Click        SocketServer.PingAllClients()    End SubEnd Class

So I just have button and text box on the form for logging/pinging tests

I can't seem to wrap my mind around what Mutex and the ManualResetEvent objects are. I keep reading the MSDN stuff but it's not clicking I guess...

Okay what's really messing with me is that it would seem I am not doing something with my sockets correctly. Mind you that this is for multiple clients. Is my AcceptCallback function correct? Something tells me it isn't. Either that or something is blocking. If I uncomment the lines:

'handler.Shutdown(SocketShutdown.Both)
'handler.Close()

For some reason the client says it connected. Then it disconnects as expected. I can't figure out why the client won't connect normally.

Again any information on the subject would be very helpful. This is seemingly very difficult for me to understand.
For Flash Player 9.0.115 and superior:

Flash Player expect a policy file null terminated, in VB it is Chr(0)
then the code line with Send( policy file ) is:

Send(handler, "<cross-domain-policy><allow-access-from domain=" & Chr(34) & "*" & Chr(34) & "to-ports=" & Chr(34) & "30303" & Chr(34) & "/></cross-domain-policy>" & Chr(0) )


regards all

This topic is closed to new replies.

Advertisement