| 階段 | 內容 | 範例 |
| 命令 | 操作類型 | SENDTEXT, SENDFILE, EXECUTEFILE |
| 描述 | 參數/檔名 | 檔案路徑、執行參數 |
| 動態資料 | 實際承載 | 文字內容、檔案二進位流 |
客戶端發送端實作
通用執行緒啟動器
private void StartThread(ThreadStart target) {
var t = new Thread(target) { IsBackground = true };
t.Start();
}
進度回調(UI 更新)
public void OnSendProgress(int total, int sent) {
if (pbMain.Maximum != total) pbMain.Maximum = total;
pbMain.Value = sent;
}
發送文字
private void StartSendText(string host, int port, string text) {
var sender = new SendText(host, port, text, OnSendProgress);
StartThread(sender.Send);
}
public class SendText {
string Host, Info; int Port; CommunClass.OnSend OnSend;
public SendText(string h, int p, string i, CommunClass.OnSend cb) {
Host = h; Port = p; Info = i; OnSend = cb;
}
public void Send() {
Socket s = null;
try {
s = CommunClass.ConnectToServer(Host, Port);
CommunClass.WriteCommandToSocket(s, "SENDTEXT");
CommunClass.WriteCommandDescToSocket(s, "");
CommunClass.WriteDynamicTextToSocket(s, Info, OnSend);
} catch (Exception ex) { MessageBox.Show(ex.Message); }
finally { s?.Close(); }
}
}
發送檔案
private void StartSendFile(string host, int port, string file) {
var sender = new SendFile(host, port, file, pbMain);
pbMain.Value = 0;
StartThread(sender.Send);
}
public class SendFile {
string Host, FileToSend; int Port; ProgressBar Pbar;
public SendFile(string h, int p, string f, ProgressBar pb) {
Host = h; Port = p; FileToSend = f; Pbar = pb;
}
public void Send() {
Socket s = null;
try {
s = CommunClass.ConnectToServer(Host, Port);
CommunClass.WriteCommandToSocket(s, "SENDFILE");
CommunClass.WriteCommandDescToSocket(s, "");
CommunClass.WriteFileToSocket(s, FileToSend, new CommunClass.OnSend(OnSendProgress));
} catch (Exception ex) { MessageBox.Show(ex.Message); }
finally { s?.Close(); }
}
void OnSendProgress(int total, int sent) {
if (Pbar.Maximum != total) Pbar.Maximum = total;
Pbar.Value = sent;
}
}
遠端執行指令
public class ExecuteFile {
string Host, FileName, CmdParam; int Port;
public ExecuteFile(string h, int p, string fn, string cp) {
Host = h; Port = p; FileName = fn; CmdParam = cp;
}
public void Send() {
Socket s = null;
try {
s = CommunClass.ConnectToServer(Host, Port);
CommunClass.WriteCommandToSocket(s, "EXECUTEFILE");
CommunClass.WriteCommandDescToSocket(s, FileName);
CommunClass.WriteDynamicTextToSocket(s, CmdParam, null);
MessageBox.Show(CommunClass.ReadDynamicTextFromSocket(s));
} catch (Exception ex) { MessageBox.Show(ex.Message); }
finally { s?.Close(); }
}
}
伺服器端接收實作
監聽啟動
private TcpListener _listener;
private void BuildServer(string addr, int port) {
IPAddress ip = Dns.GetHostEntry(addr).AddressList[0];
_listener = new TcpListener(ip, port);
}
private void StartListen() {
_listener.Start();
while (true) {
Socket client = _listener.AcceptSocket();
if (client != null) {
var handler = new DealWithSocket(client, tbLog);
StartThread(handler.DealWith);
}
}
}
連線處理器
public class DealWithSocket {
Socket _socket; TextBox _log;
public DealWithSocket(Socket s, TextBox log) { _socket = s; _log = log; }
public void DealWith() {
string cmd = CommunClass.ReadCommandFromSocket(_socket);
string desc = CommunClass.ReadCommandDescFromSocket(_socket);
Log(cmd);
switch (cmd) {
case "SENDFILE":
CommunClass.ReadDynamicFileFromSocket(_socket, @"e:\rrr.txt");
break;
case "EXECUTEFILE":
string param = CommunClass.ReadDynamicTextFromSocket(_socket);
string result = ExecuteFile(desc, param);
CommunClass.WriteDynamicTextToSocket(_socket, result, null);
break;
default:
string detail = CommunClass.ReadDynamicTextFromSocket(_socket);
Log(detail);
break;
}
try { _socket.Close(); } catch { Log("Close error"); }
}
string ExecuteFile(string file, string args) {
var proc = new Process {
StartInfo = new ProcessStartInfo(file, args)
};
try { proc.Start(); return "OK"; }
catch (Exception ex) { return ex.Message; }
}
void Log(string msg) {
string line = $"{DateTime.Now:HH:mm:ss} {msg}\r\n";
_log.Invoke((Action)(() => { _log.Text += line; _log.Refresh(); }));
}
}
CommunClass 關鍵方法(假設實作)
public static class CommunClass {
public delegate void OnSend(int total, int sent);
public static Socket ConnectToServer(string host, int port) {
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sock.Connect(host, port);
return sock;
}
// 寫入命令(固定長度或分隔符)
public static void WriteCommandToSocket(Socket s, string cmd) { ... }
public static string ReadCommandFromSocket(Socket s) { ... }
public static void WriteCommandDescToSocket(Socket s, string desc) { ... }
public static string ReadCommandDescFromSocket(Socket s) { ... }
// 動態文字:長度前綴 + 內容
public static void WriteDynamicTextToSocket(Socket s, string text, OnSend progress) { ... }
public static string ReadDynamicTextFromSocket(Socket s) { ... }
// 檔案傳輸:檔案大小 + 分塊傳輸 + 進度回調
public static void WriteFileToSocket(Socket s, string file, OnSend progress) { ... }
public static void ReadDynamicFileFromSocket(Socket s, string savePath) { ... }
}
關鍵技術點與改進方向
優點
- 協定清晰,擴充新命令容易
- 執行緒隔離,單一連線錯誤不影響其他
- 進度回調提供良好 UX
- 檔案/文字/指令統一框架
缺點與風險
| 議題 | 風險 | 改進 |
| 每連線一執行緒 | 高併發時執行緒耗盡 | 改用 `SocketAsyncEventArgs` / `async-await` / IOCP |
| 無加密傳輸 | 資料竊聽、竄改 | 加入 TLS (SslStream) 或應用層加密 |
| 無驗證授權 | 任意連線可執行指令(後門風險) | 加入 Token/憑證驗證、命令白名單 |
| 檔案傳輸無斷點續傳 | 大檔中斷需重傳 | 支援 Range/Offset 斷點續傳 |
| UI 跨執行緒直接存取 | 可能例外 | 統一用 `Control.Invoke` / `SynchronizationContext` |
| 緩衝區大小固定 1024 | 效能非最佳 | 動態調整或使用 `NetworkStream` |
現代化重構建議
// 非同步接受
_listener.Start();
while (true) {
Socket client = await _listener.AcceptSocketAsync();
_ = HandleClientAsync(client); // Fire-and-forget
}
async Task HandleClientAsync(Socket client) {
using var stream = new NetworkStream(client, true);
using var reader = new BinaryReader(stream);
using var writer = new BinaryWriter(stream);
string cmd = reader.ReadString();
// ... 協定解析
}
總結
此實作展示了 Socket 程式設計核心概念:連線建立、協定設計、資料序列化、多執行緒並發、進度報告。雖架構屬傳統阻塞模型,但邏輯清晰適合教學與中小規模應用。生產系統建議遷移至非同步 IO、加入安全性層、使用成熟框架(SignalR、gRPC、WebSocket)替代原始 Socket。
- 上一篇文章: C# 資料存取通用類別庫:ADO.NET 封裝、連線管理與交易支援
- 下一篇文章: C# TreeView 完整操作指南(一):資料綁定、拖曳排序與資料庫同步
Copyright ©
程式碼之家 All Rights Reserved