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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

socket编程实现文件传输功能

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

這節(jié)我們來完成?socket?文件傳輸程序,這是一個(gè)非常實(shí)用的例子。要實(shí)現(xiàn)的功能為:client 從 server 下載一個(gè)文件并保存到本地。

編寫這個(gè)程序需要注意兩個(gè)問題:
1) 文件大小不確定,有可能比緩沖區(qū)大很多,調(diào)用一次 write()/send() 函數(shù)不能完成文件內(nèi)容的發(fā)送。接收數(shù)據(jù)時(shí)也會(huì)遇到同樣的情況。

要解決這個(gè)問題,可以使用 while 循環(huán),例如:

  • //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);
  • }
  • 對(duì)于 Server 端的代碼,當(dāng)讀取到文件末尾,fread() 會(huì)返回 0,結(jié)束循環(huán)。

    對(duì)于 Client 端代碼,有一個(gè)關(guān)鍵的問題,就是文件傳輸完畢后讓 recv() 返回 0,結(jié)束 while 循環(huán)。

    注意:讀取完緩沖區(qū)中的數(shù)據(jù) recv() 并不會(huì)返回 0,而是被阻塞,直到緩沖區(qū)中再次有數(shù)據(jù)。

    2) Client 端如何判斷文件接收完畢,也就是上面提到的問題——何時(shí)結(jié)束 while 循環(huán)。

    最簡(jiǎn)單的結(jié)束 while 循環(huán)的方法當(dāng)然是文件接收完畢后讓 recv() 函數(shù)返回 0,那么,如何讓 recv() 返回 0 呢?recv() 返回 0 的唯一時(shí)機(jī)就是收到FIN包時(shí)。

    FIN 包表示數(shù)據(jù)傳輸完畢,計(jì)算機(jī)收到 FIN 包后就知道對(duì)方不會(huì)再向自己傳輸數(shù)據(jù),當(dāng)調(diào)用 read()/recv() 函數(shù)時(shí),如果緩沖區(qū)中沒有數(shù)據(jù),就會(huì)返回 0,表示讀到了”socket文件的末尾“。

    這里我們調(diào)用 shutdown() 來發(fā)送FIN包:server 端直接調(diào)用 close()/closesocket() 會(huì)使輸出緩沖區(qū)中的數(shù)據(jù)失效,文件內(nèi)容很有可能沒有傳輸完畢連接就斷開了,而調(diào)用 shutdown() 會(huì)等待輸出緩沖區(qū)中的數(shù)據(jù)傳輸完畢。

    本節(jié)以Windows為例演示文件傳輸功能,Linux與此類似,不再贅述。請(qǐng)看下面完整的代碼。

    服務(wù)器端 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"); //以二進(jìn)制方式打開文件
  • 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);
  • ?
  • //循環(huán)發(fā)送數(shù)據(jù),直到文件結(jié)尾
  • char buffer[BUF_SIZE] = {0}; //緩沖區(qū)
  • int nCount;
  • while( (nCount = fread(buffer, 1, BUF_SIZE, fp)) > 0 ){
  • send(clntSock, buffer, nCount, 0);
  • }
  • ?
  • shutdown(clntSock, SD_SEND); //文件讀取完畢,斷開輸出流,向客戶端發(fā)送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(){
  • //先輸入文件名,看文件是否能創(chuàng)建成功
  • char filename[100] = {0}; //文件名
  • printf("Input filename to save: ");
  • gets(filename);
  • FILE *fp = fopen(filename, "wb"); //以二進(jìn)制方式打開(創(chuàng)建)文件
  • 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));
  • ?
  • //循環(huán)接收數(shù)據(jù),直到文件傳輸完畢
  • char buffer[BUF_SIZE] = {0}; //文件緩沖區(qū)
  • int nCount;
  • while( (nCount = recv(sock, buffer, BUF_SIZE, 0)) > 0 ){
  • fwrite(buffer, nCount, 1, fp);
  • }
  • puts("File transfer success!");
  • ?
  • //文件接收完畢后直接關(guān)閉套接字,無需調(diào)用shutdown()
  • fclose(fp);
  • closesocket(sock);
  • WSACleanup();
  • system("pause");
  • return 0;
  • }
  • 在D盤中準(zhǔn)備好send.avi文件,先運(yùn)行 server,再運(yùn)行 client:
    Input filename to save: D:\\recv.avi↙
    //稍等片刻后
    File transfer success!

    打開D盤就可以看到 recv.avi,大小和 send.avi 相同,可以正常播放。

    注意 server.cpp 第42行代碼,recv() 并沒有接收到 client 端的數(shù)據(jù),當(dāng) client 端調(diào)用 closesocket() 后,server 端會(huì)收到FIN包,recv() 就會(huì)返回,后面的代碼繼續(xù)執(zhí)行。

    總結(jié)

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。