C# 应用程序如何轻松地与存在于同一网络上的另一台计算机上的自身实例进行通信,并传输文件和数据?
假设网络上的计算机具有固定的本地 IP 地址,并且它们都知道彼此的 IP。如果IP未知,是否还有一种通信方式?基于一些发现协议?
我听说 Apple 的“Bonjour”服务是一个很好的协议。我们可以从我们的 Windows 应用程序通过它进行通信吗?还是您必须使用“套接字”。我主要是在寻找可以轻松满足我需求的库或示例代码,我不想开发自己的基于 TCP 的协议或任何核心!
C# 应用程序如何轻松地与存在于同一网络上的另一台计算机上的自身实例进行通信,并传输文件和数据?
假设网络上的计算机具有固定的本地 IP 地址,并且它们都知道彼此的 IP。如果IP未知,是否还有一种通信方式?基于一些发现协议?
我听说 Apple 的“Bonjour”服务是一个很好的协议。我们可以从我们的 Windows 应用程序通过它进行通信吗?还是您必须使用“套接字”。我主要是在寻找可以轻松满足我需求的库或示例代码,我不想开发自己的基于 TCP 的协议或任何核心!
您可以使用System.Net.Sockets类进行通信,它具有发送文件Socket.SendFile的方法。
更新:这是从 C# 帮助文件共享和发送文件
的一个很好的例子
C# 中的文件和套接字的优点在于它们都作为流公开。将大文件从一个流复制到另一个流非常简单:
byte[] data = new byte[1024];
while(true) {
int bytesRead = filestream.read(data,0,data.Length);
if (bytesRead==0) break;
netstream.write(data,0,bytesRead);
}
然后在完成后关闭套接字。
如果您想发送元数据(文件名、大小)或不想关闭连接,则需要某种协议来处理此问题。FTP 使用两个单独的套接字(一个用于元数据,一个用于数据;这称为带外通信)。如果您在没有防火墙的 LAN 上,那是完全可以接受的。另一方面,如果要进行互联网传输,打开一个端口是一项艰巨的任务,而两个则难以忍受。如果你不太关心性能,你可以用 base64 编码对字节进行编码,这样可以确保它们在一定的字节范围内。使用 base64,您可以使用换行符或其他非字母数字字符分隔消息。然后在第一条消息中包含文件名、大小或其他内容,然后将数据作为第二条消息发送,然后发送“那个”
消息的另一种策略是使用转义序列。例如,获取您的字节流并将“\0”的每个实例替换为“\0\0”。现在使用 '\0\1' 表示消息结束,保证不会包含在您的数据消息中。在接收端将 '\0\0' 解码回 '\0'。这在 C 中运行良好,但我发现,在实践中,循环每个字节可能比在 C# 中读取整个缓冲区要慢。
最好的方法是采用某种自适应长度协议。例如,以一定大小(比如 512 字节)的块发送数据。在每个块之前,通过 System.BitConverter 发送一个表示块大小的 32 位 int。所以消息看起来像这样(英文):
Here's 512 bytes:
[data]
Here's 512 bytes:
[data]
Here's 32 bytes:
[data]
Here's 4 bytes:
That was the whole file
这里的好处是您可以使复制/读取缓冲区为您工作(一次读取 512 个字节),这意味着您的吞吐量受到网络堆栈而不是 C# 代码的限制。客户端读取固定长度的 32 位 int,让其知道应该用于下一个 [data] 段的缓冲区的大小。
这里有一些代码来写这样的消息:
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();
这是一些阅读它们的代码:
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;
为了传输文件/数据,您可以使用TcpClient / TcpListener类,这是对 grittier 套接字功能的很好的抽象。或者,您可以简单地将应用程序用作使用HttpListener类的 HTTP 服务器,如果这更容易/更适合您的应用程序。
对于发现,如果您能够拥有中央服务器;然后您可以让每个客户端在启动时连接到服务器,以注册自身并检索其他在线客户端及其 IP 的列表。随后的通信可以直接在客户端之间进行。
该方案的一个变体是让中央服务器充当代理,客户端之间的所有流量都流经该代理。如果客户端不在同一个网络上,这对于克服防火墙或路由问题非常有帮助(因此您的场景可能不需要它)。
要复制文件,您可能还需要查看文件同步提供程序,它是 Microsoft 同步框架的一部分。http://msdn.microsoft.com/en-us/sync/bb887623。