程式執行流程與業務邏輯流程常不一致。以同步 Socket 讀取 HTTP 請求為例,巢狀迴圈與狀態分散導致代碼難維護。本文探討如何將邏輯流程顯式建模,提升可讀性與擴充性。
void read(HTTP_REQUEST& req) {
read(req.header);
read(req.body, req.header);
}
void read(HTTP_REQUEST_HEADER& hdr) {
string line = read_line();
parse_request_line(line, hdr.method, hdr.uri, hdr.version);
while (true) {
line = read_line();
if (line.empty()) break;
parse_header_field(line, hdr);
}
}
void read(BYTE*& body, HTTP_REQUEST_HEADER& hdr) {
string te = hdr.fields["Transfer-Encoding"];
if (te != "chunked")
body = read_bytes(hdr.fields["Content-Length"]);
else {
while (true) {
string chunk_hdr = read_line();
DWORD sz = atoi(chunk_hdr.c_str());
if (sz == 0) break;
BYTE* chunk = read_bytes(sz);
body = append(body, chunk, sz);
}
}
}
// 底層讀取緩衝...
缺點:控制流分散、錯誤處理分散、難以單元測試、阻塞執行緒。
定義枚舉狀態,每個狀態對應一個處理函式,主迴圈驅動狀態轉移:
enum class HttpState { RequestLine, Headers, BodyLength, BodyChunked, ChunkHeader, ChunkData, Done, Error };
class HttpParser {
HttpState state = HttpState::RequestLine;
Buffer buffer;
HTTP_REQUEST req;
public:
bool feed(const char* data, size_t len) { /* 追加緩衝 */ }
bool process() {
while (state != HttpState::Done && state != HttpState::Error) {
switch (state) {
case HttpState::RequestLine: return parseRequestLine();
case HttpState::Headers: return parseHeaders();
case HttpState::BodyLength: return readBodyLength();
case HttpState::BodyChunked: return readChunkedBody();
// ...
}
}
return state == HttpState::Done;
}
};
優點:邏輯集中、易測試、可中斷恢復、支援非同步事件驅動。
C++20 協同程式可將非同步邏輯寫成同步風格:
#include <coroutine>
task<HTTP_REQUEST> read_http_request(AsyncSocket& sock) {
auto header = co_await read_header(sock);
auto body = co_await read_body(sock, header);
co_return HTTP_REQUEST{header, body};
}
編譯器自動生成狀態機,代碼線性、無回呼地獄。需支援 C++20 協同程式的編譯器(MSVC /std:c++20, GCC -fcoroutines, Clang -fcoroutines-ts)。
產業級非同步 I/O 框架,以完成處理器回呼或 C++20 awaitable 形式表達邏輯流程,自動管理緩衝、超時、取消、背壓等。
| 方案 | 適用場景 | 學習曲線 |
|---|---|---|
| 手寫狀態機 | 簡單協議、嵌入式、無 C++20 環境 | 低 |
| C++20 協同程式 | 現代專案、複雜非同步邏輯、團隊接受新標準 | 中 |
| Boost.Asio | 高效能網路服務、成熟生態、長期維護專案 | 中高 |
引入邏輯流程控制,是從「能跑」邁向「好維護、好擴充、好測試」的關鍵步驟。