tags:

views:

2252

answers:

3

I'm building a client/server application to make periodic backups of certain files. Basically, the client will monitor certain files for changes, and upload them to the server. The server will then receive them and will save them with an incrementing postfix.(i.e. "filename_v1", "filename_v2", etc). Now, I managed to get most of it done, but some issues arose and I could not find anything related to them.

Before I begin, please note that:

  • I did my best to find a solution on my own.
  • I followed (and completed) several socket programming tutorials.
  • I don't know anybody with c# and/or socket programming experience.

Here's the logic:

  1. Server starts up, begins to listen. (BeginAccept)
  2. When a client connects, a "worker" socket is created.
  3. Worker socket begins receiving. (BeginReceive) / Client sends a file ( SendFile )
  4. When data is received, file is saved. (also checking for earlier versions, but it's irrelevant here.)
  5. goto 3.(we need to receive more files from the same client.)

steps 1-4 work just fine.

Here's the problem: in step 5, even though server receives nothing, onDataReceive(the callback method) is called, and the server finds itself in an infinite loop. I tried to close (or disconnect) the worker socket, but no chance there. I'm beginning to suspect that maybe my algorithm is a bit sloppy.

Anyway, any comments, suggestions or help is highly appreciated.

+1  A: 

I've done some work on a socket library that is used to transfer medical images in .NET over the past year or so. Early on, we had a hard time detecting when a socket has shutdown, but have finally reached a stable solution.

Our application is using NetworkStream to do the reading/writing to the socket. We have a thread, similar to you, that is created when a new connection arrives, and is used to read and write data to the network. We call the method below to poll when data has arrived or when the connection has shut down. The method could be adjusted to use a longer timeout if you don't want to poll. When we know data is available, we read from the NetworkStream object.

The key point below is the zero byte Send on the socket, this seems to detect when the socket has shut down, as mentioned in the comments. The OnNetworkError method is just called to start the thread being shutdown.

Steve


protected override bool NetworkHasData() 
{
    if (_socket == null)
        return false;

    // Tells the state of the connection as of the last activity on the socket
    if (!_socket.Connected)
    {
        OnNetworkError(null, true);
        return false;
    }

    // This is the recommended way to determine if a socket is still active, make a
    // zero byte send call, and see if an exception is thrown.  See the Socket.Connected
    // MSDN documentation.  Only do the check when we know there's no data available
    try
    {
        List readSockets = new List();
        readSockets.Add(_socket);
        Socket.Select(readSockets, null, null, 100000);
        if (readSockets.Count == 1)
        {
            if (_socket.Available > 0)
                return true;
            OnNetworkError(null, true);
            return false;
        }

        _socket.Send(new byte[1], 0, 0);
    }
    catch (SocketException e)
    {
        // 10035 == WSAEWOULDBLOCK
        if (!e.NativeErrorCode.Equals(10035))
            OnNetworkError(e, true);
    }

    return false;
}

Steve Wranovsky
+1  A: 

I think you should take a look at WCF. Why reinvent the wheel with socket programming when you can use a messaging application that can easily transmit byte array over the wire?

WCF will handle your security, data transfer and all the plumbing that are currently trying to do. Of course, to transfer files you will be forced to set the message size way higher than the default... but I would seriously consider making a prototype with WCF before recoding the wheel.

I suggest the following resources:

Maxim
+2  A: 

Does your callback implement EndReceive?

Are you passing a state object? If so, have one of the properties be the buffer you pass to BeginReceive, then in your callback, check the state buffer to see if any data was received. If not, don't continue the loop.

struct SocketStateObject
{
  public byte[] Buffer;
  public Socket Socket;
}



Socket mySocket = new Socket(...);
SocketStateObject state;
state.Buffer = new byte[1024];
state.Socket = mySocket;

socket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, callback, state);

//don't remember the signature
public void callback(IAsyncResult a)
{
  if (((SocketStateObject)a.State).Buffer.Length = 0)
    //don't call socket.beginreceive again.
}
scottm