c# socket 傳輸文件

 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();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章