連線建立後,雙方透過 資料流 (Stream) 傳輸資料。幾乎任何物件都可序列化存入流中,因此實際上可以在客戶端與伺服器之間傳輸任意類型的資料。
本節實作一個完整來回流程:
我們分兩部分講解:1. 客戶端發送、伺服器接收並輸出;2. 伺服器回發、客戶端接收並輸出。
在 TcpClient 上呼叫 GetStream() 取得連往遠端的資料流。注意「遠端」一詞:在客戶端呼叫時取得連往伺服器的流;在伺服器呼叫時取得連往客戶端的流。以下為伺服器核心程式碼(未使用 do/while 迴圈):
class Server {
static void Main(string[] args) {
const int BufferSize = 8192; // 緩衝區 8192 位元組
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 ...");
// 同步阻塞,等待客戶端連線
TcpClient remoteClient = listener.AcceptTcpClient();
Console.WriteLine("Client Connected!{0} <-- {1}",
remoteClient.Client.LocalEndPoint, remoteClient.Client.RemoteEndPoint);
// 取得流並讀取資料
NetworkStream streamToClient = remoteClient.GetStream();
byte[] buffer = new byte[BufferSize];
int bytesRead = streamToClient.Read(buffer, 0, BufferSize);
Console.WriteLine("Reading data, {0} bytes ...", bytesRead);
// 依實際讀取長度解碼字串
string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);
Console.WriteLine("Received: {0}", msg);
// 按 Q 退出
}
}
關鍵點:AcceptTcpClient() 會阻塞直到有客戶端連線;Read() 同樣阻塞直到有資料到達。這就是同步模式的特徵——簡單直觀,但無法同時處理多個客戶端。