目录
一、TCP客户端文件上传
TCP文件传输( 客户端的文件上传 )https://blog.csdn.net/2302_77573185/article/details/140999259
二、TCP传输的流程 ( 服务端 )
1. WSAStartup函数
1.WSAStartup()
在应用程序当中调用Winsock API 函数,首先通过WSAStartup 函数完成对Winsock服务的初始化,因此需要调用WSAStartup 函数,使用Socket的程序在使用Socket之前必须调用WSAStartup函数。
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 sockservice = socket(AF_INET,SOCK_STREAM,0);
3.bind函数
bind()函数将长为addlen的参数my_addr 与 sockfd 绑定在一起。
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
1)sockfd 是用 socket()函数创建的文件套接字描述符;
2)my_addr 是指向一个结构为 sockaddr 参数的指针,sockaddr 中包含了地址、端口和 IP 地址的信息。在进行地址绑定的时候,需要先将地址结构 struct sockaddr 中的 IP 地 址、端口、类型等中的域进行设置后才能进行绑定,这样进行绑定后才能将套接字文件 描述 符与地址等结合在一起; 如下:sockaddr_in service; service.sin_addr.S_un.S_addr = 0;//IP地址 service.sin_family = AF_INET;//IPV4 service.sin_port = htons(1234);//端口号
3)addrlen 是 my_addr 结构的长度,可以设置成 sizeof(struct sockaddr)。
bind()函数的返回值为0时表示绑定成功,-1表示绑定失败。例如:
bind(sockservice, (sockaddr *) &service, sizeof (service));
4.listen函数
当一个 TCP 连接请求到达时,
listen
函数会让该套接字进入监听状态,等待客户端发起连接。int listen(int sockfd, int backlog);
1)sockfd 是用 socket()函数创建的文件套接字描述符;
2)backlog 是调用
listen()
函数设置最大同时处理的连接数(backlog),即允许排队的最大未接受连接数。如果超过这个限制,后续的连接请求将会被暂时拒绝;
例如:
listen(socklisten, 10);
5.accept函数
从处于 established 状态的连接队列头部取出一个已经完成的连接,如果这个队列没有已经完成的连接,accept()函数就会阻塞,直到取出队列中已完成的用户连接为止。
int accept(int sockfd, struct sockaddr * cliaddr, socklen_t *addrlen);
1)sockfd 是用 socket()函数创建的文件套接字描述符;
2) cliaddr 用来返回已连接的对端(客户端)的协议地址;
3) addrlen客户端地址长度;
返回值:已连接的套接字描述符
例如:
sockaddr_in addrclient; int nsize =sizeof(addrclient); SOCKET sockWaiter = accept(socklisten,(struct sockaddr*)&addrclient,&nsize);
6.recv函数
recv函数是指从TCP连接的另一端接收数据。
int send( SOCKET s, const char FAR *buf, int len, int flags );
1)s 为指定发送端套接字;
2)buf 为一个存放应用程序要接收数据的缓冲区;
3)len 为实际要发送的数据的字节数;
4)flags 为执行方式,一般置0;
例如:
char szbuf[1024] = {0}; recv(sockWaiter,szbuf,sizeof(szbuf),0);
7.send函数
send函数是指从TCP连接的另一端接收数据。
int send( SOCKET s, const char FAR *buf, int len, int flags );
1)s 为指定发送端套接字;
2)buf 为一个存放应用程序要发送数据的缓冲区;
3)len 为实际要发送的数据的字节数;
4)flags 为执行方式,一般置0;
例如:
char sebuf[1024]={0}; send(sockWaiter,sebuf,sizeof(sebuf),0);
8.closesocket和WSACleanup函数
closesocket该函数是Windows系统中的一个重要函数,它用来关闭已经打开的socket,参数是一个已经创建好的socket描述符,该函数会将socket描述符标记为无效,并将相应的描述符从系统中移除。在程序完成之后,都应该使用closesocket函数关闭socket,以保证系统资源能够及时被释放。如果不关闭socket,将出现资源泄漏的情况,影响程序的正常运行。
WSACleanup为程序结束需要停止Socket库的使用,需要调用WSACleanup函数,这一步和最开始的WSAStartup是对应的。
三、main函数
#include <QCoreApplication>
#include <winsock.h>
#include <iostream>
#include <windows.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.找地方 --bind();
* 4.宣传 --listen();
* 5.接客人进店里,交给服务员 --accept();
* 6.服务员等着客人说话 --recv();
* 7.回复 --send();
* 8.下班,关店 --closesocket();WSACleanup();
*/
/********************************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***************************************/
//2.雇店长
SOCKET socklisten = socket(AF_INET,SOCK_STREAM,0);
if(INVALID_SOCKET == socklisten){
printf("sock erro\n");
}
/********************************3***************************************/
//3.找地方
sockaddr_in service;
service.sin_addr.S_un.S_addr = 0;
service.sin_family = AF_INET;
service.sin_port = htons(1234);
if (bind(socklisten, (sockaddr *) &service, sizeof (service)) == SOCKET_ERROR) {
printf("bind erro\n");
}
/********************************4***************************************/
//4.宣传
if (listen(socklisten, 10) == SOCKET_ERROR){
printf("listen erro\n");
}
/********************************5***************************************/
sockaddr_in addrclient;
int nsize =sizeof(addrclient);
SOCKET sockWaiter = accept(socklisten,(struct sockaddr*)&addrclient,&nsize);
char szbuf[Onepage] = {0};
char sebuf[10]={0};
int nst = 0;
/********************************6-7**************************************/
FILEHEADER fh;
recv(sockWaiter,(char*)&fh,sizeof(fh),0);
cout<<"file name: "<<fh.m_FileName<<" file size: "<<fh.m_Filesize<<endl;
cout<<"Receive yes or no"<<endl;
cin>>sebuf;
send(sockWaiter,sebuf,sizeof(sebuf),0);
if(strcmp(sebuf,"yes")== 0)
{
cout<<"please input save path"<<endl;
char szPath[MAX_PATH]={0};
cin>>szPath;
strcat(szPath,fh.m_FileName);
FILE*pf = fopen(szPath,"wb");
while(1){
int nRead=recv(sockWaiter,szbuf,sizeof(szbuf),0);
if(nRead > 0){
fwrite(szbuf,sizeof(char),nRead,pf);
nst+=nRead;
}
cout<<"save speed: "<<nst*100/fh.m_Filesize<<"%"<<endl;
if(nst==fh.m_Filesize)break;
}
fclose(pf);
cout<<"save ok"<<endl;
}
/********************************8***************************************/
closesocket(socklisten);
WSACleanup();
return a.exec();
}