c# socket 传输文件

c# socket 传输文件
2009-05-03 13:38

int port = 1234;

int port = 1234;
         IPAddress ip = IPAddress.Parse("127.0.0.1");
         socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
         IPEndPoint iep = new IPEndPoint(ip,port);
         //socket.Blocking = false;
         socket.Bind(iep);
         socket.Listen(10);
         Console.WriteLine("start......");
         try
         {
            for (int i = 0; i < 10;i++ )
            {
                 
            }
         }
         catch
         {
            Console.WriteLine("异常!");
            socket.Close();
         }

接收端
private void Receive(Socket socket)
{
NetworkStream ns = new NetworkStream(socket);
FileStream fs = new FileStream("c:\\file.txt", FileMode.OpenOrCreate);
bool isRead = true;
while (isRead)
{
       int count = ns.Read(this._receiveBuf, 0, this._receiveBuf.Length);
       int datanum = 0;
       datanum = BitConverter.ToInt32(this._receiveBuf, 0);   //从buffer中的前4个字节读出count
       if (datanum > 0)                                      //确定每次要接受多少字节数
       {
         fs.Write(this._receiveBuf, 4, datanum);
       }
       else                            //如果接受字节数为0 就推出
       {
         isRead = false;
       }
}
this.txtFile.Text = "文件传输成功";
fs.Close();
}
---------------------
发送端
private void btSend_Click(object sender, System.EventArgs e)
{
if (this._isConnect)
{
       _ns = _tc.GetStream();
       string path = this.txtPath.Text.Trim();
       FileStream fs = new FileStream(path, FileMode.Open);
       int sendCount = 0;
       byte[] countbuffer = null;
       byte[] clientbuffer = new byte[1004];
       while (sendCount < fs.Length && _ns.CanWrite)
       {
         int count = fs.Read(_sendBuf, 0, _sendBuf.Length);   //读出要发送的数据
         countbuffer = BitConverter.GetBytes(count);
         countbuffer.CopyTo(clientbuffer,0);
         _sendBuf.CopyTo(clientbuffer, 4);            
         this._ns.Write(clientbuffer, 0, 4 + count);       //写入网络流
         sendCount += count;
       }
       countbuffer = BitConverter.GetBytes(0);             //发送完文件后 发送count = 0
       this._ns.Write(countbuffer, 0, countbuffer.Length); //使接收端停止
       _ns.Close();
       fs.Close();
}
}

你为什么不把这两种方案结合在一起呢?
首先把文件的总长度和每次发送的大小先发送出去,等接收端接受并分析,然后开始。
比如每次发送4K(这是操作系统文件管理中使用到的最小文件大小,你可以看看你系统中的任何一个文件,占用空间都是4K的整数倍),
最后一次可能会少与4K,但是接受方是可以计算出来的。
必要时,你可以使用多线程,分段发送,接收端收集后分段组合,这还要多使用一个段号码。
socket是最底层的类,传输效率最高!
对于你说的异步操作,一句话说不清楚,基本上可以用“非阻塞模型”来概括,就是调用后立马返回,不是等到操作完成后才返回!
打个比方:阻塞模型
while(isok)
{
readdata(data);//从文件读数据
send(data); //一直等到data发送完毕后才返回,其实这期间本来可以进行下一次读操作
             //影响了效率。
if(读完)
isok=false;
else
isok=true;
}
非阻塞模型,可以在发送过程中进行读取操作,提高了效率。
当然,在第二次发送前,必须等待第一次发送操作完成才行,需要检测和控制!

while (sendCount   < fs.Length && _ns.CanWrite)
       {
         int count = fs.Read(_sendBuf, 0, _sendBuf.Length);   //读出要发送的数据
         countbuffer = BitConverter.GetBytes(count);
         countbuffer.CopyTo(clientbuffer,0);
         _sendBuf.CopyTo(clientbuffer, 4);            
         this._ns.Write(clientbuffer, 0, 4 + count);       //写入网络流
         sendCount += count;
       }

有点乱:你每次读取1000还是1004??不是前四个字节是长度吗?为什么从文件里读取1004个字节啊?

BeginReceiveFrom 方法启动从远程主机异步读取无连接数据报的操作。调用 BeginReceiveFrom 方法将使您能够在单独的执行线程中接收数据。

您可以创建一个实现 AsyncCallback 委托的回调方法并将它的名称传递给 BeginReceiveFrom 方法。为此,您的 state 参数至少必须包含用于通信的已连接或默认 Socket。如果您的回调需要更多信息,则可以创建一个小型类来保存 Socket 和其他必需的信息。通过 state 参数将此类的一个实例传递给 BeginReceiveFrom 方法。

回调方法应调用 EndReceiveFrom 方法。当应用程序调用 BeginReceiveFrom 时,系统将会使用单独的线程来执行指定的回调方法,并将在 EndReceiveFrom 上一直阻止到 Socket 读取数据或引发异常为止。如果想要在调用 BeginReceiveFrom 方法后使原始线程阻止,请使用 WaitHandle.WaitOne。当需要原始线程继续执行时,请在回调方法中调用 T:System.Threading.ManualResetEvent 的 Set 方法。有关如何编写 callback 方法的其他信息,请参见 Callback 示例。

注意
在调用 BeginReceiveFrom 之前,必须使用 Bind 方法显式地将 Socket 绑定到本地终结点,否则 BeginReceiveFrom 将会引发 SocketException。


该方法将数据读入 buffer 参数中,并捕获从其发送数据的远程主机终结点。有关如何检索此终结点的信息,请参考 EndReceiveFrom。如果打算从未知主机或多个主机异步接收无连接的数据报,则最适合使用此方法。在这些情况下, BeginReceiveFrom 将会读取本地网络缓冲区接收到的第一个排队数据报。如果您接收到的数据报大于 buffer 的大小,则 BeginReceiveFrom 方法将在 buffer 中尽可能多地填充消息内容,并引发 SocketException。如果您使用的是不可靠协议,多余的数据将会丢失。而如果当前使用的是可靠协议,则服务提供程序将保留多余的数据,而且通 过使用一个足够大的缓冲区调用 BeginReceiveFrom 方法来检索这些数据。

虽然 BeginReceiveFrom 是用于无连接协议的,但您同样可以使用面向连接的协议。如果选择这样做,则必须通过调用 Connect / BeginConnect 方法来建立远程主机连接,或者调用 Accept 或 BeginAccept 方法来接受传入的连接请求。如果在建立连接或接受连接之前就调用了 BeginReceiveFrom 方法,则您将得到 SocketException。您也可以在调用 BeginReceiveFrom 方法之前,为无连接协议建立默认远程主机。在上述任何一种情况下,BeginReceiveFrom 方法都会忽略 remoteEP 参数,并且只从已连接的或默认的远程主机接收数据。

对于面向连接的套接字,BeginReceiveFrom 将读取所有可用的数据,直到达到 size 参数所指定的字节数。

若要取消挂起的 BeginReceiveFrom,请调用 Close 方法。

下面的代码示例异步接收来自远程主机的无连接数据报。

BeginReceiveFrom 方法启动从远程主机异步读取无连接数据报的操作。调用 BeginReceiveFrom 方法将使您能够在单独的执行线程中接收数据。

您可以创建一个实现 AsyncCallback 委托的回调方法并将它的名称传递给 BeginReceiveFrom 方法。为此,您的 state 参数至少必须包含用于通信的已连接或默认 Socket。如果您的回调需要更多信息,则可以创建一个小型类来保存 Socket 和其他必需的信息。通过 state 参数将此类的一个实例传递给 BeginReceiveFrom 方法。

回调方法应调用 EndReceiveFrom 方法。当应用程序调用 BeginReceiveFrom 时,系统将会使用单独的线程来执行指定的回调方法,并将在 EndReceiveFrom 上一直阻止到 Socket 读取数据或引发异常为止。如果想要在调用 BeginReceiveFrom 方法后使原始线程阻止,请使用 WaitHandle.WaitOne。当需要原始线程继续执行时,请在回调方法中调用 T:System.Threading.ManualResetEvent 的 Set 方法。有关如何编写 callback 方法的其他信息,请参见 Callback 示例。

注意
在调用 BeginReceiveFrom 之前,必须使用 Bind 方法显式地将 Socket 绑定到本地终结点,否则 BeginReceiveFrom 将会引发 SocketException。


该方法将数据读入 buffer 参数中,并捕获从其发送数据的远程主机终结点。有关如何检索此终结点的信息,请参考 EndReceiveFrom。如果打算从未知主机或多个主机异步接收无连接的数据报,则最适合使用此方法。在这些情况下, BeginReceiveFrom 将会读取本地网络缓冲区接收到的第一个排队数据报。如果您接收到的数据报大于 buffer 的大小,则 BeginReceiveFrom 方法将在 buffer 中尽可能多地填充消息内容,并引发 SocketException。如果您使用的是不可靠协议,多余的数据将会丢失。而如果当前使用的是可靠协议,则服务提供程序将保留多余的数据,而且通 过使用一个足够大的缓冲区调用 BeginReceiveFrom 方法来检索这些数据。

虽然 BeginReceiveFrom 是用于无连接协议的,但您同样可以使用面向连接的协议。如果选择这样做,则必须通过调用 Connect / BeginConnect 方法来建立远程主机连接,或者调用 Accept 或 BeginAccept 方法来接受传入的连接请求。如果在建立连接或接受连接之前就调用了 BeginReceiveFrom 方法,则您将得到 SocketException。您也可以在调用 BeginReceiveFrom 方法之前,为无连接协议建立默认远程主机。在上述任何一种情况下,BeginReceiveFrom 方法都会忽略 remoteEP 参数,并且只从已连接的或默认的远程主机接收数据。

对于面向连接的套接字,BeginReceiveFrom 将读取所有可用的数据,直到达到 size 参数所指定的字节数。

若要取消挂起的 BeginReceiveFrom,请调用 Close 方法。

下面的代码示例异步接收来自远程主机的无连接数据报。

C# code
IPHostEntry lipa = Dns.Resolve("host.contoso.com");
IPEndPoint lep = new IPEndPoint(lipa.AddressList[0], 11000);

Socket s = new Socket(lep.Address.AddressFamily,
                              SocketType.Dgram,
                                 ProtocolType.Udp);

IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;
s.Connect(sender);

try{
       while(true){
         allDone.Reset();
         StateObject so2 = new StateObject();
         so2.workSocket = s;
         Console.WriteLine("Attempting to Receive data from host.contoso.com");
     
         s.BeginReceiveFrom(so2.buffer, 0, StateObject.BUFFER_SIZE,0, ref tempRemoteEP,
                                  new AsyncCallback(Async_Send_Receive.ReceiveFrom_Callback), so2);
         allDone.WaitOne();
       }
}
catch (Exception e){
       Console.WriteLine(e.ToString());
}

在端口 11000 上建立 UdpClient 连接。将很短的字符串消息发送到两个单独的远程主机。Receive 方法在接收消息前阻止执行。使用传递给 Receive 的 IPEndPoint 可以显示响应主机的标识。

C# code
// This constructor arbitrarily assigns the local port number.
UdpClient udpClient = new UdpClient(11000);
try{
      udpClient.Connect("www.contoso.com", 11000);

      // Sends a message to the host to which you have connected.
      Byte[] sendBytes = Encoding.ASCII.GetBytes("Is anybody there?");
  
      udpClient.Send(sendBytes, sendBytes.Length);

      // Sends a message to a different host using optional hostname and port parameters.
      UdpClient udpClientB = new UdpClient();
      udpClientB.Send(sendBytes, sendBytes.Length, "AlternateHostMachineName", 11000);

      //IPEndPoint object will allow us to read datagrams sent from any source.
      IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);

      // Blocks until a message returns on this socket from a remote host.
      Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
      string returnData = Encoding.ASCII.GetString(receiveBytes);

      // Uses the IPEndPoint object to determine which of these two hosts responded.
      Console.WriteLine("This is the message you received " +
                                     returnData.ToString());
      Console.WriteLine("This message was sent from " +
                                 RemoteIpEndPoint.Address.ToString() +
                                 " on their port number " +
                                 RemoteIpEndPoint.Port.ToString());

      udpClient.Close();
      udpClientB.Close();
     
      } 
   catch (Exception e ) {
               Console.WriteLine(e.ToString());
       }

另外如果你想保证传输的质量的话,用TCP/IP协议吧,它的优点是可以控制传输中的质量(降低出错率),缺点是效率稍微低点。
TcpClient 类提供了一些简单的方法,用于在同步阻止模式下通过网络来连接、发送和接收流数据。

为使 TcpClient 连接并交换数据,使用 TCP ProtocolType 创建的 TcpListener 或 Socket 必须侦听是否有传入的连接请求。可以使用下面两种方法之一连接到该侦听器:

创建一个 TcpClient,并调用三个可用的 Connect 方法之一。

使用远程主机的主机名和端口号创建 TcpClient。此构造函数将自动尝试一个连接。

注意
如果要在同步阻止模式下发送无连接数据报,请使用 UdpClient 类。


给继承者的说明 要发送和接收数据,请使用 GetStream 方法来获取一个 NetworkStream。调用 NetworkStream 的 Write 和 Read 方法与远程主机之间发送和接收数据。使用 Close 方法释放与 TcpClient 关联的所有资源。

下面的代码示例建立 TcpClient 连接。

C# code
static void Connect(String server, String message)
{
   try
   {
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
Int32 port = 13000;
TcpClient client = new TcpClient(server, port);

// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);      

// Get a client stream for reading and writing.
//   Stream stream = client.GetStream();

NetworkStream stream = client.GetStream();

// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);

Console.WriteLine("Sent: {0}", message);      

// Receive the TcpServer.response.

// Buffer to store the response bytes.
data = new Byte[256];

// String to store the response ASCII representation.
String responseData = String.Empty;

// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}", responseData);      

// Close everything.
stream.Close();      
client.Close();      
   }
   catch (ArgumentNullException e)
   {
Console.WriteLine("ArgumentNullException: {0}", e);
   }
   catch (SocketException e)
   {
Console.WriteLine("SocketException: {0}", e);
   }

   Console.WriteLine("\n Press Enter to continue...");
   Console.Read();
}

你可能感兴趣的:(c# socket 传输文件)