[英]TCP Client Connection
我有一個為整個應用程序編寫的應用程序,該應用程序分布在整個公司中,以通過Windows 2003服務器(運行IIS 6.0)向我發送數據。 小型文本消息通過,但是包含更多數據(大約20 KB)的較大消息沒有通過。
我將字節緩沖區設置為TCP客戶端的緩沖區大小。 我注意到服務器上已收到我的數據。 但是,它僅循環執行一次接收例程,而且我的大文件始終與緩沖區大小完全相同,即服務器上的8 KB。 換句話說,我的代碼只能在服務器關閉套接字連接之前經過一個循環。
考慮到填充整個緩沖區可能存在問題,我嘗試將讀取/寫入限制為僅1 KB,但這僅導致服務器在關閉連接之前收到1 KB后關閉了套接字。
我將服務器的錯誤消息發送回客戶端,以便可以查看。 我從客戶端收到的特定錯誤消息是:
“無法將數據寫入傳輸連接:已建立的連接已被主機中的軟件中止。”
我更新了服務器應用程序,以便基礎TCP套接字在此行中使用“保持活動”:
client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, true);
現在,每當我嘗試發送消息時,客戶端都會收到錯誤:
“無法將數據寫入傳輸連接:遠程主機強行關閉了現有連接。”
我們的網絡管理員告訴我,他的防火牆在內部服務器上沒有防火牆或任何端口。
搜尋錯誤,發現帖子暗示人們嘗試通過telnet進入服務器。 我使用了他們的指令將telnet接入服務器,但是不確定響應如何:
C:> telnet歡迎使用Microsoft Telnet客戶端
轉義字符為'CTRL +]'
Microsoft Telnet>打開cpapp 500連接到cpapp…
這就是我所得到的。 我從沒有收到錯誤,Microsoft的Telnet屏幕最終將變為“按任意鍵繼續...” –我想它超時了,但是我的代碼可以連接。
我已經嘗試通過代碼和Telnet嘗試使用其他端口,包括25、80和8080。Telnet踢出了端口25,但是無論我告訴它運行哪個端口,我的應用程序似乎都讀取了第一個循環。
這是我在客戶端上運行的代碼:
int sendUsingTcp(string location) {
string result = string.Empty;
try {
using (FileStream fs = new FileStream(location, FileMode.Open, FileAccess.Read)) {
using (TcpClient client = new TcpClient(GetHostIP, CpAppDatabase.ServerPortNumber)) {
byte[] riteBuf = new byte[client.SendBufferSize];
byte[] readBuf = new byte[client.ReceiveBufferSize];
using (NetworkStream ns = client.GetStream()) {
if ((ns.CanRead == true) && (ns.CanWrite == true)) {
int len;
string AOK = string.Empty;
do {
len = fs.Read(riteBuf, 0, riteBuf.Length);
ns.Write(riteBuf, 0, len);
int nsRsvp = ns.Read(readBuf, 0, readBuf.Length);
AOK = Encoding.ASCII.GetString(readBuf, 0, nsRsvp);
} while ((len == riteBuf.Length) && (-1 < AOK.IndexOf("AOK")));
result = AOK;
return 1;
}
return 0;
}
}
}
} catch (Exception err) {
Logger.LogError("Send()", err);
MessageBox.Show(err.Message, "Message Failed", MessageBoxButtons.OK, MessageBoxIcon.Hand, 0);
return -1;
}
}
這是我在服務器上運行的代碼:
SvrForm.Server = new TcpListener(IPAddress.Any, CpAppDatabase.ServerPortNumber);
void Worker_Engine(object sender, DoWorkEventArgs e) {
BackgroundWorker worker = sender as BackgroundWorker;
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Application.CompanyName);
if (Directory.Exists(path) == false) Directory.CreateDirectory(path);
Thread.Sleep(0);
string eMsg = string.Empty;
try {
SvrForm.Server.Start();
do {
using (TcpClient client = SvrForm.Server.AcceptTcpClient()) { // waits until data is avaiable
if (worker.CancellationPending == true) return;
client.Client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, true);
string location = Path.Combine(path, string.Format("Acp{0:yyyyMMddHHmmssff}.bin", DateTime.Now));
byte[] buf = new byte[client.ReceiveBufferSize];
try {
using (NetworkStream ns = client.GetStream()) {
if ((ns.CanRead == true) && (ns.CanWrite == true)) {
try {
int len;
byte[] AOK = Encoding.ASCII.GetBytes("AOK");
using (FileStream fs = new FileStream(location, FileMode.Create, FileAccess.Write)) {
do {
len = ns.Read(buf, 0, client.ReceiveBufferSize);
fs.Write(buf, 0, len);
ns.Write(AOK, 0, AOK.Length);
} while ((0 < len) && (ns.DataAvailable == true));
}
byte[] okBuf = Encoding.ASCII.GetBytes("Message Received on Server");
ns.Write(okBuf, 0, okBuf.Length);
} catch (Exception err) {
Global.LogError("ServerForm.cs - Worker_Engine(DoWorkEvent)", err);
byte[] errBuf = Encoding.ASCII.GetBytes(err.Message);
ns.Write(errBuf, 0, errBuf.Length);
}
}
}
}
worker.ReportProgress(1, location);
}
} while (worker.CancellationPending == false);
} catch (SocketException) {
// See MSDN: Windows Sockets V2 API Error Code Documentation for detailed description of error code
e.Cancel = true;
} catch (Exception err) {
eMsg = "Worker General Error:\r\n" + err.Message;
e.Cancel = true;
e.Result = err;
} finally {
SvrForm.Server.Stop();
}
}
為什么我的應用程序不繼續從TCP客戶端讀取? 我是否忽略了一些設置,以使Socket保持打開狀態直到完成? 服務器代碼永遠不會出現異常,因為TCP客戶端永遠不會停止,因此我知道沒有錯誤。
我們的網絡管理員尚未獲得他的副學士學位,因此,如果發現這與服務器有關,請在您如何解決此問題的說明中進行詳細說明,因為我們可能無法理解您的意思。
對於這么長時間,我深表歉意,但我想確保你們那里的人們知道我在做什么-甚至可以從我的技術中獲取一些信息!
感謝您的幫助! 〜喬
您應該在發送的內容之前加上該內容的長度。 您的循環假定所有數據都在循環執行之前發送,而實際上您的循環是在發送數據時執行的。 有時,線路上沒有數據等待,因此循環終止。 同時,內容仍通過網絡發送。 這就是循環僅運行一次的原因。
如果我正確地閱讀了您的代碼,那么您基本上已經獲得(對不起c樣式-我對c#不好:
do { socket = accept(); read(socket, buffer); }while(not_done);
如果我是正確的,那意味着您需要……還有更多。 如果要序列化,按順序讀取每個上載,則需要第二個循環:
do { socket = accept(); do { read(socket, buffer); not_done_reading=...; } while (not_done_reading); }while(not_done);
如果您想同時閱讀多個上載,則需要更多類似的內容:
do { socket = accept(); if( !fork() ) { do { read(socket, buffer); not_done_reading=...; } while (not_done_reading); } }while(not_done);
您的telnet示例與您描述的代碼的行為有些矛盾-如果您能夠在服務器上獲取任何內容,則“ telnet <主機名> <端口號>”應該可以使您很快進入黑屏(在Windows上在CMD提示符下顯示)。 因此,這是第一個奇怪的事情-最好使用Wireshark進行調試。
在代碼方面,我認為服務器上的此內線可能有問題:
...而(((0 <len)&&(ns.DataAvailable == true));
您說您想在能夠讀取某些內容並且有一些可用數據時循環播放。
但是,可能第二段尚未到達服務器,因此尚無可用數據,因此您將退出此循環。
當您正在讀取內容且沒有任何讀取錯誤時,應該循環接收數據-這樣可以保證即使在慢速鏈接上,您也可以可靠地接收數據。
旁注:
我注意到您的協議是請求-響應-請求-響應類型。 它在LAN上運行良好,但是,如果將來您需要使其在高往返時間鏈路上工作,這將成為很大的性能瓶頸(用於文件傳輸或TFTP的MS SMB協議以這種方式工作) 。
(免責聲明:我沒有用C#編寫太多代碼,因此在解釋“ DataAvailable()”方法時可能是錯誤的,請使用此FWIW)。
編輯:也許我上面的答案需要根據您的協議進行更正-即您需要先讀取文件的長度,然后再讀取文件-因為如果逐字閱讀,它將破壞您設計文件的方式完全。
也就是說,對於TCP,永遠不要假設發送方的write()操作數與接收方的read()操作數相同-在某些情況下,情況可能是這樣(沒有數據包丟失,沒有Nagle )-但通常情況下並非如此。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.