您好,登錄后才能下訂單哦!
這節我們來完成 socket 文件傳輸程序,這是一個十分適用的例子。要完成的功用為:client 從 server 下載一個文件并保管到當地。
編寫這個程序需求留意兩個成績:
1) 文件巨細不肯定,有能夠比緩沖區大許多,挪用一次 write()/send() 函數不克不及完成文件內容的發送。接納數據時也會碰到異樣的狀況。
要處理這個成績,可以運用 while 輪回,例如:
//Server 代碼 int nCount; while( (nCount = fread(buffer, 1, BUF_SIZE, fp)) > 0 ){ send(sock, buffer, nCount, 0); } //Client 代碼 int nCount; while( (nCount = recv(clntSock, buffer, BUF_SIZE, 0)) > 0 ){ fwrite(buffer, nCount, 1, fp); }
關于 Server 端的代碼,當讀取到文件末尾,fread() 會前往 0,完畢輪回。
關于 Client 端代碼,有一個癥結的成績,就是文件傳輸終了后讓 recv() 前往 0,完畢 while 輪回。
留意:讀取完緩沖區中的數據 recv() 并不會前往 0,而是被壅塞,直到緩沖區中再次無數據。
2) Client 端若何判別文件接納終了,也就是下面提到的成績——何時完畢 while 輪回。
最復雜的完畢 while 輪回的辦法當然是文件接納終了后讓 recv() 函數前往 0,那么,若何讓 recv() 前往 0 呢?recv() 前往 0 的獨一機遇就是收到FIN包時。
FIN 包表現數據傳輸終了,盤算機收到 FIN 包后就曉得對方不會再向本人傳輸數據,當挪用 read()/recv() 函數時,假如緩沖區中沒無數據,就會前往 0,表現讀到了”socket文件的末尾“。
這里我們挪用 shutdown() 來發送FIN包:server 端直接挪用 close()/closesocket() 會使輸入緩沖區中的數據生效,文件內容很有能夠沒有傳輸終了銜接就斷開了,而挪用 shutdown() 會等候輸入緩沖區中的數據傳輸終了。
本節以Windows為例演示文件傳輸功用,Linux與此相似,不再贅述。請看下面完好的代碼。
效勞器端 server.cpp:
#include <stdio.h> #include <stdlib.h> #include <winsock2.h> #pragma comment (lib, "ws2_32.lib") //加載 ws2_32.dll #define BUF_SIZE 1024 int main(){ //先反省文件能否存在 char *filename = "D:\\send.avi"; //文件名 FILE *fp = fopen(filename, "rb"); //以二進制方法翻開文件 if(fp == NULL){ printf("Cannot open file, press any key to exit!\n"); system("pause"); exit(0); } WSADATA wsaData; WSAStartup( MAKEWORD(2, 2), &wsaData); SOCKET servSock = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = PF_INET; sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); sockAddr.sin_port = htons(1234); bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); listen(servSock, 20); SOCKADDR clntAddr; int nSize = sizeof(SOCKADDR); SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize); //輪回發送數據,直到文件開頭 char buffer[BUF_SIZE] = {0}; //緩沖區 int nCount; while( (nCount = fread(buffer, 1, BUF_SIZE, fp)) > 0 ){ send(clntSock, buffer, nCount, 0); } shutdown(clntSock, SD_SEND); //文件讀取終了,斷開輸入流,向客戶端發送FIN包 recv(clntSock, buffer, BUF_SIZE, 0); //壅塞,等候客戶端接納終了 fclose(fp); closesocket(clntSock); closesocket(servSock); WSACleanup(); system("pause"); return 0; }
客戶端代碼:
#include <stdio.h> #include <stdlib.h> #include <WinSock2.h> #pragma comment(lib, "ws2_32.lib") #define BUF_SIZE 1024 int main(){ //先輸出文件名,看文件能否能創立勝利 char filename[100] = {0}; //文件名 printf("Input filename to save: "); gets(filename); FILE *fp = fopen(filename, "wb"); //以二進制方法翻開(創立)文件 if(fp == NULL){ printf("Cannot open file, press any key to exit!\n"); system("pause"); exit(0); } WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = PF_INET; sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); sockAddr.sin_port = htons(1234); connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //輪回接納數據,直到文件傳輸終了 char buffer[BUF_SIZE] = {0}; //文件緩沖區 int nCount; while( (nCount = recv(sock, buffer, BUF_SIZE, 0)) > 0 ){ fwrite(buffer, nCount, 1, fp); } puts("File transfer success!"); //文件接納終了后直接封閉套接字,無需挪用shutdown() fclose(fp); closesocket(sock); WSACleanup(); system("pause"); return 0; }
在D盤中預備好send.avi文件,先運轉 server,再運轉 client:
Input filename to save: D:\\recv.avi↙
//稍等少焉后
File transfer success!
翻開D盤就可以看到 recv.avi,巨細和 send.avi 相反,可以正常播放。
留意 server.cpp 第42行代碼,recv() 并沒有接納到 client 端的數據,當 client 端挪用 closesocket() 后,server 端會收到FIN包,recv() 就會前往,前面的代碼持續履行。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。