Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
Go Back  Xtreme Visual Basic Talk > > > Weird Flow of Control, NetworkStream.Read() loop


Reply
 
Thread Tools Display Modes
  #1  
Old 05-13-2011, 10:02 AM
Adel99 Adel99 is offline
Centurion
 
Join Date: Aug 2005
Posts: 160
Default Weird Flow of Control, NetworkStream.Read() loop


Hello,

I'm in the process of making a simple file sharing application, to be used in a simple archiving project. I want to do the file sharing using .Net sockets; and I have been learning about them for a week. I'm stuck now, though, and need some help please.

I have two projects, one for clients (ClientApp), the other for the server (ServerApp). I want files to be sent from clients to the server only. I have two computers at home (LAN) on which I test my applications.

ClientApp:
  1. Enter server IP
  2. Select a file
  3. Connect to the server using TcpClient class
  4. Send the file using NetworkStream class
ServerApp:
  1. Use main thread for UI
  2. Start a new thread for listening, TcpListener class
  3. Start a new thread for handling every incoming file
  4. Read the file using NetworkStream.Read() loop, and save the file using FileStream class.
The ClientApp seems to work OK. The whole file is transferred to the server and saved, but with strange problems.

Problem #1: Weird Flow of Control Around NetworkStream.Read() loop
Code:
netStream As New NetworkStream(handlingSocket)
        Dim packet(1024) As Byte
        Dim bytesRead As Integer = netStream.Read(packet, 0, packet.Length)
        Dim fileStream As New FileStream("C:\Server Files\File Sent.mp3", FileMode.Create, FileAccess.Write)
        SyncLock Me
            While bytesRead > 0
                fileStream.Write(packet, 0, bytesRead)
                bytesRead = netStream.Read(packet, 0, packet.Length)
            End While
            'EXECUTION NEVER REACHES THE FOLLOWING LINES
            fileStream.Close()
            netStream.Close()
            handlingSocket = Nothing
        End SyncLock
If I'm correct, the execution never exits the While loop; and at the same time, doesn't execute the statements inside the While loop after transferring is complete! This can't be true, but I need a correct explanation: The execution seems to be stopping at some point. Where is that point?

Important: on the server, the transferred file is complete and readable; BUT it can NOT be deleted until the ServerApp is terminated (Being used by another person or program).

Problem #2: Closing the Form Doesn’t Terminate the ServerApp
The ServerApp doesn't terminate simply by pressing the [X] in the title bar of the form. The form is closed but the application is still running. Why? Is it because it's mutilthreaded? Is the following workaround correct in all cases of termination:
Code:
    Private Sub Form1_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
        End
    End Sub
What is the correct way of terminating the ServerApp?

Thank you very much in advance. You can find the two projects in the attachments. If testing, please send an .mp3 file.

Adel,

Last edited by Cerian Knight; 05-23-2011 at 07:24 PM. Reason: Removed attachments containing binaries
Reply With Quote
  #2  
Old 05-23-2011, 06:06 PM
Schravendeel Schravendeel is offline
Newcomer
 
Join Date: Apr 2010
Posts: 19
Default

Not sure if you still need this but I think the problem lies in the fact that you dont close the file stream after the file has been sent. Simply adding

Code:
netStream.Close()
here

Code:
Imports System.IO
Imports System.Threading
Imports System.Net.Sockets

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim ofd As New OpenFileDialog()
        ofd.ShowDialog()
        FilePath.Text = ofd.FileName
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim fileStream As Stream = File.OpenRead(FilePath.Text)
        Dim binaryFile(fileStream.Length) As Byte
        fileStream.Read(binaryFile, 0, fileStream.Length)
        Dim clientSocket As New TcpClient(ServerIp.Text, 8410)
        Dim netStream As NetworkStream = clientSocket.GetStream()
        netStream.Write(binaryFile, 0, fileStream.Length)
        netStream.Close() '<--- HERE
    End Sub
End Class
Fixes the problem for me. Happy coding!
Reply With Quote
  #3  
Old 05-23-2011, 07:04 PM
AtmaWeapon's Avatar
AtmaWeaponWeird Flow of Control, NetworkStream.Read() loop AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Adel99, you've been here long enough you should know not to post binaries in your zip files. A moderator's going to have to delete them. Remove the bin/obj folders before posting. Usually it's a good idea to zap the .suo file too; it clobbered my window layout and made me sad.

Everything is working as designed, and Schravendeel is correct. If you don't close your network stream, the packets sent to the server don't indicate that the connection has terminated. So Read() sits there waiting for data that'll never come. It's not an immediate, "Read data and return 0 if there isn't any" method because on a slow connection it might be likely you'll go a stretch with no data. Instead it blocks and assumes you won't call it unless you A) know there's data to read and/or B) behave and close the streams on the other side.

If a class has a Dispose() or a Close() method, NEVER write code that doesn't call it. That method is your only chance to tell the object you're done and it should free up any expensive resources it owns. In this case, the "expensive resource" is a network stream and if you don't close it you have to wait for the garbage collector to run before it is freed. That might happen in 10 seconds. It might happen in an hour. GC.Collect() might not even force it to work; if you get too pushy the GC ignores you. Never, ever, EVER rely on the GC to clean up a disposable object for you.

To that end, you're still not protected if something goes wrong. If an exception is thrown, you might not call Close() and the stream might not get released properly. The hard way to guarantee this is with a Try/Finally:
Code:
Dim fileStream As Stream = Nothing
Try
    fileStream = File.OpenRead(...)
    ' blah blah blah
Finally
    If fileStream IsNot Nothing Then
        fileStream.Close()
    End If
End Try
A Finally block always executes, even if an exception is thrown. The unfortunate side effect here is the variable has to live outside the Try so the Finally can see it, and you might accidentally try to use the variable in a bad place after the End Try. The Using statement is much more elegant:
Code:
Using fileStream As Stream = File.OpenRead(...)
    ' blah blah blah
End Using
In your case, you might want to stack them. The server code could be:
Code:
Using netStream As New NetworkStream(handlingSocket), _
    fileStream As New FileStream(...)
    ' blah blah blah
End Using
Note that the Socket class implements the IDisposable interface (the thing that tells you it has a Dispose() or Close() method.) It has a Disconnect() method that should be called when you're done with it to make sure it cleans itself up. Call it. You can't use a Using statement here because it's not created by you.

On to question #2: why doesn't the server quit? Because End is not a good way to stop a program. Read the tin.:
Quote:
The End statement stops code execution abruptly, without invoking the Dispose or Finalize method, or any other Visual Basic code. Object references held by other programs are invalidated. If an End statement is encountered within a Try or Catch block, control does not pass to the corresponding Finally block.
The End statement is for, "The sky is falling, kill everything and let the OS try and sort it out." Note how it mentions no Dispose or Finalize methods are called? That's what would shut down your server.

The more appropriate thing to do is shut down the server when the form is closing. VB will notice the last form has closed and initiate a proper shutdown. But you've got two problems: there's no way for your FormClosed event to get at the TcpListener and it's actually going to be too busy working to shut down. Let's attack them one at a time.

First, it's easy to make it accessible:
Code:
Public Class Form1
    Private _listener As TcpListener
    
    '...

    Private Sub ListeningMethod()
        _listener = New TcpListener(localhost)
        ' ...
    End Sub
    
    ' ...
    
    Private Sub Form1_FormClosed(...)
        _listener.Stop()
    End Sub
End Class
This might not work though. AcceptSocket() is blocking. It sits there waiting and waiting for a connection to come, and if you're closing that connection isn't coming. Odds are Stop() won't have an effect. The documentation for TcpListener tells you as much:
Quote:
Any instance members are not guaranteed to be thread safe.
Since your thread isn't configured to be a background thread, your application can't quit until the thread quits. But the thread's waiting for a connection it won't ever get, and Stop() has no effect. What do you do?

You should only call AcceptSocket() if you know there's a connection waiting. How do you know? TcpListener has a Pending() function that returns True if a connection is waiting. BUT, you also need to know if the main thread has asked the listener to stop. So let's start with that plumbing:
Code:
Public Class Form1
    Private _stopRequested As Boolean
    Private _readyToClose As Boolean
    ' ...
    
    Sub Form1_Closed(...)
        _stopRequested = True
        
        While Not _readyToClose
            Thread.Sleep(1000)
        End While
    End Sub
This sets a flag that tells the listener thread we'd like to stop. Then it waits for the other thread to indicate it's finished. Should I use SyncLock here? It probably doesn't matter for setting a boolean. The worst case scenario is the other thread takes an extra second to die. Using SyncLock improperly here could cause a deadlock; it's safer to ignore it.

Now how does the server know when to quit?
Code:
Private Sub ListeningMethod()
    _listener = blah blah blah
    _listener.Start()
    
    While Not _stopRequested
        If _listener.Pending()
            Dim handlingSocket As Socket = listener.AcceptSocket()
            ' ...
        Else
            Thread.Sleep(250)
        End If
    End While
    
    _listener.Stop()
    _readyToClose = True
End Sub
First, it checks if the form is closed and it should stop. Next, it checks if there's any pending connections. If there are connections, it accepts the connection and receives the file. If there are no connections it waits 1/4 of a second before checking again. When the form closes, _stopRequested is True and the listener stops, then indicates it's ready to close. The trap is sprung; the form can close.

Here's a final pitfall:

NEVER lock on a public object like Me. Some stupid classes in the .NET Framework might decide to do so as well and deadlock your program. It's even more concerning in other cases, where people think it's a good idea to lock on some instance they've been passed. Instead, declare some private object for locking. I always use this pattern:
Code:
Private ReadOnly LockObject As New Object()

Public Sub SomeThreadMethod()
    SyncLock LockObject
        ' do some stuff
    End SyncLock
End Sub
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #4  
Old 05-31-2011, 12:26 PM
Adel99 Adel99 is offline
Centurion
 
Join Date: Aug 2005
Posts: 160
Default

Thank you, and sorry for the late reply. In fact, I found this, and have been studying it for a while in order to write my own. Because of this, I really need the information AtmaWeapon provided. That's very helpful.

Thanks again to both of you.
Adel,
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
 
Weird Flow of Control, NetworkStream.Read() loop
Weird Flow of Control, NetworkStream.Read() loop
 
-->