socket文件传输功用的完成

网友投稿 272 2022-10-30


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  #include  #include  #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  #include  #include  #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() 就会前往,前面的代码持续履行。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:了解UDP套接字
下一篇:解析Mybatis的insert方法返回数字
相关文章

 发表评论

暂时没有评论,来抢沙发吧~