日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

socket编程实现文件传输功能

發布時間:2025/3/12 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 socket编程实现文件传输功能 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這節我們來完成?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() 就會返回,后面的代碼繼續執行。

    總結

    以上是生活随笔為你收集整理的socket编程实现文件传输功能的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。