基於 Winsock 2 的 TCP 檔案傳輸範例,支援目錄列舉、檔案下載、進度顯示與多執行緒併發。程式碼針對 VC++ 6.0 撰寫,現代 Visual Studio 亦可編譯。
listen → accept → 建立執行緒處理客戶端 → 指令分派dir/get/help/exit)quit 標記傳輸結束#pragma comment(lib, "Ws2_32.lib")
#define PORT 8088
#define BUFSZ 4096
SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in addr = {AF_INET, htons(PORT), INADDR_ANY};
bind(listen_sock, (SOCKADDR*)&addr, sizeof(addr));
listen(listen_sock, 10);
DWORD WINAPI ClientThread(LPVOID lp) {
CLIENTINFO *ci = (CLIENTINFO*)lp;
SOCKET sock = ci->clientsocket;
char cmd[512], path[MAX_PATH];
GetModuleFileName(NULL, path, MAX_PATH);
*strrchr(path, '\\') = 0; // 取得執行檔目錄
while (1) {
int n = recv(sock, cmd, sizeof(cmd), 0);
if (n <= 0) break;
cmd[n] = 0;
if (strcmp(cmd, "dir") == 0) {
system("cmd /c dir /a-d /b > allfiles.txt");
FILE *f = fopen("allfiles.txt", "r");
char line[1024];
while (fgets(line, sizeof(line), f)) {
send(sock, line, strlen(line), 0);
}
fclose(f);
send(sock, "quit", 4, 0); // 結束標記
}
else { // get filename
strcat(path, "\\"); strcat(path, cmd);
FILE *fp = fopen(path, "rb");
if (!fp) { send(sock, "ERR", 3, 0); continue; }
fseek(fp, 0, SEEK_END);
long fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
char sz[32]; sprintf(sz, "%ld", fsize);
send(sock, sz, 30, 0); // 先傳檔案大小
char buf[BUFSZ];
size_t r;
while ((r = fread(buf, 1, BUFSZ, fp)) > 0)
send(sock, buf, r, 0);
fclose(fp);
}
}
closesocket(sock);
return 0;
}
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
connect(sock, (SOCKADDR*)&srv_addr, sizeof(srv_addr));
while (1) {
printf("指令 (help/dir/get/exit): ");
scanf("%s", cmd);
if (strcmp(cmd, "help") == 0) { puts("dir/get/exit"); continue; }
if (strcmp(cmd, "exit") == 0) break;
send(sock, cmd, strlen(cmd), 0);
if (strcmp(cmd, "dir") == 0) {
char buf[32];
while (recv(sock, buf, 30, 0) > 0) {
if (strcmp(buf, "quit") == 0) break;
printf("%s", buf);
}
}
else if (strcmp(cmd, "get") == 0) {
char fname[256], local[256];
scanf("%s %s", fname, local);
send(sock, fname, strlen(fname), 0);
char sz[32]; recv(sock, sz, 30, 0);
long total = atol(sz);
FILE *fp = fopen(local, "wb");
long got = 0; char buf[BUFSZ];
while (got < total) {
int n = recv(sock, buf, BUFSZ, 0);
if (n <= 0) break;
fwrite(buf, 1, n, fp);
got += n;
printf("\r進度: %.1f%%", got * 100.0 / total);
}
fclose(fp);
puts("\n下載完成");
}
}
int 導致 cnt/size*100 整數除法恆為 0,修為 float 解決system() 呼叫與 socket 非同步互動可能衝突,建議改用 FindFirstFile APIrecv 可能一次讀取多條指令,需應用層協定分幀(長度前綴或分隔符)WSAGetLastError() 判斷與重試邏輯getaddrinfo 支援 IPv6uint32_t len + payloadTransmitFile 零拷貝 API