網絡程序設計實驗三——TCP實現跨平臺文件傳輸

一、實驗目的

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的函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章