Click here to Skip to main content
Click here to Skip to main content

WCF TCP-based File Server

By , 2 Mar 2009
 

FileServer

Introduction

This article shows you how to build a remote file store using WCF. Specifically:

  • Using TCP binding to stream files to a client from a storage location
  • Connecting to the server from a WinForms client
  • Implementing the ability to download, upload, and delete files from remote storage
  • Configuring both server and client for streamed TCP transfer

Background

There are a few reasons why you might want to create such a remote file store; one of them might be because you have several applications which need to share files which have been uploaded by a system user and then access from a separate administration system. This is the very reason which prompted me to investigate how this can be done using WCF, and my solution is presented in this article.

Using the Code

In this section, I shall detail the sections of code which are relevant to the topic, starting with an overview of the service itself. You don't necessarily have to do this, but to keep things clean and tidy, I have split my solution up into three projects:

  • A class library project to house the service contract and the implementation, plus any utility classes which the service is dependant upon
  • A console application which hosts the service
  • A WinForms application to act as the client

The Service

The operations which our service will provide are as follows:

  • Downloading a file
  • Uploading a file
  • Deleting a file
  • Listing the contents of the file store

For three of the operations (downloading, uploading, and deleting), the service requires only a virtual path to the file. Since the client should know nothing about the actual physical locations of the files in the file store, all we can pass to these operations is the path relative to the root of the store.

The last operation helps with this, since we can get a list of the available files complete with their virtual paths and make them available through some user interface.

Let's have a look at the contract which defines the operations our service provides:

[ServiceContract] 
public interface IFileRepositoryService 
{ 
    [OperationContract] 
    Stream GetFile(string virtualPath); 

    [OperationContract] 
    void PutFile(FileUploadMessage msg); 

    [OperationContract] 
    void DeleteFile(string virtualPath); 

    [OperationContract] 
    StorageFileInfo[] List(string virtualPath); 
}

This is a pretty standard implementation of a service contract. Note that we are passing an actual Stream object from GetFile() which will contain the file data. Soon, we will configure our service for streaming which allows us to do this. When uploading a file (using PutFile()), we need to be able to send a Stream object and a virtual path to save the file to. However, in streaming mode, any messages which are to be streamed enforces a couple of restrictions on our methods (taken from http://msdn.microsoft.com/en-us/library/ms789010.aspx):

  1. The parameter which contains the streamed data must be the only parameter in the method.
  2. At least one of the types of the parameter and the return value must be Stream, Message, or IXmlSerializable.

The first point says that if the input message is to be streamed, there can only be one parameter in that method. If the output is to be streamed, then there can only be one output parameter or a return value.

So, if we need to pass some metadata along with our stream (such as a virtual path, in this case), then we can implement a message contract to send this data:

[MessageContract]
public class FileUploadMessage
{
    [MessageHeader(MustUnderstand=true)]
    public string VirtualPath { get; set; }

    [MessageBodyMember(Order=1)]
    public Stream DataStream { get; set; }
}

Here, the VirtualPath property is sent to the service operation as the SOAP header, whilst the Stream property is sent as the input parameter to the operation. This allows us to send both bits of information that we require to store a file on the server. The MustUnderstand setting on the MessageHeader attribute simply indicates that the message receiver must understand and be able to process that member; otherwise, the channel is faulted.

Service Implementation

Let's finish up the service by implementing the service contract. In addition to the four methods which are required in order to fulfill the implementation of the service contract, I've added some events and properties which allow us to monitor what is happening to the repository, along with a custom EventArgs class and an appropriate delegate:

public delegate void FileEventHandler(object sender, FileEventArgs e);

public class FileEventArgs : EventArgs
{
    /// <summary>
    /// Gets the virtual path.
    /// </summary>
    public string VirtualPath
    {
        get { return _VirtualPath; }
    }
    string _VirtualPath = null;

    /// <summary>
    /// Initializes a new instance of the <see cref="FileEventArgs"/> class.
    /// </summary>
    /// <param name="vPath">The v path.</param>
    public FileEventArgs(string vPath)
    {
        this._VirtualPath = vPath;
    }
}

We can then start to implement the service:

[ServiceBehavior(IncludeExceptionDetailInFaults=true,
InstanceContextMode=InstanceContextMode.Single)]
public class FileRepositoryService : IFileRepositoryService
{
    #region Events

    public event FileEventHandler FileRequested;
    public event FileEventHandler FileUploaded;
    public event FileEventHandler FileDeleted;

    #endregion
}

Looking at the attributes here, I've specified that the server should include any exception details if the service were to fault. This way, we get more detailed exception messages at the client should the service fail. I've also declared that the InstanceContextMode should be Single. This means that the service instance should be used for multiple calls, and not be recycled after every call. While this does mean that the service can only handle one call at a time, it becomes a requirement if we want to handle events and monitor service status (as we shall see when we set up the service host). Depending on your requirements, you might want to abandon this facility to allow the service to handle multiple calls.

We've also set up our public events which can be handled in order to monitor access to the file server.

Let's implement those service methods now.

/// <summary>
/// Gets or sets the repository directory.
/// </summary>
public string RepositoryDirectory { get; set; }

/// <summary>
/// Gets a file from the repository
/// </summary>
public Stream GetFile(string virtualPath)
{
    string filePath = Path.Combine(RepositoryDirectory, virtualPath);

    if (!File.Exists(filePath))
        throw new FileNotFoundException("File was not found", 
                                        Path.GetFileName(filePath));

    SendFileRequested(virtualPath);

    return new FileStream(filePath, FileMode.Open, FileAccess.Read);
}

/// <summary>
/// Uploads a file into the repository
/// </summary>
public void PutFile(FileUploadMessage msg)
{
    string filePath = Path.Combine(RepositoryDirectory, msg.VirtualPath);
    string dir = Path.GetDirectoryName(filePath);

    if (!Directory.Exists(dir))
        Directory.CreateDirectory(dir);

    using (var outputStream = new FileStream(filePath, FileMode.Create))
    {
        msg.DataStream.CopyTo(outputStream);
    }

    SendFileUploaded(filePath);
}

/// <summary>
/// Deletes a file from the repository
/// </summary>
public void DeleteFile(string virtualPath)
{
    string filePath = Path.Combine(RepositoryDirectory, virtualPath);

    if (File.Exists(filePath))
    {
        SendFileDeleted(virtualPath);
        File.Delete(filePath);
    }
}

/// <summary>
/// Lists files from the repository at the specified virtual path.
/// </summary>
/// <param name="virtualPath">The virtual path.
/// This can be null to list files from the root of
/// the repository.</param>
public StorageFileInfo[] List(string virtualPath)
{
    string basePath = RepositoryDirectory;

    if (!string.IsNullOrEmpty(virtualPath))
        basePath = Path.Combine(RepositoryDirectory, virtualPath);

    DirectoryInfo dirInfo = new DirectoryInfo(basePath);
    FileInfo[] files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);

    return (from f in files
           select new StorageFileInfo()
           {
               Size = f.Length,
               VirtualPath = f.FullName.Substring(
                 f.FullName.IndexOf(RepositoryDirectory) + 
                 RepositoryDirectory.Length + 1)
           }).ToArray();
}

Most of these methods implement standard file operations. Remember that these methods only want to work with files relative to the root of the repository. This service class also has a public property on it called RepositoryDirectory, which indicates where the root path of the repository is. The intention is for the service host to configure this when the host is started. All operations which need to find files inside the repository should need this root path.

Since we don't want to reveal physical locations to the client, the List() method takes care to strip off all the sensitive path information, and only returns virtual paths relative to RepositoryDirectory. You could also return additional information about the file at this point; I've chosen to also return the size of the file in bytes.

There is one helper method for each public event on this service, and they are:

/// <summary>
/// Raises the FileRequested event.
///  </summary>
protected void SendFileRequested(string vPath)
{
    if (FileRequested != null)
        FileRequested(this, new FileEventArgs(vPath));
}

///  <summary>
/// Raises the FileUploaded event
///  </summary>
protected void SendFileUploaded(string vPath)
{
    if (FileUploaded != null)
        FileUploaded(this, new FileEventArgs(vPath));
}

/// <summary>
/// Raises the FileDeleted event.
/// </summary>
protected void SendFileDeleted(string vPath)
{
    if (FileDeleted != null)
        FileDeleted(this, new FileEventArgs(vPath));
}

That wraps up the service itself; let's look at how to host this service.

Hosting the Repository Service

For this example, I have chosen to use NetTcpBinding for communication, but you could also use BasicHttpBinding, or any binding which supports the Streaming Transfer Mode. I'm going to run the server program as a Console application, configure the service entirely in the app.config file, and subscribe to those events that were created as part of the service implementation.

Let's create a console app, and set up a ServiceHost to host our repository service:

static void Main(string[] args)
{

    FileRepositoryService service = new FileRepositoryService();
    service.RepositoryDirectory = "storage";

    service.FileRequested += new FileEventHandler(Service_FileRequested);
    service.FileUploaded += new FileEventHandler(Service_FileUploaded);
    service.FileDeleted += new FileEventHandler(Service_FileDeleted);

    host = new ServiceHost(service);

    try
    {
        host.Open();
        Console.WriteLine("Press a key to close the service");
        Console.ReadKey();
    }
    finally
    {
        host.Close();
    }
}

This will start the service running at the location specified in our service configuration (coming shortly). Note here that we pass in an actual instance of FileRepositoryService. The other option is to simply tell the host what type we want to work with, and let the host create the instance for itself. I've done it this way so that I can handle the various events which can be raised from my service implementation, but this is what limits you to using the InstanceContextMode.Single setting on your service implementation; this instance must stay alive across multiple service calls for it to be useful, and the only way to ensure this is to not recycle the service after every call.

I've also set the RepositoryDirectory property here, pointing the service towards the "storage" folder in whatever directory the host is running from. All the files within this storage directory are counted as being part of the repository.

The event handlers simply write out some feedback text to the console window to let us know what is going on with the repository:

static void Service_FileRequested(object sender, FileEventArgs e)
{
    Console.WriteLine(string.Format("File access\t{0}\t{1}", e.VirtualPath, DateTime.Now));
}

static void Service_FileUploaded(object sender, FileEventArgs e)
{
    Console.WriteLine(string.Format("File upload\t{0}\t{1}", e.VirtualPath, DateTime.Now));
}

static void Service_FileDeleted(object sender, FileEventArgs e)
{
    Console.WriteLine(string.Format("File deleted\t{0}\t{1}", e.VirtualPath, DateTime.Now));
}

Configuring the service is just as short and sweet as setting up the hosting for it:

<configuration>
    <system.serviceModel>
        <services>
            <service name="FileServer.Services.FileRepositoryService">
                <endpoint name="" binding="netTcpBinding"
                    address="net.tcp://localhost:5000"
                    contract="FileServer.Services.IFileRepositoryService"
                    bindingConfiguration="customTcpBinding" />
            </service>
        </services>
        <bindings>
            <netTcpBinding>
                <binding name="customTcpBinding" 
                  transferMode="Streamed" 
                  maxReceivedMessageSize="20480000" />
            </netTcpBinding>
        </bindings>
    </system.serviceModel>
</configuration>

Here, we do several things:

  • Specify that the binding to be used is NetTcpBinding.
  • Specify that the address which the service should be hosted on should be "net.tcp://localhost:5000".
  • Tell the service which service contract we want to expose.
  • Specify some custom binding settings. We need this in order to be able to set up the streaming transfer mode. I've bumped up the maxReceivedMessageSize from the default value of 65,536 bytes, since I'd like this service to be able to transfer files larger than 64K.

The name attribute on the service element in the configuration is the same as the full type name of the service implementation; this is how the ServiceHost can pick up the correct configuration section when creating the host.

So, you should now be able to fire up your server program and leave it running, ready to accept connections.

The Client

The client that I built to connect to my server is a WinForms application. It's a simple GUI for displaying the file list from the server, and allows you to upload, download, and delete files. Here, I'll run through the major operations that this client performs rather than give you a code dump of the entire listing.

First, let's create a client proxy that we can use to access the service. For more information on the various methods of creating your proxy classes, please have a look at this article on client proxy generation. I'm going to use a hand-crafted proxy, which means that in my client, I have a reference to my Services project directly. If you created a proxy using Visual Studio, then you won't need this reference.

Here's my client proxy class:

public class FileRepositoryServiceClient : 
    ClientBase<IFileRepositoryService>, 
    IFileRepositoryService, IDisposable
{
    public FileRepositoryServiceClient()
        : base("FileRepositoryService")
    {
    }

    #region IFileRepositoryService Members

    public System.IO.Stream GetFile(string virtualPath)
    {
        return base.Channel.GetFile(virtualPath);
    }

    public void PutFile(FileUploadMessage msg)
    {
        base.Channel.PutFile(msg);
    }

    public void DeleteFile(string virtualPath)
    {
        base.Channel.DeleteFile(virtualPath);
    }

    public StorageFileInfo[] List()
    {
        return List(null);
    }

    public StorageFileInfo[] List(string virtualPath)
    {
        return base.Channel.List(virtualPath);
    }

    #endregion

    #region IDisposable Members

    void IDisposable.Dispose()
    {
        if (this.State == CommunicationState.Opened)
            this.Close();
    }

    #endregion
}

All of the methods here simply reflect the methods which are available on the server, with the exception of the overload on List() (since for this particular client, I want to always list files from the root of the repository and not have to pass in null all the time). Note also the lack of error handling; in a production environment, you will almost certainly want to implement the appropriate error handling routines here.

In the constructor for my client, I've given it the name of the configuration element that it should use when setting up the communication channel. This configuration section looks like the following:

<system.serviceModel>
    <client>
        <endpoint name="FileRepositoryService"
            address="net.tcp://localhost:5000"
            binding="netTcpBinding"
            contract="FileServer.Services.IFileRepositoryService"
            bindingConfiguration="customTcpBinding" />
    </client>

    <bindings>
        <netTcpBinding>
            <binding name="customTcpBinding" 
                maxReceivedMessageSize="20480000" 
                transferMode="Streamed" />
        </netTcpBinding>
    </bindings>
</system.serviceModel>

It is pretty similar to the host configuration. Note the endpoint name that we've given; this is what the client's constructor parameter is set to in the client class we created above. Also note that it uses the same binding (netTcpBinding) and points to the same address that the host is using. In the binding configuration, I've also set the transfer mode to 'Streamed' and have set the maxReceivedMessageSize attribute to the same value as the host.

The message size attribute setting isn't a necessity however; it's set to the same value on both, since I'm going to be streaming larger files to and from the server. If I was only downloading files from the server, then I would only need to set this on the client side. Similarly, if I was only going to be sending large files to the server, I would only need to set that on the host's side. Note also that these settings only need to be set manually if you wish to transfer more than 64K of data in a single transfer.

OK, lets go ahead and actually use the client.

There are four places where we can make use of the remote storage service:

  • Uploading a file to the repository using the 'Upload' button.
  • Downloading a file from the repository using the 'Download' button.
  • Deleting a file from the repository using the 'Delete' button.
  • Listing the files when the form loads, and also when the repository changes (such as uploading and deleting a file).

I'll go through each of these buttons and also list the method which fetches the file list from the repository. Each function simply makes a call to the service using the client which we crafted above, and as a result, makes implementing the client application very easy.

To list the files, we use the List() method:

private void RefreshFileList()
{
    StorageFileInfo[] files = null;

    using (FileRepositoryServiceClient client = new FileRepositoryServiceClient())
    {
        files = client.List(null);
    }

    FileList.Items.Clear();

    int width = FileList.ClientSize.Width - SystemInformation.VerticalScrollBarWidth;

    float[] widths = { .2f, .6f, .2f };

    for (int i = 0; i < widths.Length; i++)
        FileList.Columns[i].Width = (int)((float)width * widths[i]);

    foreach (var file in files)
    {
        ListViewItem item = new ListViewItem(Path.GetFileName(file.VirtualPath));

        item.SubItems.Add(file.VirtualPath);

        float fileSize = (float)file.Size / 1024.0f;
        string suffix = "Kb";

        if (fileSize > 1000.0f)
        {
            fileSize /= 1024.0f;
            suffix = "Mb";
        }
        item.SubItems.Add(string.Format("{0:0.0} {1}", fileSize, suffix));

        FileList.Items.Add(item);
    }
}

As you can see, most of this code deals with sorting out the UI, including setting column widths on the ListView control and creating the actual list items. The actual call to get the files from the remote store is done in only a few lines.

The upload, download, and delete file operations are executed in the same simple manner:

private void UploadButton_Click(object sender, EventArgs e)
{
    OpenFileDialog dlg = new OpenFileDialog()
    {
        Title = "Select a file to upload",
        RestoreDirectory = true,
        CheckFileExists = true
    };

    dlg.ShowDialog();

    if (!string.IsNullOrEmpty(dlg.FileName))
    {
        string virtualPath = Path.GetFileName(dlg.FileName);

        using (Stream uploadStream = new FileStream(dlg.FileName, FileMode.Open))
        {
            using (FileRepositoryServiceClient client = new FileRepositoryServiceClient())
            {
                client.PutFile(new FileUploadMessage() { VirtualPath = virtualPath, 
                                                         DataStream = uploadStream });
            }
        }

        RefreshFileList();
    }
}

private void DownloadButton_Click(object sender, EventArgs e)
{

    if (FileList.SelectedItems.Count == 0)
    {
        MessageBox.Show("You must select a file to download");
    }
    else
    {
        ListViewItem item = FileList.SelectedItems[0];

        // Strip off 'Root' from the full path
        string path = item.SubItems[1].Text;

        // Ask where it should be saved
        SaveFileDialog dlg = new SaveFileDialog()
        {
            RestoreDirectory = true,
            OverwritePrompt = true,
            Title = "Save as...",
            FileName = Path.GetFileName(path)
        };

        dlg.ShowDialog(this);

        if (!string.IsNullOrEmpty(dlg.FileName))
        {
            // Get the file from the server
            using (FileStream output = 
                   new FileStream(dlg.FileName, FileMode.Create))
            {
                Stream downloadStream;

                using (FileRepositoryServiceClient client = 
                       new FileRepositoryServiceClient())
                {
                    downloadStream = client.GetFile(path);
                }

                downloadStream.CopyTo(output);
            }

            Process.Start(dlg.FileName);
        }
    }
}

The upload and download functions get special mention because they work with Stream objects, but as you can see, you don't do anything different with them than what you'd normally do when working with streams. You just have to create some sort of stream and pass it to the service; we have already configured our service for streamed mode, so the transfer of the data is handled appropriately for us.

One thing I do use here is an extension method on Stream which simply copies data between any two streams. The extension method is called 'CopyTo', and looks like the following:

public static class StreamExtensions
{
    /// <summary>
    /// Copies data from one stream to another.
    /// </summary>
    /// <param name="input">The input stream</param>
    /// <param name="output">The output stream</param>
    public static void CopyTo(this Stream input, Stream output)
    {
        const int bufferSize = 2048;
        byte[] buffer = new byte[bufferSize];
        int bytes = 0;

        while ((bytes = input.Read(buffer, 0, bufferSize)) > 0)
        {
            output.Write(buffer, 0, bytes);
        }
    }
}

I've kept this extension method in my service library, since both my client and the service itself use it.

Finally, the delete method:

private void DeleteButton_Click(object sender, EventArgs e)
{

    if (FileList.SelectedItems.Count == 0)
    {
        MessageBox.Show("You must select a file to delete");
    }
    else
    {
        string virtualPath = FileList.SelectedItems[0].SubItems[1].Text;

        using (FileRepositoryServiceClient client = new FileRepositoryServiceClient())
        {
            client.DeleteFile(virtualPath);
        }

        RefreshFileList();
    }

}

When you delete a file, the service simply requires the virtual path to the file you wish to delete; I store this virtual path in a SubItem of the ListItem object when populating the ListView control, so all I do here is read that value out and send it back to the file service.

When you download, upload, and delete files, you should also be able to see the log messages coming through from our host in the console window. This way, if you have remote clients (and not a client on the same computer like we have here in our local environment), you can keep a tab on their actions directly.

Taking it Further

Obviously, this is a pretty basic implementation designed to show you how you could go about implementing something like this using WCF. To use in a production environment, you'll more than likely want to take into account security requirements. How can you restrict access to files in a multi-user environment? If you were to host the service on a public endpoint, how can you secure that so that only authorized clients can connect? What about more technical considerations such as restricting file size and enforcing transfer limits, possibly configurable per user?

Some of these requirements can be implemented by making the appropriate changes to the service; for example, supplying the service with a username and password which the service would authenticate against a database, or by using Windows security to control access to the files.

These sorts of considerations I have yet to touch on myself, but at least the code presented here will stand you in good stead to begin to tackle these issues.

And, that's all there is to it. All of the code here is available to view and mess around with in the archive attached to this article. If you have any questions, I'd be happy to answer them!

Thanks for reading.

History

  • 02/03/2009: First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

StevenHobbs
Software Developer Orchid Software
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionIs this application a P2P system?memberAmir Jalilifard22 Mar '13 - 23:42 
Hi,
I am confused to understand that is this application a Peer to Peer application?
If it is not,What should we do for being a P2P application?
Is TCP a different concept than P2P?
So what is different between net.tcp:// and net.p2p:// in addressing ?
AnswerRe: Is this application a P2P system?memberdu[DE]23 Mar '13 - 4:15 
No, it is not. If you are looking for one such project, take a look: A simple peer to peer chat application using WCF netPeerTcpBinding[^]
QuestionHow to use the sample without app.config??memberMember 945705526 Sep '12 - 3:57 
How to use the sample without app.config?? I don't want to store the config information into app.config.
QuestionCopyTo now out of the boxmemberwarnov18 Aug '12 - 18:56 
First of all, let me congratulate you for such a great article and nice solution you've done...
 
Second, have you noticed that CopyTo now is a native method of Stream?
 
I just found very special that they called the same way you called your extension method.
Do you have an story about this?
 
Thanks!
Open mind for a different view.
http://warnov.com

QuestionGetting an Errormembervarun surana18 Oct '11 - 3:05 
Hello,

I tried using net.tcp://74.208.195.53:5000 in your code to upload files to my remote server but i am getting the follwoing error

An unhandled exception of type 'System.ServiceModel.EndpointNotFoundException' occurred in mscorlib.dll Additional information: Could not connect to net.tcp://74.208.195.53:5000/. The connection attempt lasted for a time span of 00:00:21.0122019. TCP error code 10060: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 74.208.195.53:5000.

Can someone please tell what i am doing wrong.

Thanks
QuestionResuming uploads and downloadsmemberChristian Suarez29 Jun '11 - 6:10 
Great article!! Any ideas about resuming uploads and downloads?? I was looking at the msdn article about chunking channel but it's still confused, maybe there is another approach. Thanks.
GeneralMultiple Downloads not supportedmemberGilZhaiek17 Jan '11 - 11:41 
The stream is being sent back. Only when the client has finished downloading another client can start a new download.
I believe there is a problem with multiple connections in the same time.
 
If you add this to GetFile (after you create a stream):
 
OperationContext clientContext = OperationContext.Current;
clientContext.OperationCompleted += new EventHandler(delegate(object sender, EventArgs args)
{
   if (_stream != null)
   {
     _stream.Dispose();
   }
});
 
You will see a new GetFile will be called only when the OperationCompleted has exited.
 
Any help? Am I doing something wrong?
GeneralGood Article But Need HelpmemberMember 176491311 Jan '11 - 10:44 
I keep getting the following error when trying to add a Service Reference on the same Windows 7 computer that is hosting the application as a Windows Service:
 
Metadata contains a reference that cannot be resolved: 'net.tcp://localhost:5000'.
The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:04:59.6044922'.
An existing connection was forcibly closed by the remote host
If the service is defined in the current solution, try building the solution and adding the service reference again.
 
I used your app.config and tried several others as well. I also tried removing the custom binding but nothing is working.
 
The service itself starts up without any errors.
 
Do you have any ideas?
 
Thanks!
GeneralRe: Good Article But Need HelpmemberGilZhaiek17 Jan '11 - 11:43 
Did you start the service? (FileServer app)
You need to start this before you can start the client.
QuestionIs there a way to add windows credentials?memberDriftWare9 Mar '10 - 1:26 
Awsome app. Smile | :)
 
I would like to know if there is a way to intergrate windows credentials?
 
I noticed when you connect via internal/external network from a user not on
the computer hosting the server, I get a "Server rejected your credentials" Exception.
 
However when the computer running the client is Administrator it works fine.
I'm thinking because Administrator is in my user group on the PC hosting the server.
QuestionWCF limitsmemberDarinko30 Dec '09 - 22:03 
Hi, i have some issues about services like these.
I developed a similar service for transfering files via network.
 
Anyhow, i got task to test it's bandwidth in multi-user environment.
I created test client that calls "GetFile" method in multiple separate threads.
I was intrested in how many clients can download one single file at the time.
 
Can not determine what is 'bottleneck', since every test gives me different results.
 
After 15 to 20 open connections (sometimes less), thread gives me following exeception:
 
The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '22:59:59.7999885'.
 
{"An existing connection was forcibly closed by the remote host"}
 
File is around 11MB.
AnswerRe: WCF limitsmemberGil Zhaiek17 Jan '11 - 11:51 
I believe the default connection to a tcp is 10
 
Here is an example:
streamedFileTransferServiceHost = new ServiceHost(typeof(StreamedFileTransferService));
 
ServiceThrottlingBehavior behavior = new ServiceThrottlingBehavior ()
{
   MaxConcurrentCalls = 100,
   MaxConcurrentSessions = 100,
MaxConcurrentInstances = 100            
};
streamedFileTransferServiceHost.Description.Behaviors.Add(behavior);
streamedFileTransferServiceHost.Open();
 
Did you succeed to create a multiple simultaneous download? At least 2 clients that copy data from the stream together?
I am having some difficult times with this issue.
GeneralStream and Memory leaksmemberOleh Mykhaylovych1 Sep '09 - 11:31 
hello, I'm having such question.
 
if you return a stream from webservice, won't it give any memory leaks if we don't close stream object?
what if we use byte array and path it to the client. thanks
 
No sleep for you yet, I wanna see smoke from those keyboards!!.

GeneralTransaction usage, whil streaming the file with WCFmemberhitendrapatel28 Apr '09 - 0:36 
Hi,
Could you help on any drawback of using transaction (System.transaction) in the code you posted? I heard from couple of my friend that Transaction doesn't work in WCF when there is streaming of file, I was not able to find the concrete reason or is it true and why?
 
Say we made File server which has operation to write file in to some data source (say SQL or local file system) and same way of giving back this file to the client when they want (a download option)
 
There is transaction when user uploads the file because there are some attributes of file needed to be stored in separate entity at data source level,
 
When File Service is on other machine and there are multiple remote client, multiple calls to the server, call to download from or write the file on server
So does theses actions going to work under "System.Transaction" transaction scope, if there is any issue then things will get rollback? If transaction can not work then what’s the way or alternate to it?
 
It would be great if you could answer, i could have tried but not having such resource available right now to sample this, but before that If i can have any answer that would make my work easy.
 

 
Regards,
HPatel
GeneralHost as a Windows Servicemembermoldie8 Apr '09 - 6:09 
Great piece, just what I was looking for except I wanted to host it in a Windows Service. I followed the instructions on http://msdn.microsoft.com/en-us/library/cc949080.aspx and deleted FileRepositoryServiceClient.cs on the client as the Services References does this for you.
 
My problem is that whenever I access any of the Interface commands I get an Exception "Value cannot be null. Parameter name: path".
Anyone able to point me in the general direction?
GeneralRe: Host as a Windows ServicememberStevenHobbs8 Apr '09 - 22:27 
Glad you like the article. The only think I can suggest without digging too deep is that each service method I've implemented uses Path.Combine() to build the path to the local file store on the server. The property which is passed to the first parameter of this method is RepositoryDirectory, which in turn gets its value from the code which creates the service host.
 
So the first thing I have to ask is have you configured the server with that setting correctly? See the section under 'Hosting the repository service' to see how I've done this.
GeneralRe: Host as a Windows Servicemembermoldie9 Apr '09 - 4:59 
Superb, thanks for you assistance, much appreciated. You were right, I had this:
 
host = new ServiceHost(typeof(FileServer.Services.FileRepositoryService)); in my WindowsServiceHost and change to
 
service = new FileRepositoryService();
service.RepositoryDirectory = "storage";
host = new ServiceHost(service); just like your example.
 
It saves to c:\Windows\system32\storage now but I guess thats because it is a windows service, but can figure that out for myself.
 
Once again, thanks Smile | :)
GeneralRe: Host as a Windows ServicememberStevenHobbs9 Apr '09 - 5:13 
No probs. You would able to set RepositoryDirectory to a full logical path to the storage directory and it'll work Smile | :)
QuestionHow to handle big filememberBruce Zhang2 Mar '09 - 18:33 
It's cool one that I want to do. Actually, I had finished the file manager based on WCF technology like this, but it's very big issue to handle the big file. Can you solve it by your application?
AnswerRe: How to handle big filememberStevenHobbs2 Mar '09 - 20:45 
After doing a bit of reading, large files should be handled easily by the very fact that the file is being transfered in streamed mode (and not buffered/chunked), although you might run into other issues, such as the request timing out.
 
You need to set the maxReceivedMessageSize attribute in your config to be big enough to handle your largest file; other than that, I'm not sure at this point about any issues with large files, but I can sure find out..
GeneralRe: How to handle big filememberStevenHobbs2 Mar '09 - 20:50 
I've just come across this (and this is for the first thread poster too)..
 
http://kjellsj.blogspot.com/2007/02/wcf-streaming-upload-files-over-http.html[^]
 
In there the author implements an uploading service which handles large files under IIS - maybe something in there can help you?
GeneralRe: How to handle big filememberBruce Zhang4 Mar '09 - 1:58 
Yeah. you can set the value of maxReceivedMessaeSize into the Int32.Value in theory. But I don't know what it occurs when the file's size reaches the so huge number. Second, I have to worry about the performance of your implementation. My recommendation is that you should provide the Begin*** and End*** methods pair by async mode. Do you think so?
GeneralRe: How to handle big filememberStevenHobbs4 Mar '09 - 2:21 
Yes absolutely; that is something that could be added under the 'Taking it further' section.
 
The svcutil.exe program (if you're generating your client proxy classes) can take a parameter which, when set, will generate those Async methods for you. When working with larger files you'll almost certainly want those async methods in order to keep the program responsive.
GeneralRe: How to handle big filememberBruce Zhang4 Mar '09 - 3:32 
Right. I agree with you. It's best way to implement the async method using by /async switch. Thanks.
GeneralRe: How to handle big filemvpSacha Barber6 Mar '09 - 6:21 
Yeah I actually think MSMQ endpoint could help out with this.
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

AnswerRe: How to handle big file - Rsyncmemberjboarman9 Mar '09 - 11:38 
I do hope someone does take this further with async methods for handling very large files (100s of MG or even GBs). We transfer large files between various remote locations and use RoboCopy for that now.
 
We've experimented with compression and found that it would be faster (using 7z compression for example) than straight RoboCopy. However, we'd need to invest the time to create command-line tools to simplify the interface for our usage.
 
Another tool we looked at is rsync. It is a very good tool for only sending the chunks of data needed to update the changed portions as compared to an existing copy residing at the destination. It would be very useful if someone does expand this project to include a "Synchronize" operation contract (if even just one file at a time). The synchronize operation could borrow the implementation using the rsync algorithm:
http://en.wikipedia.org/wiki/Rsync[^]
GeneralRe: How to handle big file - RsyncmemberStevenHobbs10 Mar '09 - 3:31 
Looks like an interesting extension there - I'll look into it as an extension to this project, and also look into the options regarding sending large files with async operations.
GeneralmaxReceivedMessageSizemembergiammin2 Mar '09 - 11:40 
maxReceivedMessageSize set the maximun file size for uploaded file...
 
you should strip the file into buffer and sequential wcf messages.
 
i think that send over the web a 20mb wcf message would fail.
GeneralRe: maxReceivedMessageSizememberStevenHobbs2 Mar '09 - 20:29 
Yes I agree; if you wanted to use an http binding to implement this you might need to think about how you send larger files. My implementation above was Tcp based across a network and so I didn't run into that particular issue.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 2 Mar 2009
Article Copyright 2009 by StevenHobbs
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid