通过 NetworkStream C# 识别传入文件
Identify an incoming file through NetworkStream C#
我正在使用 TcpClient 和 TcpListener 类 制作一个 client/server 聊天应用程序。它应该能够在客户端和服务器之间传输文本消息和文件。我可以通过为每个单独的客户端创建一个线程,然后创建一个辅助线程来接收传入消息,从而使主线程保留用于发送消息来处理文本消息。现在,如果我能够识别传入消息是文件而不是文本消息,那么我就知道如何使用 NetworkStream 和 FileStream 来处理它。但我无法这样做。处理传入文件的代码是 here。另外请告诉我使用 NetworkStream 是否有任何限制 FTP.
答案:构建您自己的协议。
通过构建自己良好的通信协议,您可以控制所有 data/message 流。
例如;
1-用户想要发送文件到服务器
2-客户端发送命令通知服务器它将发送一个 file.Like ;
@File@filename;filesize;
3-服务器向客户端发送就绪消息 @FileAccepted@
4-Server 开始监听缓冲包,当它收到时将它们写入流。
5-当客户端收到 {@FileAccepted@}
命令时,开始向服务器发送包。确保它们的缓冲区大小相同。
6-当所有字节完成时,客户端在不同的缓冲区中发送 @FileEnd@
(我使用 256 用于命令,1024 用于文件传输)
7-当服务器接收到 256 字节命令时,查看它的 @FileEnd@
命令是否为真刷新文件流并关闭连接。
我建议你使用 Async
像这样监听服务器上的连接
server.BeginAcceptTcpClient(ServerAcceptEnd,server);
当连接存在时
public void ServerAcceptEnd(IAsyncResult ar)
{
if(!ar.IsCompleted)
{
//Something went wrong
AcceptServer();
return;
}
try
{
var cli = servertc.EndAcceptTcpClient(ar);
if(cli.Connected)
{
//Get the first Command
cli.GetStream().BeginRead(serverredbuffer,0,serverredbuffer.Length,ServerFirstReadEnd,cli);
}
else
{
//Connection was not successfull log and wait
AcceptServer();
}
}
catch(Exceiption ex)
{
//An error occur log and wait for new connections
AcceptServer();
}
}
收到第一个命令时;
public void ServerFirstReadEnd(IAsyncResult ar)
{
if(!ar.IsCompleted)
{
//Something went wrong
AcceptServer();
return;
}
try
{
TcpClient cli = (TcpClient)ar.AsyncState;
int read = cli.GetStream().EndRead(ar);
string req = toString(serverredbuffer);
if(req.StartsWith("@File@"))
{
//File Received
string filename = req.Replace("@File@","");
string[] spp = filename.Split(';');
filename = spp[0];
serverreceivetotalbytes = Convert.ToInt64(spp[1]);
cli.GetStream().Write(toByte("@FileAccepted@",256),0,256);
cli.GetStream().BeginRead(serverreceivebuffer,0,1024,ServerReadFileCyle,cli)
}
else
{
//Message Received
}
}
catch(Exception ex)
{
//An error occur log and wait for new connections
AcceptServer();
}
}
文件接收方法;
public void ServerReadFileCyle(IAsyncResult ar)
{
TcpClient cli = (TcpClient)ar.AsyncState;
if(ar.IsCompleted)
{
int read = cli.GetStream().EndRead(ar);
if(read == 256)
{
try
{
string res = toString(serverreceivebuffer);
if(res == "@FileEnd@")
read = 0;
}
catch
{
}
}
if(read > 0)
{
serverfile.Write(serverreceivebuffer,0,read);
cli.GetStream().BeginRead(serverreceivebuffer,0,1024,ServerReadFileCyle,cli);
}
else
{
serverfile.Flush();
serverfile.Dispose();
AcceptServer();
}
}
}
这部分是客户端的服务器side.And;
发送文件时先向服务器发送文件信息,然后等待服务器响应。
try
{
System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
ofd.Multiselect = false;
ofd.FileName="";
if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
filesendpath = ofd.FileName;
senderfilestream = System.IO.File.Open(filesendpath,System.IO.FileMode.Open);
sendertotalbytes = senderfilestream.Length;
filesendcommand = "@File@" + System.IO.Path.GetFileName(filesendpath) + ";" + senderfilestream.Length;
senderfilestream.Position = 0;
sendertc.BeginConnect(ip.Address,55502,FileConnect,null);
}
else
{
//No file selected
}
}
catch(Exception ex)
{
//Error connecting log the error
}
如果连接成功,则发送文件命令,等待响应;
public void FileConnect(IAsyncResult ar)
{
if(ar.IsCompleted)
{
sender.EndConnect(ar);
if(sender.Connected)
{
sender.GetStream().Write(toByte(filesendcommand,256),0,256);
sender.GetStream().BeginRead(ComputerNameBuffer,0,256,FileSendCyleStarter,null);
}
}
}
收到响应后查看是否成功接受;
public void FileSendCyleStarter(IAsyncResult ar)
{
if(ar.IsCompleted)
{
if(sender.Connected)
{
string kabul = toString(ComputerNameBuffer);
if(kabul == "@FileAccepted@")
{
senderfilestream.BeginRead(filesendbuffer,0,1024,FileSendCyle,null);
}
}
}
}
发送文件分三步;
1-开始读取一个块
2-然后将块发送到 server.if 其完成的发送 @FileEnd@ 命令并跳过第 3 步
3-读取下一个文件块
4-Return 如果文件未完成则步骤 2
第 1 步:
senderfilestream.BeginRead(filesendbuffer,0,1024,FileSendCyle,null);
第 2-4 步:
public void FileSendCyle(IAsyncResult ar)
{
if(ar.IsCompleted)
{
if(sendertc.Connected)
{
int read = senderfilestream.EndRead(ar);
if(read > 0)
{
sendertc.GetStream().BeginWrite(filesendbuffer,0,read,FileSendCyle2,null);
}
else
{
sendertc.GetStream().Write(toByte("@FileEnd@",256),0,256);
}
}
}
}
第 3 步:
public void FileSendCyle2(IAsyncResult ar)
{
if(ar.IsCompleted)
{
if(sendertc.Connected)
{
sendertc.GetStream().EndWrite(ar);
senderfilestream.BeginRead(filesendbuffer,0,1024,FileSendCyle,null);
}
}
}
在上面的示例中,有两个方法称为 toString() 和 toByte()。我用它们将字符串转换为字节,然后将字节转换为 strings.Here;
public string toString(byte[] buffer)
{
return Encoding.UTF8.GetString(buffer).Replace("[=20=]","");
}
public byte[] toByte(string str,int bufferlenght)
{
byte[] buffer = new byte[256];
Encoding.UTF8.GetBytes(str,0,str.Length,buffer,0);
return buffer;
}
上面的示例代码并不完美,需要大量的错误处理和流程controls.I写这些来给你一个想法和一个快速的开始。
希望其中的任何部分对大家有所帮助^_^
我正在使用 TcpClient 和 TcpListener 类 制作一个 client/server 聊天应用程序。它应该能够在客户端和服务器之间传输文本消息和文件。我可以通过为每个单独的客户端创建一个线程,然后创建一个辅助线程来接收传入消息,从而使主线程保留用于发送消息来处理文本消息。现在,如果我能够识别传入消息是文件而不是文本消息,那么我就知道如何使用 NetworkStream 和 FileStream 来处理它。但我无法这样做。处理传入文件的代码是 here。另外请告诉我使用 NetworkStream 是否有任何限制 FTP.
答案:构建您自己的协议。
通过构建自己良好的通信协议,您可以控制所有 data/message 流。
例如;
1-用户想要发送文件到服务器
2-客户端发送命令通知服务器它将发送一个 file.Like ;
@File@filename;filesize;
3-服务器向客户端发送就绪消息 @FileAccepted@
4-Server 开始监听缓冲包,当它收到时将它们写入流。
5-当客户端收到 {@FileAccepted@}
命令时,开始向服务器发送包。确保它们的缓冲区大小相同。
6-当所有字节完成时,客户端在不同的缓冲区中发送 @FileEnd@
(我使用 256 用于命令,1024 用于文件传输)
7-当服务器接收到 256 字节命令时,查看它的 @FileEnd@
命令是否为真刷新文件流并关闭连接。
我建议你使用 Async
像这样监听服务器上的连接
server.BeginAcceptTcpClient(ServerAcceptEnd,server);
当连接存在时
public void ServerAcceptEnd(IAsyncResult ar)
{
if(!ar.IsCompleted)
{
//Something went wrong
AcceptServer();
return;
}
try
{
var cli = servertc.EndAcceptTcpClient(ar);
if(cli.Connected)
{
//Get the first Command
cli.GetStream().BeginRead(serverredbuffer,0,serverredbuffer.Length,ServerFirstReadEnd,cli);
}
else
{
//Connection was not successfull log and wait
AcceptServer();
}
}
catch(Exceiption ex)
{
//An error occur log and wait for new connections
AcceptServer();
}
}
收到第一个命令时;
public void ServerFirstReadEnd(IAsyncResult ar)
{
if(!ar.IsCompleted)
{
//Something went wrong
AcceptServer();
return;
}
try
{
TcpClient cli = (TcpClient)ar.AsyncState;
int read = cli.GetStream().EndRead(ar);
string req = toString(serverredbuffer);
if(req.StartsWith("@File@"))
{
//File Received
string filename = req.Replace("@File@","");
string[] spp = filename.Split(';');
filename = spp[0];
serverreceivetotalbytes = Convert.ToInt64(spp[1]);
cli.GetStream().Write(toByte("@FileAccepted@",256),0,256);
cli.GetStream().BeginRead(serverreceivebuffer,0,1024,ServerReadFileCyle,cli)
}
else
{
//Message Received
}
}
catch(Exception ex)
{
//An error occur log and wait for new connections
AcceptServer();
}
}
文件接收方法;
public void ServerReadFileCyle(IAsyncResult ar)
{
TcpClient cli = (TcpClient)ar.AsyncState;
if(ar.IsCompleted)
{
int read = cli.GetStream().EndRead(ar);
if(read == 256)
{
try
{
string res = toString(serverreceivebuffer);
if(res == "@FileEnd@")
read = 0;
}
catch
{
}
}
if(read > 0)
{
serverfile.Write(serverreceivebuffer,0,read);
cli.GetStream().BeginRead(serverreceivebuffer,0,1024,ServerReadFileCyle,cli);
}
else
{
serverfile.Flush();
serverfile.Dispose();
AcceptServer();
}
}
}
这部分是客户端的服务器side.And;
发送文件时先向服务器发送文件信息,然后等待服务器响应。
try
{
System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
ofd.Multiselect = false;
ofd.FileName="";
if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
filesendpath = ofd.FileName;
senderfilestream = System.IO.File.Open(filesendpath,System.IO.FileMode.Open);
sendertotalbytes = senderfilestream.Length;
filesendcommand = "@File@" + System.IO.Path.GetFileName(filesendpath) + ";" + senderfilestream.Length;
senderfilestream.Position = 0;
sendertc.BeginConnect(ip.Address,55502,FileConnect,null);
}
else
{
//No file selected
}
}
catch(Exception ex)
{
//Error connecting log the error
}
如果连接成功,则发送文件命令,等待响应;
public void FileConnect(IAsyncResult ar)
{
if(ar.IsCompleted)
{
sender.EndConnect(ar);
if(sender.Connected)
{
sender.GetStream().Write(toByte(filesendcommand,256),0,256);
sender.GetStream().BeginRead(ComputerNameBuffer,0,256,FileSendCyleStarter,null);
}
}
}
收到响应后查看是否成功接受;
public void FileSendCyleStarter(IAsyncResult ar)
{
if(ar.IsCompleted)
{
if(sender.Connected)
{
string kabul = toString(ComputerNameBuffer);
if(kabul == "@FileAccepted@")
{
senderfilestream.BeginRead(filesendbuffer,0,1024,FileSendCyle,null);
}
}
}
}
发送文件分三步;
1-开始读取一个块
2-然后将块发送到 server.if 其完成的发送 @FileEnd@ 命令并跳过第 3 步
3-读取下一个文件块
4-Return 如果文件未完成则步骤 2
第 1 步:
senderfilestream.BeginRead(filesendbuffer,0,1024,FileSendCyle,null);
第 2-4 步:
public void FileSendCyle(IAsyncResult ar)
{
if(ar.IsCompleted)
{
if(sendertc.Connected)
{
int read = senderfilestream.EndRead(ar);
if(read > 0)
{
sendertc.GetStream().BeginWrite(filesendbuffer,0,read,FileSendCyle2,null);
}
else
{
sendertc.GetStream().Write(toByte("@FileEnd@",256),0,256);
}
}
}
}
第 3 步:
public void FileSendCyle2(IAsyncResult ar)
{
if(ar.IsCompleted)
{
if(sendertc.Connected)
{
sendertc.GetStream().EndWrite(ar);
senderfilestream.BeginRead(filesendbuffer,0,1024,FileSendCyle,null);
}
}
}
在上面的示例中,有两个方法称为 toString() 和 toByte()。我用它们将字符串转换为字节,然后将字节转换为 strings.Here;
public string toString(byte[] buffer)
{
return Encoding.UTF8.GetString(buffer).Replace("[=20=]","");
}
public byte[] toByte(string str,int bufferlenght)
{
byte[] buffer = new byte[256];
Encoding.UTF8.GetBytes(str,0,str.Length,buffer,0);
return buffer;
}
上面的示例代码并不完美,需要大量的错误处理和流程controls.I写这些来给你一个想法和一个快速的开始。
希望其中的任何部分对大家有所帮助^_^