Someone asked recently if there were any existing components out there that would allow them to send text messages between a client and server using the TcpClient without locking up the UI and without their having to learn the intricacies of the TcpClient class. I don't know for sure but I'm guessing that the answer is "no", so I decided to create one.
I've attached the project below, so you're welcome to use the source code as a learning tool or as a basis for your own code. If you do use the code, in part or in full, I don't require any recognition but I do require that you don't claim the code as your own creation. You can also just compile the library and slot it straight into your own project as a black box if you don't care about how it works.
FEATURES:
* Uses TcpListener and TcpClient to provide communication between server and multiple clients.
* All connection, read and write operations performed asynchronously to avoid UI issues.
* Simple interface for send text messages, i.e. just call Send and specify the message.
* Server can send a message to a single client or all clients with a single method call in each case.
* Uses event model to provide notifications regarding connections and messages.
* Events can be raised in background threads or the UI thread simply by setting a property.
* Solution includes WinForms projects for testing server and multiple clients.
* Code demonstrates various techniques including inheritance, custom events, asynchronous programming and LINQ.
USAGE:
To test out the features you can simply open the project in VS and run it. The test projects for the client and server will both run and present you with two windows: one for the server and one for the client. Click the Connect button in the client window to connect to the server.
Both server and client provide a log window to notify you of all connection and communication events. To send a message from the client to the server simply type into the TextBox at the bottom of the window and click the Send button. The same goes for the server sending a message to the client.
The client test rig is an MDI application. Each child window represents a client and multiple clients can connect to the server simultaneously. Use the File menu to create a new client.
When multiple clients are connected to the server you can select the client to send a message to using the ComboBox at the bottom of the server window. Clicking the Send All button will send the message to all connected clients.
To use the Wunnell.Net.MessageClientServer.dll assembly in your own applications you'll first want to open the References page of your project properties, add a reference to the assembly and then import the Wunnell.Net namespace. You can add instances of the client and server classes in code like so:
vb.net Code:
Private WithEvents client As New MessageClient(hostName, remotePort)
Private WithEvents server As New MessageServer(port)
In the case of the client, the host name and remote port are the name or address and port number of the server. In the case of the server, the port is the port number to listen on. The server's port and the client's remote port must be the same.
Note that by using WithEvents in the declarations you can use the drop-down lists at the top of the code window to generate event handlers with Handles clauses. You can use AddHandler if you prefer.
Note that you don't necessarily have to handle any events if you don't want to, but if you want to provide feedback on what's happening you will obviously need to. Note also that, if you using the library in a WinForms app and you want events raised in the UI thread then you should assign your form to the SynchronisingObject property of the client or server. All operations will still be performed on background threads. It's only notifications that will occur on the UI thread.
vb.net Code:
Me.client.SynchronisingObject = Me
To be able to communicate you will first have to call the client's Connect method. You can then call Send to send a message, e.g.
vb.net Code:
Me.client.Connect()
Me.client.Send("Hello Server")
To send a message from the server to a specific client you simply call Send and specify the client host details and the message. To send a message to all clients you call Send and just specify the message, e.g.
vb.net Code:
For Each host In Me.server.Hosts
Me.server.Send(host, "Hello " & host.ToString())
Next
Me.server.Send("Hello All Clients")
To disconnect you simply dispose the client or the server.
KNOWN ISSUES:
1. If you try to close a client window in the test application without having connected the window will not close. [Fixed in v1.0.0.1 on 2009-10-11. See post #2]
2. HostInfo.Equals throws an exception if passed a value that is not type HostInfo. [Fixed in v1.0.1.0 on 2009-11-10. See post #6]
3. IOException may be thrown when closing client child window. [Fixed in v1.1.0.1 on 2010-10-10]
The whole solution has been tested but more rigorous testing is still required. I'll be looking to improve the feature set and address any issues that arise so please feel free to post comments, suggestions and questions here, although I can't guarantee if and when I'll get to them.
UPDATES:
25 Oct 2009: v1.1.0.0 posted. See post #22 for details of changes.
10 Oct 2010: v1.1.0.1 posted. Fixes occasional crash when closing client child window.
Last edited by jmcilhinney; Oct 9th, 2010 at 09:53 PM.
Reason: Uploaded version 1.1.0.1
The problem was that the 'stream' field of the MessageClient class was not set until the connection was established and Close was being called on that field in the Dispose method regardless. The exception was preventing the form closing. To resolve the issue the Dispose method was changed from this:
vb.net Code:
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If Not Me.isDisposed Then
If disposing Then
'Close the connection and the underlying stream.
Me.stream.Close()
Me.client.Close()
End If
Me.stream = Nothing
End If
MyBase.Dispose(disposing)
Me.isDisposed = True
End Sub
to this:
vb.net Code:
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
To be able to communicate you will first have to call the client's Connect method. You can then call Send to send a message, e.g.
vb.net Code:
Me.client.Connect()
Me.client.Send("Hello Server")
I just wanted to point out that you can't really use code quite like that. When you call Connect the connection request is made asynchronously and the ConnectionAccepted event will be raised when that operation completes. I haven't actually tested the scenario but I imagine that, if you call Connect and then you call Send before receiving the ConnectionAccepted event, an exception will be thrown.
I'm afraid I've run into an immediate problem. As soon as I start up the program, the server starts running, but as soon as I hit 'Connect' on the client, I get the error message 'When casting from a number, the value must be less than infinity' in this section of code....
Code:
Public Overrides Function Equals(ByVal obj As Object) As Boolean
With DirectCast(obj, HostInfo)
Return Me.HostName.Equals(.HostName, StringComparison.CurrentCultureIgnoreCase) AndAlso _
Me.Port = .Port
End With
End Function
It's on the line 'With DirectCast.......'
One other thing, would it be possible to seperate this into two seperate programs, one for the server and one for the client? I realise it's much easier to test like this but it'll be necessary to split them eventually. Or give me a rough idea which files I'll need for each seperate program?
I'm afraid I've run into an immediate problem. As soon as I start up the program, the server starts running, but as soon as I hit 'Connect' on the client, I get the error message 'When casting from a number, the value must be less than infinity' in this section of code....
Code:
Public Overrides Function Equals(ByVal obj As Object) As Boolean
With DirectCast(obj, HostInfo)
Return Me.HostName.Equals(.HostName, StringComparison.CurrentCultureIgnoreCase) AndAlso _
Me.Port = .Port
End With
End Function
It's on the line 'With DirectCast.......'
I'd have to know more about exactly how you're using it to know exactly what the problem is. Are you running my test apps or are you trying to use the library in your own project? I'm not sure how you could come up with that error if you're using my test apps as I've checked where HostInfo.Equals method is called in my code and I can't see how that error could occur. Maybe I've missed something though so, if you are using my test apps, please give me a step by step run down of EXACTLY what you did so I can do the same thing and see if I get the same result.
If you're calling that method then I can only assume that you're using it incorrectly. The HostInfo.Equals method overrides the Object.Equals method so it will accept any object, but it's supposed to compare HostInfo objects. It sounds like you're passing it a value that isn't a HostInfo object. If it is your own code, what is the type and value of 'obj' when that error occurs and can you show me the code that calls that method.
That said, I guess that that method should really return False if the value passed in isn't a HostInfo object. That's not going to make your code work correctly if you're passing in an object of the wrong type but it will stop it crashing. I'll make that change and post a new version of the solution.
Originally Posted by Farflamex
One other thing, would it be possible to seperate this into two seperate programs, one for the server and one for the client? I realise it's much easier to test like this but it'll be necessary to split them eventually. Or give me a rough idea which files I'll need for each seperate program?
There's no need or point to breaking it up into two assemblies. The MessageClient and MessageServer classes both inherit the MessageClientServerBase class so you will need a common assembly somewhere regardless. The idea is that, if you have two separate applications, i.e. a client and a server, then they both reference the Wunnell.Net.MessageClientServer.dll assembly. The server application would simply ignore the MessageClient class and only instantiate the MessageServer class, while the client application would do the opposite. If you have one application that uses a TextBox and another that uses a ComboBox, is it a problem that both those apps have to reference System.Windows.Forms.dll because both those controls are defined in that same assembly?
I'm afraid I've run into an immediate problem. As soon as I start up the program, the server starts running, but as soon as I hit 'Connect' on the client, I get the error message 'When casting from a number, the value must be less than infinity' in this section of code....
Code:
Public Overrides Function Equals(ByVal obj As Object) As Boolean
With DirectCast(obj, HostInfo)
Return Me.HostName.Equals(.HostName, StringComparison.CurrentCultureIgnoreCase) AndAlso _
Me.Port = .Port
End With
End Function
It's on the line 'With DirectCast.......'
It seems I may have done you a disservice. I changed my code a bit and when I ran it I saw that the HostInfo.Equals method was being called by the system when the items were added to the ComboBox and the value they were compared to was DBNull, which would have failed the cast. Maybe those exceptions were being swallowed on my system but not on yours. Anyway, I've changed that method from this:
vb.net Code:
Public Overrides Function Equals(ByVal obj As Object) As Boolean
Public Overloads Overrides Function Equals(ByVal obj As Object) As Boolean
Return TypeOf obj Is HostInfo AndAlso _
Me.Equals(DirectCast(obj, HostInfo))
End Function
Download version 1.0.1.0 once I've uploaded it and see if that solves your problem.
I also found an InvalidOperationException being thrown sometimes when the server is closed while clients are connected that wasn't being caught, so I've addressed that too.
Thanks, with my level of incompetence, I'm sure we can sort these problems out
Hopefully you've sorted that one out, but all I did was load up the program and run it. I didn't make any changes at all. The server starts automatically and I just clicked 'Connect' on the client, and the error appeared. Hopefully the update will resolve that.
As for having two seperate assemblies, this is probably due to my incompetence with VB, but what I was looking to do was to have one program for my server and one for my clients. But I assume what you're saying is, they would both be identical anyway, I just need to set one up as a server, which runs as it does now but doesn't start any clients, and one which only starts the client. I'll tinker around with it once you've uploaded the new code and see if I can sort that without any problems.
Thanks, with my level of incompetence, I'm sure we can sort these problems out
Hopefully you've sorted that one out, but all I did was load up the program and run it. I didn't make any changes at all. The server starts automatically and I just clicked 'Connect' on the client, and the error appeared. Hopefully the update will resolve that.
That makes sense. From what I saw after I changed the code, that's where I would expect the problem to be because that's when the HostInfo gets added to the ComboBox. For some reason that exception was crashing the app on your system but it was getting swallowed on mine and the app continued. I don't know whether that's because of my VS settings or it's a Win7 thing but I'll be looking more closely at the Output window for first-chance exceptions in future. I think the change I made should fix that issue.
Originally Posted by Farflamex
As for having two seperate assemblies, this is probably due to my incompetence with VB, but what I was looking to do was to have one program for my server and one for my clients. But I assume what you're saying is, they would both be identical anyway, I just need to set one up as a server, which runs as it does now but doesn't start any clients, and one which only starts the client. I'll tinker around with it once you've uploaded the new code and see if I can sort that without any problems.
I think you misunderstand how this is supposed to work. You say that you want two separate applications for the client and the server. That's exactly what you already have. That solution contains three separate projects. One is the client/server library, one is the client application and one is the server application.
When you run the solution and the two windows pop up, they are two completely separate applications, and each of them references the third. That's how it would work for you. You would most likely create a solution with two projects: one for the client and one for the server. To each of those projects you would add a reference to the Wunnell.Net.MessageClientServer.dll assembly that you created by building my solution, or at least the Wunnell.Net.MessageClientServer project in my solution.
Ok, just to let you know it now works fine and is sending and receiving messages with no problems between server and client.
Now I just have to work out how where I add my own code for my server and client. The whole MDI thing confuses me. Don't worry, it's not your job to teach me VB, I'll delve into it and see how far I get
Ok, just to let you know it now works fine and is sending and receiving messages with no problems between server and client.
Now I just have to work out how where I add my own code for my server and client. The whole MDI thing confuses me. Don't worry, it's not your job to teach me VB, I'll delve into it and see how far I get
In your server app you create a MessageServer object and handle at least its ConnectionAccepted and MessageReceived events. Additionally, call its Send method to send messages to the client(s).
For the client, create a MessageClient instance and handle at least its ConnectionAccepted event and, if you want to receive messages from the server, its MessageReceived event too. Call its Connect method, wait for the ConnectionAccepted event and then you can start calling Send.
No probs. I understand the basic calls. What I don't understand at the moment is how the program is laid out. I'd like, for example, to just have the single client window, instead of the window within the window which opens multiple clients.
The problem stems from the fact that I've 'upgraded' to VB.net from VB6 and even further back from very old types of BASIC. Whilst I can understand the beauty and power of modern languages, it's a struggle to change my mindset to this new style of programming. So whilst I understand the whole sockets issue, I don't understand all the OOP that it's wrapped up in. Infact when I think about it, there's nothing about the sockets issue that I don't understand - but I have no idea how to translate that into VB.net. All that stuff with delegates and synclocks put my brain into a synclock
Anyhoo, I'm learning as I go along. I'll try to break this down into something I understand and then build onto it.
Nice to see that it wasnt just me that had to ignore the ObjectDisposed exception when calling EndAcceptTcpClient. Same for the IOException when calling EndRead.
I assumed there was a better way to handle this that I just couldnt figure out but seeing you do the same thing makes me feel a bit better about it
Nice to see that it wasnt just me that had to ignore the ObjectDisposed exception when calling EndAcceptTcpClient. Same for the IOException when calling EndRead.
I assumed there was a better way to handle this that I just couldnt figure out but seeing you do the same thing makes me feel a bit better about it
Yeah, I was hunting and hunting for some property that I could test before calling EndWhatever but I couldn't find anything, so I can only assume that catching the exceptions is the only way to go.
is is possible to send attachments such as pictures from a picturebox in the same way as the text it sent? it may not have anything all to do with the tcpclient but i was wondering.
is is possible to send attachments such as pictures from a picturebox in the same way as the text it sent? it may not have anything all to do with the tcpclient but i was wondering.
The point of this submission is specifically to send text between a client and server without feezing the UI. My wrapper class provides only the ability to send and receive text, but that's a decision I made. It's certainly not a limitation of the TcpClient class. It sits on top of a NetworkStream, which, like all streams, simply passes binary data form one place to another without any regard for what that data represents. As such they can send any data you like. It's up to the application to interpret the binary data and give it meaning.
If you check out my code you'll see that, internally, my MessageServer and MessageClient convert the text passed in to binary form before transmitting it and they convert the binary data received across the connection in text before passing it out to the caller. You can convert anything at all to binary form, thus you can write your own code to send anything you want using the TcpClient class.
If you want any further information on that, please start your own thread in the appropriate forum as it goes beyond the scope of this topic. If you want to discuss the asynchronous aspect of my code though, feel free to post here.
As a newbie forgive my ignorance. Your solution of 3 projects works fine on my desktop, uncompiled, just in debug. It also works fine on my laptop in debug. As I understand it I will use the entire solution on both computers. My question is how I would specify IP addresses. On my desktop that I would use as the server my first adapter is tied up on an intranet, and that is the adapter that the your test detects. I have a crossover between my laptop and a second adapter on my desktop that pings correct. How do I default your solution to the second adapter? thank you very much for your help.
As a newbie forgive my ignorance. Your solution of 3 projects works fine on my desktop, uncompiled, just in debug. It also works fine on my laptop in debug. As I understand it I will use the entire solution on both computers. My question is how I would specify IP addresses. On my desktop that I would use as the server my first adapter is tied up on an intranet, and that is the adapter that the your test detects. I have a crossover between my laptop and a second adapter on my desktop that pings correct. How do I default your solution to the second adapter? thank you very much for your help.
My MessageServer class accepts a port number in the constructor and then passes that to the Initialise method, which gets the first IPv4 address on the system and then starts listening on that combination. You would need to add one or more new constructors that also took an IP address and then use that in a new overload of Initialise. I should probably make that change myself but, for now, this is the sort of change you'd need to make, from this:
vb.net Code:
Public Sub New(ByVal port As Integer)
Me.Initialise(port)
End Sub
Private Sub Initialise(ByVal port As Integer)
Me._port = port
'Listen on the first IPv4 address assigned to the local machine.
Me.server = New TcpListener(Me.GetLocalIPv4Address(), port)
Would it not make sense to have the default be to listen on any IP address rather than just the first IP it finds? I'm pretty sure that is what the constructor for TcpListener does when you just pass it a port and no IP address.
Would it not make sense to have the default be to listen on any IP address rather than just the first IP it finds? I'm pretty sure that is what the constructor for TcpListener does when you just pass it a port and no IP address.
Im getting a lot of issues I can hardly do anything :S. Here is what i have:
Server:
Code:
Imports Wunnell.Net
Module ModMain
Private WithEvents server As New MessageServer(4000)
Sub Main()
AddHandler server.MessageReceived, AddressOf MessageRecieved
Console.ReadLine()
End Sub
Private Sub MessageRecieved(ByVal Sender As Object, ByVal e As MessageReceivedEventArgs)
Console.WriteLine(e.Message)
For Each host In server.Hosts
server.Send(host, "Hello " & host.ToString())
Next
server.Send("Hello All Clients")
End Sub
Private Sub Accepted(ByVal Sender As Object, ByVal e As ConnectionEventArgs)
Console.WriteLine(e.Host.Port)
For Each host In server.Hosts
server.Send(host, "Hello " & host.ToString())
Next
server.Send("Hello All Clients")
End Sub
End Module
Client Side
Code:
Imports Wunnell.Net
Public Class Form1
Private WithEvents client As New MessageClient("localhost", 4000)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.client.SynchronisingObject = Me
Me.client.Connect()
AddHandler client.ConnectionAccepted, AddressOf Accepted
AddHandler client.MessageReceived, AddressOf MessageRecieved
End Sub
Private Sub MessageRecieved(ByVal Sender As Object, ByVal e As MessageReceivedEventArgs)
MsgBox(e.Message)
End Sub
Private Sub Accepted(ByVal Sender As Object, ByVal e As ConnectionEventArgs)
Me.client.Send("Hello Server")
End Sub
End Class
Im getting a lot of issues I can hardly do anything :S. Here is what i have:
Server:
Code:
Imports Wunnell.Net
Module ModMain
Private WithEvents server As New MessageServer(4000)
Sub Main()
AddHandler server.MessageReceived, AddressOf MessageRecieved
Console.ReadLine()
End Sub
Private Sub MessageRecieved(ByVal Sender As Object, ByVal e As MessageReceivedEventArgs)
Console.WriteLine(e.Message)
For Each host In server.Hosts
server.Send(host, "Hello " & host.ToString())
Next
server.Send("Hello All Clients")
End Sub
Private Sub Accepted(ByVal Sender As Object, ByVal e As ConnectionEventArgs)
Console.WriteLine(e.Host.Port)
For Each host In server.Hosts
server.Send(host, "Hello " & host.ToString())
Next
server.Send("Hello All Clients")
End Sub
End Module
Client Side
Code:
Imports Wunnell.Net
Public Class Form1
Private WithEvents client As New MessageClient("localhost", 4000)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.client.SynchronisingObject = Me
Me.client.Connect()
AddHandler client.ConnectionAccepted, AddressOf Accepted
AddHandler client.MessageReceived, AddressOf MessageRecieved
End Sub
Private Sub MessageRecieved(ByVal Sender As Object, ByVal e As MessageReceivedEventArgs)
MsgBox(e.Message)
End Sub
Private Sub Accepted(ByVal Sender As Object, ByVal e As ConnectionEventArgs)
Me.client.Send("Hello Server")
End Sub
End Class
First up, let me point out that there's no point declaring your fields WithEvents if you're going to use AddHandler to attach event handlers. The whole and sole point of WithEvents is so that you can use that field in the Handles clause of your event handler methods.
As for the issue, you're telling the client to connect to "localhost", which is IP address 127.0.0.1, while the server is listening on its network IPv4 address, which will likely be 192.168.x.x. If you change "localhost" to Environment.MachineName in the client then it will work.
I'm going to make some changes to my code, including making the server listen on any address by default. Doing so should mean that "localhost" can be used at the client. As it stands, if you do want to use "localhost" at the client you need to explicitly tell the server to listen on that local loopback address.
1. Server now listens on IPAddress.Any by default. The caller can also specify an IP address, the machine name or "localhost".
2. Server now listens on a random port in the range 1024 to 5000 by default. The caller can also specify a port number.
3. Removed SynchronisingObject property. Client and server both now use the SynchronizingContext class to raise all events on the same thread on which the object was created. This means that, in a WinForms app, you must ensure that the object is created on the UI thread if you want to update the UI from event handlers.
jmcilhinney, I have another question. Is your library based off the TCPClient and TCPListener, Or based off the Socket Library Or Both.
I prefer using Asynchronous Sockets, A friend of mine told me Threaded sockets are terrible thats the reason I asked.
Regards,
Rydinophor
EDIT:
I keep getting these errors:
Code:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
I've never seen that error in my own testing so I'd have to know more about how you're using my library to know whether it's an issue with my code or how you're using it. As I said at the beginning of this thread, I have tested but not as rigorously as I'd like so there amy still be issues lurking.
jmcilhinney, I have another question. Is your library based off the TCPClient and TCPListener, Or based off the Socket Library Or Both.
I prefer using Asynchronous Sockets, A friend of mine told me Threaded sockets are terrible thats the reason I asked.
You do realise that the TcpClient and TcpListener classes are just wrappers around the Socket class anyway? They just make it a bit easier to use the sockets, they dont do anything differently internally when actually sending/receiving data via the sockets as far as I know.
You do realise that the TcpClient and TcpListener classes are just wrappers around the Socket class anyway? They just make it a bit easier to use the sockets, they dont do anything differently internally when actually sending/receiving data via the sockets as far as I know.
Quite true. The TcpClient class has a Client property to get its underlying Socket. As I said, I've never used the Socket directly though; only the TcpClient and its underlying NetworkStream.
System.Reflection.TargetInvocationException was unhandled
Message="Exception has been thrown by the target of an invocation."
Source="mscorlib"
StackTrace:
at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Delegate.DynamicInvokeImpl(Object[] args)
at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
at System.Threading.ExecutionContext.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(ApplicationContext context)
at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)
at WindowsApplication1.My.MyApplication.Main(String[] Args) in 17d14f5c-a337-4978-8281-53493378c1071.vb:line 81
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: System.IndexOutOfRangeException
Message="Index was outside the bounds of the array."
Source="WindowsApplication1"
StackTrace:
at WindowsApplication1.ModHandleData.HandleData(String Data) in C:\Users\ubersaiyanx\Desktop\Pokemon Cobalt Online\Latest\Client\WindowsApplication1\WindowsApplication1\ModHandleData.vb:line 23
at WindowsApplication1.Form1.client_MessageReceived(Object sender, MessageReceivedEventArgs e) in C:\Users\ubersaiyanx\Desktop\Pokemon Cobalt Online\Latest\Client\WindowsApplication1\WindowsApplication1\Form1.vb:line 16
at Wunnell.Net.MessageClientServerBase.RaiseMessageReceived(Object e) in C:\Users\ubersaiyanx\Desktop\Message Client-Server\Wunnell.Net.MessageClientServer\MessageClientServerBase.vb:line 240
InnerException:
Message="Index was outside the bounds of the array."
at WindowsApplication1.ModHandleData.HandleData(String Data) in C:\...\ModHandleData.vb:line 23
Exceptions can be a bit confusing at first but make sure you DO read the information provided. Don't just assume it's too hard because, as you can see, the exact location and cause of your issue was in there.
3. Removed SynchronisingObject property. Client and server both now use the SynchronizingContext class to raise all events on the same thread on which the object was created. This means that, in a WinForms app, you must ensure that the object is created on the UI thread if you want to update the UI from event handlers.
Why did you do this? I've often wanted my classes that use background threads to be able to raise their events on another thread but didnt think it was possible - after reading your post here http://www.vbforums.com/showthread.php?t=589212 I realise that it is possible but I'm wondering if there was some problem you found with doing this that caused you to remove such a feature?
Why did you do this? I've often wanted my classes that use background threads to be able to raise their events on another thread but didnt think it was possible - after reading your post here http://www.vbforums.com/showthread.php?t=589212 I realise that it is possible but I'm wondering if there was some problem you found with doing this that caused you to remove such a feature?
There was no problem per se. The way I was doing it originally, i.e. with a SynchronisingObject property, is the same as some classes in the Framework do it. One advantage of this is that it gives you the choice of leaving the SynchronisingObject property empty and having events raised on a thread pool thread or setting it and having events raised on a specific thread. I didn't really see a reason why you specifically want my classes to raise their events on thread pool threads though, so the change I made did three things for me:
1. It meant that a caller could completely ignore the fact that there was multi-threading involved and, in fact, wouldn't even know it from the interface. All interaction with an instance of one of my classes would occur on the same thread without any indication that any other threads were involved. There was no need for the caller to go to the (admittedly tiny) effort of setting the SynchronisingObject property to get synchronous events.
2. It made my code a bit cleaner.
3. It gave me a chance to play with the SynchronizationContext class, which I haven't had the pleasure of previously.
Like I said, there was nothing actually wrong with the old implementation. If you're interested in doing something similar but aren't 100% sure how then I'm happy to provide the old code. That said, you might also be interested in using the SynchronizationContext class as the new code does.
Ahh I see, sorry from reading your post saying that you must be certain to create the object on the UI thread if you want to update a form/control I thought that you were no longer providing any kind of 'easy' way of handling the cross thread calls. However, I now realise that the way your class works now is that it raises the events on whatever thread it was created on. So if you create an instance of your class on the UI thread then you dont need to worry about marshaling across the threads in the events that are raised from it, correct?
The MSDN doc on the SynchronizationContext class seems to be pretty light, did you find any other good sources of info on it? I'm currently reading through this: http://www.codeproject.com/KB/cpp/Sy...tTutorial.aspx
So if you create an instance of your class on the UI thread then you dont need to worry about marshaling across the threads in the events that are raised from it, correct?
Correct.
Originally Posted by chris128
The MSDN doc on the SynchronizationContext class seems to be pretty light, did you find any other good sources of info on it? I'm currently reading through this: http://www.codeproject.com/KB/cpp/Sy...tTutorial.aspx
That page and the MSDN doco are all I've read on the subject too.
Very nice Socket.
I would replace the combobox by a listview but u don't really know how to do...
Someone can help me ?
The point of this thread and the attached solution is the asynchronous client/server library. The WinForms projects were simply to demonstrate that. Displaying information in a ListView really has nothing to do with the topic of this thread. I suggest that you start a thread in the VB.NET forum dedicated to displaying information in a ListView.
Hello,
I found a little problem ( but didn't found solution ).
Code:
FR : Une exception de première chance de type 'System.IO.IOException' s'est produite dans System.dll
EN : A first chance exception of type 'System.IO.IOException' occurred in System.dll
I think that Error is occur when the socket is unloaded during a reception of data.
Hello,
I found a little problem ( but didn't found solution ).
Code:
FR : Une exception de première chance de type 'System.IO.IOException' s'est produite dans System.dll
EN : A first chance exception of type 'System.IO.IOException' occurred in System.dll
I think that Error is occur when the socket is unloaded during a reception of data.
The Debugger report me here :
Code:
Dim byteCount = Me.stream.EndRead(ar)
A first chance exception is not a problem. It just indicates that an exception was thrown, which is perfectly legal. It's when an exception is thrown and not caught that there's an issue. If you look at the code you'll see that there are several exception handlers in there to catch the exceptions that are thrown when a connection is closed while an asynchronous operation is in progress. I'm guessing that this is one of those cases.
If the app crashes due to an unhandled exception then by all means let me know exactly where, i.e. which line in which file. Otherwise it's all part of the plan.
Hmm i don't reall know why but the bug don't happen in the original source.
Only with mine ( i modified a the class for working a Chat app) .
And not always.
Is there VB.Net 2005 version of this class?
thank's
The code uses various VB 2008 and .NET 3.5 features. You could certainly implement the same functionality in VB 2005 but you'd have to use loops where I've used LINQ queries, etc.
Last edited by jmcilhinney; Nov 26th, 2009 at 04:21 AM.