得益於 RemoteClient 建構函式已完成初始化,主程式僅需建立 TcpListener、進入迴圈接受連線、新增包裝物件即可:
class Server {
static void Main(string[] args) {
Console.WriteLine("Server is running ... ");
IPAddress ip = new IPAddress(new byte[] { 127, 0, 0, 1 });
TcpListener listener = new TcpListener(ip, 8500);
listener.Start();
Console.WriteLine("Start Listening ...");
while (true) {
TcpClient client = listener.AcceptTcpClient(); // 同步阻塞取得連線
RemoteClient wrapper = new RemoteClient(client);
}
}
}
為方便使用,將 TcpClient 包裝為 ServerClient,內含緩衝區、資料流、非同步讀取回調等完整邏輯:
public class ServerClient {
private const int BufferSize = 8192;
private byte[] buffer;
private TcpClient client;
private NetworkStream streamToServer;
private string msg = "Welcome to TraceFact.Net!";
public ServerClient() {
try {
client = new TcpClient();
client.Connect("localhost", 8500);
} catch (Exception ex) {
Console.WriteLine(ex.Message);
return;
}
buffer = new byte[BufferSize];
Console.WriteLine("Server Connected!{0} --> {1}",
client.Client.LocalEndPoint, client.Client.RemoteEndPoint);
streamToServer = client.GetStream();
}
// 發送訊息(含協議長度前綴)
public void SendMessage(string msg) {
msg = String.Format("[length={0}]{1}", msg.Length, msg);
for (int i = 0; i <= 2; i++) {
byte[] temp = Encoding.Unicode.GetBytes(msg);
try {
streamToServer.Write(temp, 0, temp.Length);
Console.WriteLine("Sent: {0}", msg);
} catch (Exception ex) {
Console.WriteLine(ex.Message);
break;
}
}
// 發送完成後啟動非同步讀取
lock (streamToServer) {
AsyncCallback callback = new AsyncCallback(ReadComplete);
streamToServer.BeginRead(buffer, 0, BufferSize, callback, null);
}
}
public void SendMessage() { SendMessage(this.msg); }
// 非同步讀取完成回調
private void ReadComplete(IAsyncResult ar) {
int bytesRead;
try {
lock (streamToServer) {
bytesRead = streamToServer.EndRead(ar);
}
if (bytesRead == 0) throw new Exception("讀取到 0 位元組");
string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);
Console.WriteLine("Received: {0}", msg);
Array.Clear(buffer, 0, buffer.Length); // 清除髒讀
// 繼續下一輪讀取
lock (streamToServer) {
AsyncCallback callback = new AsyncCallback(ReadComplete);
streamToServer.BeginRead(buffer, 0, BufferSize, callback, null);
}
} catch (Exception ex) {
if (streamToServer != null) streamToServer.Dispose();
client.Close();
Console.WriteLine(ex.Message);
}
}
}
重點:SendMessage 中對 streamToServer 加鎖,因 SendFile 為多執行緒存取且會呼叫 SendMessage;SendStatus 類別獨立負責顯示傳送進度,符合單一職責原則。