網路程式設計實驗三——TCP實現跨平臺檔案傳輸
阿新 • • 發佈:2018-12-19
一、實驗目的
1.在循環面向連線的程式基礎上,利用tcp完成linux和windows平臺的檔案傳輸。
2.對伺服器程式進行合理的封裝優化實驗步驟。
二、實驗分析
Linux伺服器:
1.首先,建立套接字,並將其繫結到提供服務的埠上,設定為被動模式,將這幾步進行封裝,定義int passiveTCP (const char*service)函式,傳入埠號。
2.然後,進入迴圈,從該套接字上接收下一個連線請求,獲得該連線的新的套接字,並向其傳送之前選中的檔案,同時統計傳送檔案長度顯示出來。當此客戶完成互動時,關閉連線。迴圈接收與傳送。
3.最後,當迴圈結束後,關閉伺服器最開始建立的用於接收客戶端的套接字。
Windows客戶端:
1.首先,利用Windows平臺下的winsock進行socket的初始化,建立並進行connect,連線伺服器,此過程封裝進connectTCP(IP,PORT)函式中,返回建立的socket的描述符。
2.然後,實現檔案的接收工作。(這部分程式碼和原來linux實現的沒有太大差別)
3.最後,關閉socket連線。
三、實驗程式碼
- Linux伺服器:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 6000 #define LISTENQ 20 #define BUFFSIZE 4096 #define FILE_NAME_MAX_SIZE 512 int passiveTCP (const char*service){ //Create socket int sockfd,connfd; struct sockaddr_in svraddr,clientaddr; bzero(&svraddr,sizeof(svraddr)); svraddr.sin_family=AF_INET; svraddr.sin_addr.s_addr=htonl(INADDR_ANY); svraddr.sin_port=htons(PORT); sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd<0) { perror("socket"); exit(1); } //bind if(bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr))<0) { perror("bind"); exit(1); } //listen if(listen(sockfd,LISTENQ)<0) { perror("listen"); exit(1); } return sockfd; } int main(int argc, char **argv[]) { //Input the file name char filename[FILE_NAME_MAX_SIZE]; bzero(filename,FILE_NAME_MAX_SIZE); printf("Please input the file name you wana to send:"); scanf("%s",&filename); getchar(); int sockfd,connfd; struct sockaddr_in clientaddr; sockfd = passiveTCP(PORT); while(1) { socklen_t length=sizeof(clientaddr); //accept connfd=accept(sockfd,(struct sockaddr*)&clientaddr,&length); if(connfd<0) { perror("connect"); exit(1); } //send file imformation char buff[BUFFSIZE]; int count; bzero(buff,BUFFSIZE); strncpy(buff,filename,strlen(filename)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(filename)); count=send(connfd,buff,BUFFSIZE,0); if(count<0) { perror("Send file information"); exit(1); } //read file FILE *fd=fopen(filename,"rb"); if(fd==NULL) { printf("File :%s not found!\n",filename); } else { bzero(buff,BUFFSIZE); int file_block_length=0; while((file_block_length=fread(buff,sizeof(char),BUFFSIZE,fd))>0) { printf("file_block_length:%d\n",file_block_length); if(send(connfd,buff,file_block_length,0)<0) { perror("Send"); exit(1); } bzero(buff,BUFFSIZE); } fclose(fd); printf("Transfer file finished !\n"); } close(connfd); } close(sockfd); return 0; }
- Windows客戶端
#include <stdlib.h> #include <winsock2.h> #include <stdio.h> //#pragma comment(lib,"ws2_32.lib") //把ws2_32.lib加到Link頁的連線庫 //#define IP "172.18.68.243" //在兩臺計算機上測試,IP為Server端的IP地址 #define IP "192.168.248.131" //在一臺計算機上測試,IP為本地回送地址 #define PORT 6000 //注意:客戶端設定通訊的埠 = 服務端的埠 #define BUFFER_SIZE 1024 //資料傳送緩衝區大小 #define LISTENQ 20 #define BUFFSIZE 4096 #define FILE_NAME_MAX_SIZE 512 void recvTCP(int clientfd){ //recv file imformation char buff[BUFFSIZE]; char filename[FILE_NAME_MAX_SIZE]; int count; //bzero(buff,BUFFSIZE); memset(buff,0,BUFFSIZE); count=recv(clientfd,buff,BUFFSIZE,0); if(count<0) { perror("recv"); exit(1); } strncpy(filename,buff,strlen(buff)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buff)); printf("Preparing recv file : %s \n",filename); //recv file FILE *fd=fopen(filename,"wb+"); if(NULL==fd) { perror("open"); exit(1); } //bzero(buff,BUFFSIZE); memset(buff,0,BUFFSIZE); int length=0; while(length=recv(clientfd,buff,BUFFSIZE,0)) { if(length<0) { perror("recv"); exit(1); } int writelen=fwrite(buff,sizeof(char),length,fd); if(writelen<length) { perror("write"); exit(1); } //bzero(buff,BUFFSIZE); memset(buff,0,BUFFSIZE); } printf("Receieved file:%s finished!\n",filename); fclose(fd); } int connectTCP(const char *host, const char *port){ WSADATA WSAData; if(WSAStartup(MAKEWORD(2,0),&WSAData)==SOCKET_ERROR) //WSAStartup()函式對Winsock DLL進行初始化 { printf("Socket initialize fail!\n"); //continue; } SOCKET sock; //客戶端程序建立套接字 if((sock=socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR) //建立流套接字(與服務端保持一致) { printf("Socket create fail!\n"); WSACleanup(); //continue; } struct sockaddr_in ClientAddr; //sockaddr_in結構用來標識TCP/IP協議下的地址,可強制轉換為sockaddr結構 ClientAddr.sin_family=AF_INET; //指Internet域 ClientAddr.sin_port=htons(PORT); //指定服務端所預留的埠 ClientAddr.sin_addr.s_addr=inet_addr(IP); //指定服務端所繫結的IP地址 if(connect(sock,(LPSOCKADDR)&ClientAddr,sizeof(ClientAddr))==SOCKET_ERROR) //呼叫connect()函式,向伺服器程序發出連線請求 { printf("Connect fail!\n"); closesocket(sock); WSACleanup(); //continue; } return sock; } int main() { SOCKET sock; //客戶端程序建立套接字 char buf[BUFFER_SIZE]; //buf陣列存放客戶端傳送的訊息 int inputLen; //用於輸入字元自增變數 while(1) { printf("Socket\\Client>"); inputLen=0; memset(buf,0,sizeof(buf)); while((buf[inputLen++]=getchar())!='\n') //輸入以回車鍵為結束標識 { ; } if(buf[0]=='e' && buf[1]=='x' && buf[2]=='i' && buf[3]=='t') { printf("The End.\n"); break; } sock=connectTCP(IP,PORT); //send(sock,buf,BUFFER_SIZE,0); //向伺服器傳送資料 recvTCP(sock); closesocket(sock); //關閉套接字 WSACleanup(); //終止對Winsock DLL的使用,並釋放資源,以備下一次使用 } return 0; }
四、執行結果
- Windows客戶端:(codeblocks編譯執行):
- Linux伺服器端: 傳送了1.txt檔案,長度為14。
五、實驗感悟
本次實驗完成了在不同作業系統下的TCP的檔案傳輸,讓我更加深刻的認識到了不精確指明的協議軟體介面的含義。通過不精確指明,程式設計師可以用基本上相同的函式實現TCP的傳輸。因此我們不需要額外編寫一套用於windows的TCP程式,而只需要用條件編譯實現不同作業系統下的不同的部分,比如標頭檔案,windows上的初始化,關閉socket的函式。