1. 程式人生 > >網路程式設計實驗三——TCP實現跨平臺檔案傳輸

網路程式設計實驗三——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的函式。