生活随笔
收集整理的這篇文章主要介紹了
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ò),歡迎將生活随笔 推薦給好友。