Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

[VB2008/.NET 3.5] Asynchronous TcpListener & TcpClient

Niya

Angel of Code
Joined
Nov 22, 2011
Messages
5,681
I think you'll find that SynchronizationContext.Current will return a null reference if you're not using it in either a WinForms or WPF application.

If you call Current in a non-UI thread it would be null. When a .Net Winforms application starts, the UI thread is created along with a SynchronizationContext for it and the message loop.
 
Joined
May 20, 2005
Messages
104,556
I'm using it in a WinForms application at the moment, so I'm sure that isn't the issue. As for working out why it's throwing that error I'm honestly at a loss. I'm running over it right now and it is showing null. I can connect fine (although I'm seeing 2-3 errors popping up due to the read/write methods, NullReferenceExceptions, IOException, ObjectDisposedException), I can close the server and it'll update all connected client's fine, I can restart the server and reconnect just fine, but when I go to close a client using client.Dispose() the server hangs on that method (OnConnectionClosed now).

If I remove client.Dispose(), the ConnectionClosed event will never be raised, even when the client application is stopped or shut down.
If that field is a null reference then obviously no value was ever assigned to it, so you need to look at where you expected a value to be assigned and determine why it's not.
 

yfzpurgatory

New member
Joined
Sep 9, 2012
Messages
30
If that field is a null reference then obviously no value was ever assigned to it, so you need to look at where you expected a value to be assigned and determine why it's not.

I've been over the code numerous times and I've found no reason for it not to function correctly. Believe I'm just going to move on and hope you decide to rewrite this library at some point in time for C# and/or .NET 4 or 4.5.
 
Joined
May 20, 2005
Messages
104,556
I've been over the code numerous times and I've found no reason for it not to function correctly. Believe I'm just going to move on and hope you decide to rewrite this library at some point in time for C# and/or .NET 4 or 4.5.
That wouldn't help.
 

yfzpurgatory

New member
Joined
Sep 9, 2012
Messages
30
That wouldn't help.

I don't see why it wouldn't when something about C# is causing it not to function correctly. When I use the library in VB.NET 2010 everything works just fine; zero problems that I haven't fixed myself. When I try the same in C# 4 it's a completely different story. For some reason it just isn't jiving well on these lines:

Code:
Protected Overridable Sub OnConnectionAccepted(ByVal e As ConnectionEventArgs)
        Me._synchronisingContext.Post(AddressOf RaiseConnectionAccepted, e)
End Sub

Nothing has been changed, and I know I'm not going crazy. I've ran over the entire library multiple times, and I've very thoroughly went over both the read and send methods line by line to no avail. It has left me completely dumbfounded. The only event that is raised is MessageReceived; both ConnectionClosed and ConnectionAccepted fail, although the client connects.
 
Last edited:

Nightwalker83

PowerPoster
Joined
Dec 26, 2001
Messages
13,346
I don't see why it wouldn't when something about C# is causing it not to function correctly. When I use the library in VB.NET 2010 everything works just fine; zero problems that I haven't fixed myself. When I try the same in C# 4 it's a completely different story. For some reason it just isn't jiving well on these lines:

Please stop talking about "C#" in this thread/section. If have a C# question post it here.
 

yfzpurgatory

New member
Joined
Sep 9, 2012
Messages
30
Please stop talking about "C#" in this thread/section. If have a C# question post it here.

When the problem is 100% relevant to this topic and this library, no, I don't think I will. What I will do, however, is completely wash my hands of this library. An "MVP" my ass.
 

drankof

New member
Joined
Jan 9, 2013
Messages
2
... nothing is being assigned to the _synchronisingContext field, so you need to work out why. ... SynchronizationContext.Current will return a null reference if you're not using it in either a WinForms or WPF application.

FOREWORD: I opened my account on this forum today for two reasons; 1) to continue discussing a legitimate question in a reasonable tone, and more importantly, to say that this code is the most amazing code I have ever gotten off the internet. Seriously. I'm also a new guy to .net, coming from AmigaBasic (joke, vb6) 1 month ago. In my month of scouring the internet looking and learning to make test programs to learn the language, I have BY FAR learned more from this code than from anything else. Multiple programs from one solution, all while sharing and building a library, with all sorts of fancy commenting and region stuff I never knew existed. The library implements great into other programs, too! THANK YOU so much. I'm a big fan.

That said, I'm working in VB2010 (.net 4.0) and am bumping into a similar issue. The server (as far as I can tell) must be run from a form in order for the SynchronizationContext.Current to not return a null value.

Making a Windows Forms Application launched from sub main (to make it behave as similar as possible to module-based code), I put the following in a blank new form's code (it's basically just a stripped down version of the test server):
Code:
Module serverModule
    Public Sub main()
        serverWindow.Show()
        Application.Run()
    End Sub
End Module

Public Class serverWindow

    Public WithEvents server As New MessageServer(12345)
    Public ReadOnly hosts As New List(Of HostInfo)

    Private Sub server_ConnectionAccepted(ByVal sender As Object, ByVal e As Net.ConnectionEventArgs) Handles server.ConnectionAccepted
        hosts.Add(e.Host)
    End Sub

    Private Sub server_MessageReceived(ByVal sender As Object, ByVal e As Net.MessageReceivedEventArgs) Handles server.MessageReceived
        server.Send(e.Message)
        If e.Message = "exit" Then Me.Close()
    End Sub

    Private Sub server_ConnectionClosed(ByVal sender As Object, ByVal e As Net.ConnectionEventArgs) Handles server.ConnectionClosed
        Dim host = e.Host
        hosts.Remove(host)
    End Sub

    Private Sub MainWindow_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        server.Dispose()
        Application.Exit()
    End Sub
End Class

Clients can connect and talk, the server repeats the chat, and clients can even shut down the server with "exit". The server only has a single blank window running, which I guess could be hidden, but I what I originally wanted was to run the server from a module.

A nearly identical module-based version:
Code:
Module serverModule

    Public Sub main()
        Application.Run()
    End Sub

    Public WithEvents server As New MessageServer(12345)
    Public ReadOnly hosts As New List(Of HostInfo)

    Private Sub server_ConnectionAccepted(ByVal sender As Object, ByVal e As Net.ConnectionEventArgs) Handles server.ConnectionAccepted
        hosts.Add(e.Host)
    End Sub

    Private Sub server_MessageReceived(ByVal sender As Object, ByVal e As Net.MessageReceivedEventArgs) Handles server.MessageReceived
        server.Send(e.Message)
        If e.Message = "exit" Then shutdown()
    End Sub

    Private Sub server_ConnectionClosed(ByVal sender As Object, ByVal e As Net.ConnectionEventArgs) Handles server.ConnectionClosed
        Dim host = e.Host
        hosts.Remove(host)
    End Sub

    Private Sub shutdown()
        server.Dispose()
        Application.Exit()
    End Sub

End Module

Whenever a client joins, the same line "Me._synchronisingContext.Post(AddressOf RaiseConnectionAccepted, e)" says object reference not set to an instance of an object.

The same error occurs when I try to reproduce the same thing by running the server from a class (without a form). Yet the client seems to run great from a module.

So I guess the question is, would it, and if so, how would it be possible to run a server using your current library without it running from a form?
 
Joined
May 20, 2005
Messages
104,556
@drankof, I'm glad that you found the code useful. As I said, SynchronizationContext.Current returns Nothing when used in other than a Windows Forms or WPF app. That's because the whole point of the SynchronizationContext class is to allow you to execute code on the thread that owns a specific control. If you have no controls then it doesn't matter what thread you execute code on. If you don't want to use my library in a GUI app then you can get rid of all the stuff relating to the SynchronizationContext. If you want to be able to use it in both GUI and non-GUI apps then you'll need to do something like this:
Code:
If mySynchronizationContext Is Nothing Then
    'Non-GUI app so call your method directly on the current thread.
Else
    'GUI app so invoke your method via the SynchronizationContext.
Else
In non-GUI apps, some additional thread synchronisation may be required to ensure that actions occur in the appropriate sequence but that is beyond the scope of this thread.
 

drankof

New member
Joined
Jan 9, 2013
Messages
2
As I said, SynchronizationContext.Current returns Nothing when used in other than a Windows Forms or WPF app. That's because the whole point of the SynchronizationContext class is to allow you to execute code on the thread that owns a specific control. If you have no controls then it doesn't matter what thread you execute code on. If you don't want to use my library in a GUI app then you can get rid of all the stuff relating to the SynchronizationContext.
...
In non-GUI apps, some additional thread synchronisation may be required to ensure that actions occur in the appropriate sequence but that is beyond the scope of this thread.

Wow...it works by executing code on a thread that owns a control, which would need a form, that makes perfect sense. I think that just explained about 1/2 of what I didn't/don't understand regarding how the asynchronization/multithreading(/not pausing just to passively listen for people joining...not sure what word is appropriate) works on this server. Now I just need to finish investigating some of the other intricacies of threading, in general. Thank you so much and for the tip on how to implement it without a form/GUI. :thumb:
 

vagkom

New member
Joined
Jan 24, 2013
Messages
1
Great Work, lean programming!
I develop listeners quite a while for a GPRS project. Let me put a question on the table:
When a GPRS device connects to the listener it takes an IP address and a random port. Then the device looses the connection and later connects to listener with the same address and same port. What is happening ? So far the program crashes. In a previous listener I kept a list of ip addresses and manually I perform a check (if exists) before connection. Do you think any other way to prevent a "same port" situation ?

Best Regards
Vangelis
 

dbasnett

Powered By Medtronic
Joined
Dec 20, 2007
Messages
9,265
@jmcilhinney - I looked at your post in the code bank. I was wondering why you chose the ports you did? It seems that you should be using port numbers 49152–65535.
 

i00

PowerPoster
Joined
Mar 1, 2002
Messages
2,347
Hi, I said a long time ago that it wasn't receiving packets in order... here is the case...

This is my modified MessageClient.Read:

[highlight=vb] Private Sub Read(ByVal ar As IAsyncResult)
Try
'The stream will be Nothing if the client has been disposed.
If Me.stream IsNot Nothing Then
Dim buffer = DirectCast(ar.AsyncState, Byte())

'Complete the asynchronous read and get the first block of data.
Dim byteCount = Me.stream.EndRead(ar)

If byteCount = 0 Then
'If there is no data when an asynchronous read completes it is because the server closed the connection.
Me.OnConnectionClosed(New ConnectionEventArgs(Me.server))
Else
'Start building the message.
'Dim message As New StringBuilder(Me.Encoding.GetString(buffer, 0, byteCount))

OnDataChunkRecieved(New DataChunkRecievedEventArgs() With {.Chunk = buffer})

'As long as there is more data...
While Me.stream.DataAvailable
'...read another block of data.
byteCount = Me.stream.Read(buffer, 0, Me.BufferSize)
OnDataChunkRecieved(New DataChunkRecievedEventArgs() With {.Chunk = buffer})
End While

'Listen asynchronously for another incoming message.
Me.stream.BeginRead(buffer, 0, Me.BufferSize, AddressOf Read, buffer)

'Notify any listeners that a message was received.
'Me.OnMessageReceived(New MessageReceivedEventArgs(Me.server, message.ToString()))
End If
End If
Catch ex As IOException
'The callback specified when BeginRead was called may get invoked one last time when the TcpClient is disposed.
'This exception is thrown when EndRead is called on a disposed client stream.
End Try
End Sub[/highlight]

I will try to describe this the best way i can ... but basically ... when there is no more data available in the Me.stream.DataAvailable loop ... because we have reached the end of the stream that the client has received sofar (but it is still sending such as a large file).. it will then come back to this sub again as the server is still sending ... and Dim buffer = DirectCast(ar.AsyncState, Byte()) then starts reading data from a random chunk... not necessarily the next chunk that was sent...

also ... if i change the code to this it works:

Code:
[FONT=Courier New][COLOR="#A9A9A9"]                    While Me.stream.DataAvailable
                        '...read another block of data.
                        byteCount = Me.stream.Read(buffer, 0, Me.BufferSize)
                        OnDataChunkRecieved(New DataChunkRecievedEventArgs() With {.Chunk = buffer})
                        [COLOR="#B22222"][B]'wait for a second to see if more data is coming...
                        System.Threading.Thread.Sleep(100)[/B][/COLOR]
                    End While[/COLOR][/FONT]

As this seems to make it slow down enough to receive the next chunk ... but this is not good of course as it slows it down ... and the data may take more than 100ms to get the next bit... it is just proof of concept...

Any ideas jmcilhinney?

Thanks,
Kris
 
Last edited:

Niya

Angel of Code
Joined
Nov 22, 2011
Messages
5,681
@i00

Your problem is the overall design. You should never depend on the socket implementation to tell you when a download is complete. Always design at least a minimum protocol. My favored method is attaching a prefix that at the start of a stream that tells me how many bytes to expect so I keep track of the number of bytes at the receiving end that we have received at any point. Using this method, one can determine when a download is complete.
 

i00

PowerPoster
Joined
Mar 1, 2002
Messages
2,347
@i00

Your problem is the overall design. You should never depend on the socket implementation to tell you when a download is complete. Always design at least a minimum protocol. My favored method is attaching a prefix that at the start of a stream that tells me how many bytes to expect so I keep track of the number of bytes at the receiving end that we have received at any point. Using this method, one can determine when a download is complete.

OK ... But if I do this ... I still have a problem ... how do I wait for the rest of the data? because Me.stream.DataAvailable will return false but it will still be transmitting...

Edit: also this download also relies on the data not being long enough to be split into multiple packets

Kris
 
Last edited:

Niya

Angel of Code
Joined
Nov 22, 2011
Messages
5,681
I'm currently putting together two small applications to demonstrate a good way to handle file transfers over TCP/IP using Winsock through the TcpListener/TcpClient classes. I'll post it in the code bank when I'm done. Hopefully it would make things a little clearer.
 

Niya

Angel of Code
Joined
Nov 22, 2011
Messages
5,681
Ok, I've posted two sample applications that show how to transfer a file between a client and a server using WinSock. They should give you an idea about to detect completion of data transfers. Here is the Code Bank thread.
 
Top