昨天,我用 Console.ReadLine() 做了一個很簡單的實驗。
我第一次真正理解到,程式「還活著」,不代表 CPU 必須一直空轉。
程式也可以停在某個地方,等待外部事件發生。
在 Console 程式裡,等待的是:
鍵盤輸入
但 Web Server 當然不可能靠Console.ReadLine(),來等 Browser。
所以新的問題就出現了:
Web Server 到底在等什麼?
Browser 的 Request 又是怎麼真正進入我的 C# 程式?
今天,我第一次讓自己的 Mini Web Framework 專案真正碰到網路。
如果 Web Server 要等待 Client,那它到底是在等待什麼?
以前聽到:
Server 正在監聽 Port 5000。
我其實只是把這句話背起來。
像 ASP.NET Core 常看到:
localhost:5000
我知道 5000 是 Port。
也知道 Browser 可以連進去。
但如果真的要我回答:
「你的 C# 程式到底怎麼知道有人連到 Port 5000?」
我其實回答不出來。
所以今天我不想直接使用 ASP.NET Core,我想先從最基本的 TCP Server 開始。
我先建立:
var listener = new TcpListener(IPAddress.Loopback, 5000);
接著啟動:
listener.Start();
並印出:
Console.WriteLine("Server 已啟動,正在等待連線...");
接著最重要的是:
TcpClient client = listener.AcceptTcpClient();
執行後,我看到:
Server 已啟動,正在等待連線...
然後程式就停在那裡。
一開始我差點以為程式卡住了。
但仔細想想,這其實正是我想要的結果。
因為:
AcceptTcpClient();
現在正在等待 Client 建立 TCP 連線。
這讓我第一次把昨天的概念接到網路:
Day9
Console.ReadLine()
↓
等待鍵盤輸入
今天變成:
Day10
AcceptTcpClient()
↓
等待網路 Client

IP 和 Port 到底在做什麼?
今天的 Server 建立在:
IPAddress.Loopback
以及:
Port 5000
Loopback 對應的就是:
127.0.0.1
也就是:
這台電腦自己,所以今天的 Client 和 Server 都在同一台電腦。
可以簡化成:
Client
│
│ 127.0.0.1:5000
▼
Server
而 Port 可以先理解成一個「入口編號」。
同一台電腦可能同時執行很多網路程式:
127.0.0.1:5000
127.0.0.1:5001
127.0.0.1:8080
Port 讓作業系統知道:
這個網路資料到底要交給哪一個程式。
所以:
IP
↓
找到哪一台電腦
Port
↓
找到這台電腦上的哪個服務
接著,我另外建立 Client 去連:
127.0.0.1:5000
當 Client 成功連線後,原本 Server 的畫面立刻出現:
收到一個連線!
這一刻其實很重要。
因為我第一次親眼看到:
Client
│
│ 建立 TCP 連線
▼
TcpListener
│
▼
AcceptTcpClient()
│
▼
收到一個連線
也就是說,Server 並不是一直「猜」有沒有人來,它可以停在等待連線的狀態,直到真的有 Client 連進來。
接著,我透過:
NetworkStream stream = client.GetStream();
取得網路資料流。
再使用:
int length = stream.Read(buffer, 0, buffer.Length);
讀取 Client 傳來的資料。
這裡又出現了一個有趣的現象。
Server 已經顯示:
收到一個連線!
但程式又停住了。
一開始我以為是不是出錯。
後來才發現:
建立連線和收到資料,其實是兩個不同階段。
流程其實是:
等待 Client
↓
Client 建立連線
↓
AcceptTcpClient() 完成
↓
等待資料
↓
stream.Read()
↓
收到資料
所以:
「有人連進來」
不代表:
「對方已經把資料送過來」
這是今天另一個很重要的發現。




Socket 到底是什麼?
做到這裡,我才開始理解 Socket 的角色。
以前看到 Socket,我總覺得它是一個很抽象的網路名詞。
但今天可以先把它理解成:
程式和網路世界溝通的一個端點。
Client 需要一個網路端點。
Server 也需要一個網路端點。
雙方透過 IP、Port 和 TCP 建立連線後,就可以開始交換資料。
可以簡化成:
Client Process
│
Socket
│
│ TCP
│
Socket
│
Server Process
而今天 C# 提供的:
TcpListener
和:
TcpClient
其實都是幫我們包裝底層 Socket 操作的類別。
所以今天我雖然沒有直接操作最底層的 Socket 類別,但已經第一次真正透過 TCP 建立 Client 與 Server 的連線。
官方怎麼說?
Microsoft 對 TcpListener 的描述,可以簡化理解為:
它提供監聽 TCP 網路 Client 連線的能力。
而:
AcceptTcpClient()
會等待傳入的 TCP 連線,成功後取得一個代表該連線的 TcpClient。
接著:
GetStream()
讓 Server 可以透過 NetworkStream 接收或傳送資料。
所以今天自己做出的流程:
Listen
↓
Accept
↓
GetStream
↓
Read
並不是我自己亂猜出來的流程,而是 TCP Server 實際工作的基本步驟。
昨天的我:
Web Server 可以一直活著,因為它會等待 Request。
今天的我:
Server 並不是直接等待「HTTP Request」這個抽象概念,它更底層會先等待網路連線,取得連線後,再從網路資料流中讀取 Client 傳來的 bytes。
也就是:
Client
↓
建立 TCP 連線
↓
Server Accept
↓
NetworkStream
↓
讀取 bytes
↓
才開始理解資料內容
我以前都是直接從:
HTTP Request 開始學。
今天才第一次知道,在 HTTP 出現以前,下面其實還有 TCP 和 Socket。
今天是 Mini Web Framework 第一次真正具備「網路能力」的一天。
前面的專案本質上都還只是:
Console Application
但今天它已經可以:
啟動
↓
監聽 Port 5000
↓
等待 Client
↓
接受 TCP 連線
↓
讀取 Client 傳來的資料
↓
繼續等待
雖然現在還沒有 Routing、Controller、Middleware,甚至還不懂 HTTP。
但它已經跨過非常重要的一步,它不再只是自己執行自己的程式,而是開始能接收外面的世界傳進來的資料。
這也是從普通程式走向 Web Server 的真正起點。