views:

1681

answers:

3

How can a C# app easily communicate with an instance of itself present on another computer, which is on the same network, and transfer files and data?

Assuming the computers on the network have fixed local IP addresses, and they each know each others IPs. Would there also be a way to communicate if the IPs are unknown? based on some discovery protocol?

I heard the "Bonjour" service from Apple was a good protocol. Can we communicate via it from our Windows apps? Or do you have to use "sockets". I'm primarily looking for libraries or sample code that can fulfill my need easily, I don't want to develop my own TCP-based protocol or anything hardcore!

+2  A: 

For transferring the files / data, you can use the TcpClient/TcpListener classes, which is nice abstractions over the grittier socket functionality. Or, you could simply have the application as a HTTP server using the HttpListener class, if that is easier/more appropiate for your application.

For discovery, if you are able to have a central server; then you could have each client connect to the server at startup, to register itself and retrieve a list of other online clients and their IP's. Subsequent communication can then take place directly between the clients.

A variation of this scheme, is to let the central server act as a proxy, which all traffic between the clients flow through. This would be mostly helpful to overcome firewall or routing issues if the clients is not on the same network (so it's propably not needed for your scenario).

driis
ASP.Net classes? I was looking for comm between Windows apps.
Jenko
What do you mean ? None of the classes I mention are ASP .NET classes.
driis
+2  A: 

You can System.Net.Sockets class to communicate and it have a method for sending file Socket.SendFile.

Update:
this is a good example for file sharing and sending file from C# help

ecleel
+1  A: 

The great thing about files and sockets in C# is that they're both exposed as streams. Copying a large file from one stream to another is pretty simple:

byte[] data = new byte[1024];
while(true) {
int bytesRead = filestream.read(data,0,data.Length);
if (bytesRead==0) break;
netstream.write(data,0,bytesRead);
}

Then just close the socket when you're done.

If you want to send metadata (filenames, sizes) or don't want to close the connection, you need some sort of protocol to handle this. FTP uses two seperate sockets(one for metadata, one for data; this is called out-of-band communication). If you're on a LAN with no firewalls, that can be perfectly acceptable. On the other hand, if you want to do internet transfer, getting a single port open is a difficult-enough task, and two is unbearable. If you don't care too much about performance, you could encode the bytes in base64 encoding, which makes sure that they're within a certain byte range. With base64, you can seperate messages with newlines or other non-alphanumeric characters. Then in the first message include the filename, size, or whatever, then send the data as a second message, then send a "that's the whole file" message so the client knows it's done.

Another tactic for messages is using an escape sequence. For instance, take your bytestream and replace each instance of '\0' with '\0\0'. Now use '\0\1' to signal the end-of-message, which is guaranteed not to be contained in your data message. Decode the '\0\0' back to '\0' on the receiving end. This works well enough in C, but I find that, in practice, looping through each byte can be slower than reading whole buffers in C#.

The best way is to adopt some sort of adaptive-length protocol. For instance, send the data in chunks of a certain size (say 512 bytes). Before each chunk, send a 32bit int representing the size of the chunk via System.BitConverter. So messages look like this (english):

Here's 512 bytes:
[data]
Here's 512 bytes:
[data]
Here's 32 bytes:
[data]
Here's 4 bytes:
That was the whole file

The advantage here is that you can make the copy/read buffers work for you (reading 512 bytes at a time), meaning your throughput is limited by your network stack instead of your C# code. The client reads the fixed-length 32-bit int that lets it know the size of the buffer it should use for the next [data] segment.

Here's some code to write messages like that:

  logger.logger.debug("Sending message of length " + length);
  byte[] clength = System.BitConverter.GetBytes(buffer.Length);
  plaintextStream.Write(clength,0,clength.Length);
  plaintextStream.Write(buffer,0,buffer.Length);
  plaintextStream.Flush();

And here's some code to read them:

               byte[] intbuf = new byte[int_32_size];
  int offset = 0;
  while (offset < int_32_size)
  {
   int read = 0;

   read = d.plaintextStream.Read(intbuf,offset,int_32_size - offset);
   offset += read;

  }
  int msg_size = System.BitConverter.ToInt32(intbuf,0);
  //allocate a new buffer to fill the message
  byte[] msg_buffer = new byte[msg_size];
  offset = 0;
  while (offset < msg_size)
  {
   int read = 0;

   read = d.plaintextStream.Read(msg_buffer,offset,msg_size - offset);
   offset += read;
  }


  return msg_buffer;
Drew