TCP文件传输(客户端的文件上传)

TCP协议的定义:传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

一、TCP传输的流程

1.WSAStartup()

        在应用程序当中调用Winsock API 函数,首先通过WSAStartup 函数完成对Winsock服务的初始化,因此需要调用WSAStartup 函数,使用Socket的程序在使用Socket之前必须调用WSAStartup函数。

WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    wVersionRequested = MAKEWORD(2, 2);
    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        printf("WSAStartup failed with error: %d\n", err);
        return 1;
    }
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
       printf("Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return 1;
    }
    else
        printf("The Winsock 2.2 dll was found okay\n");

2.socket()

        套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

int socket(int af, int type, int protocol);

  1)af 为是 IP 地址类型,常用的有 AF_INET(IPv4 地址)和 AF_INET6(IPv6 地址);

  2)type 为数据传输方式,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字)         和 SOCK_DGRAM ( 数据报套接字/无连接的套接字 );
  3)protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP         传输协议和 UDP 传输协议,设为0时系统自动匹配对应传输协议;

例如:

SOCKET sockclient = socket(AF_INET,SOCK_STREAM,0);

 3.connect()

connect () 系统调用将套接字(socket)连接到 addr 指定的地址。

int connect(int socket, const struct sockaddr *addr, socklen_t addrlen);

  1)  socket 为连接所用的套接字;

  2)addr 该地址为所要连接的socket的地址;

  3)addrlen为指定 addr 的大小;

例如:

sockaddr_in service;
    service.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//本地
    service.sin_family = AF_INET;//IPV4
    service.sin_port = htons(1234);//端口号
    connect(sockclient,(sockaddr*)&service,sizeof(service));

 4.send()

int send( SOCKET s, const char FAR *buf, int len, int flags );

   1)s 为指定发送端套接字

   2)buf 为一个存放应用程序要发送数据的缓冲区;

   3)len 为实际要发送的数据的字节数;

   4)flags 为执行方式,一般置0;

例如:

char szbuf[1024];
send(sockclient,szbuf,sizeof(szbuf),0);

5.recv()

        recv函数是指从TCP连接的另一端接收数据。

int recv( SOCKET s, char FAR *buf, int len, int flags );

   1)s 为指定发送端套接字

   2)buf 为一个存放应用程序要接收数据的缓冲区;

   3)len 为实际要发送的数据的字节数;

   4)flags 为执行方式,一般置0;

例如:

 char rebuf[4]= {0}; 
 recv(sockclient,rebuf,sizeof(rebuf),0);

6.关闭...

closesocket(sockclient);

        该函数是Windows系统中的一个重要函数,它用来关闭已经打开的socket,参数是一个已经创建好的socket描述符,该函数会将socket描述符标记为无效,并将相应的描述符从系统中移除。在程序完成之后,都应该使用closesocket函数关闭socket,以保证系统资源能够及时被释放。如果不关闭socket,将出现资源泄漏的情况,影响程序的正常运行。
WSACleanup();

        一旦程序结束需要停止Socket库的使用,需要调用WSACleanup函数,这一步和最开始的WSAStartup是对应的。

  二、main代码

#include <QCoreApplication>
#include <winsock.h>
#include <iostream>
#include <sys/stat.h>
using namespace std;
#define Onepage 4096
//若使用VS,在此需引入ws2_32网络库
//qt则在.pro文件中加入

struct FILEHEADER{
    char m_FileName[MAX_PATH];
    long m_Filesize;
};
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    /*
     * 1.WSAStartup()
     * 2.socket()
     * 3.connect()
     * 4.send()
     * 5.recv()
     * 6.关闭...
     *
   */
    /*****************************1************************************/
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        /* Tell the user that we could not find a usable */
        /* Winsock DLL.                                  */
        printf("WSAStartup failed with error: %d\n", err);
        return 1;
    }

    /* Confirm that the WinSock DLL supports 2.2.*/
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        printf("Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return 1;
    }
    else
        printf("The Winsock 2.2 dll was found okay\n");
    /*****************************2************************************/
    cout<<" this is client "<<endl;
    SOCKET sockclient = socket(AF_INET,SOCK_STREAM,0);
    if(INVALID_SOCKET == sockclient){
        printf("sock erro\n");
    }
    /*****************************3************************************/
    sockaddr_in service;
    service.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    service.sin_family = AF_INET;
    service.sin_port = htons(1234);
    if(SOCKET_ERROR == connect(sockclient,(sockaddr*)&service,sizeof(service))){
        printf("connect erro\n");
    }
    /*****************************传输文件******************************/
    char szPath[MAX_PATH];
    //1.获取文件的名、大小
    cout<<"please input Path information: "<<endl;
    cin>>szPath;
    char *ptemp = szPath;
    while(*++ptemp!='\0');
    while(*--ptemp!='\\');
    ++ptemp;//从拖入地址中找到,目标文件的名字
    struct stat s;
    stat(szPath,&s);//获取目标文件大小
    cout<<"File name: "<<*ptemp<<endl;
    cout<<"File size: "<<s.st_size<<endl;
    /*****************************4************************************/
    //发送信息给服务器
    //先向服务器发送要传输的文件名字、大小
    FILEHEADER fh;
    strcpy(fh.m_FileName,ptemp);
    fh.m_Filesize = s.st_size;
    send(sockclient,(char*)&fh,sizeof(fh),0);
    /*****************************5************************************/
    //接收服务器是否继续传输文件的回复
    char szbuf[4]= {0}; //接收缓存区 
    char rebuf[Onepage];//文件读取缓存区
    recv(sockclient,szbuf,sizeof(szbuf),0);
    if(0 == strcmp(szbuf,"yes"))//如果回复的是接收("yes")
    {
        FILE* pf = fopen(szPath,"rb");//打开文件
        while (1) {
            //读文件内容并发送
            size_t nread = fread(rebuf,sizeof(char),Onepage,pf);
            if(nread > 0){
                send(sockclient,rebuf,nread,0);
            }
            else{
                break;
            }
        }
        //关闭文件
        fclose(pf);
    }
    /*****************************关闭*********************************/
    closesocket(sockclient);
    WSACleanup();

    return a.exec();
}

引入所需ws2_32网络库(.pro文件)

方法一:QT为例,在.pro文件中引入所需库:

/****************************************************************************/
QT = core

CONFIG += c++17 cmdline

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
/****************************************************************************/
LIBS+=-lws2_32//在最后引入lws2_32库

方法二:以VS为例,在main函数前加入

#pragma comment(lib, "ws2_32.lib")
  • 33
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值