我是靠谱客的博主 老实招牌,最近开发中收集的这篇文章主要介绍TCP文件传输(一),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述


最近在做网络抓包和文件传输,今天遇到一个奇怪的现象,自己查了半天也没搞清楚,导致花了一个下午时间就只实现了文件名和文件长度的发送,首先给出文件传输的客户端和服务器端代码:

一、客户端

相关头文件定义:

1)Winsock Com组件初始化和释放

#include "stdafx.h"

#ifndef _WINSOCK_STARTUP_H_
#define _WINSOCK_STARTUP_H_


#include <winsock2.h>
#pragma comment(lib , "ws2_32.lib") 

class WinsockStartup{
private:
	WinsockStartup(){
		WSADATA wsaData;
		WSAStartup(MAKEWORD(2,2),&wsaData);
	}
	static WinsockStartup winStart;
public:
	~WinsockStartup(){
		WSACleanup();
	}
};

#endif
2)传输类定义

#include "stdafx.h"

#ifndef _STREAM_TRANSFER_H_
#define _STREAM_TRANSFER_H_

/********************************************
** Transfer a .flv file use TCP
********************************************/
#include "WinsockStartup.h"

struct fileInfo{
	int fileLen;
	char fileName[256];
};

class StreamTransfer{
public:
	StreamTransfer(string fName);
	void SendFile();
private:
	string fileName;
};

#endif

3)传输代码实现

#include "stdafx.h"
#include "StreamTransfer.h"
#include <io.h>

StreamTransfer::StreamTransfer(string fName):fileName(fName){
}

void StreamTransfer::SendFile(){
	//client send code
	int iRet=0;                   //socket API return value 
	FILE *f=NULL;                 //file handle

	//server sockaddr
	SOCKADDR_IN mAddr;
	mAddr.sin_family=AF_INET;
	mAddr.sin_port=5050;
	mAddr.sin_addr.S_un.S_addr=inet_addr("10.117.7.195");
  
	//open the file 
	/*if((f=fopen("stdafx.h","rb"))==NULL){
		cout<<"Open file Error!"<<endl;
		return;
	}*/
	if((f=fopen(fileName.c_str(),"rb"))==NULL){
		cout<<"Open file Error!"<<endl;
		return;
	}
	int fileSize= filelength(fileno(f));
	cout<<"The length of the file["<<fileName<<"] is:"<<fileSize<<endl;

	SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    
	iRet=connect(s,(SOCKADDR *)&mAddr,sizeof(mAddr));
	if(iRet){
		int errorCode=WSAGetLastError();
		cout<<"connect Error("<<errorCode<<")!"<<endl;
		return ;
	}

	cout<<"connect success!!"<<endl;

	fileInfo fInfo;
	strcpy(fInfo.fileName,fileName.c_str());
	fInfo.fileLen=fileSize;

	//char dataBuf[100];
	//sprintf(dataBuf,"%d%s",fInfo.fileLen,fInfo.fileName.c_str());
	send(s,(char*)&fInfo,sizeof(fileInfo),0);    

	iRet=closesocket(s);
	if(iRet){
		int errorCode=WSAGetLastError();
		cout<<"Close socket Error("<<errorCode<<")!"<<endl;
		return ;
	}
}

二、服务器端代码实现

// FileRecesiver.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "WinsockStartup.h"
#include <iostream>
#include <string>
using namespace std;

struct fileInfo{
	int fileLen;
	char fileName[256];
};

int _tmain(int argc, _TCHAR* argv[])
{
    int fileLen=0;                         //file Length
	string fileName;                       //file Name

	//temp variables
	int iRet=0;                            //Socket API return value
	char *databuf=NULL;                     //receive data buffer

	SOCKADDR_IN serverAddr;
	serverAddr.sin_family=AF_INET;
	serverAddr.sin_port=5050;
	serverAddr.sin_addr.S_un.S_addr=inet_addr("10.117.7.195");

	SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    
	//bind
	iRet=bind(s,(SOCKADDR *)&serverAddr,sizeof(serverAddr));
	if(iRet){
		int errorCode = WSAGetLastError();
		cout<<"bind error("<<errorCode<<")!"<<endl;
		return -1;
	}

	//listen
    iRet=listen(s,5);
	if(iRet){
		int errorCode = WSAGetLastError();
		cout<<"listen error("<<errorCode<<")!"<<endl;
		return -1;
	}

	SOCKADDR_IN connAddr;
	int sizeAddr;
    //accept
	SOCKET clientSocket=accept(s,NULL/*(SOCKADDR *)&connAddr*/,NULL/*&sizeAddr*/);
	if (clientSocket==INVALID_SOCKET){
		cout<<"Accept Error("<<WSAGetLastError()<<")!"<<endl;
		return 0;
	}

	//normally here should be in a thread or use CALLBACK
	//receive data

	//receive file information
	databuf=new char[100];
	memset(databuf,0,sizeof(databuf));
	int recyByte=recv(clientSocket,databuf,100,MSG_PEEK);
	fileLen=((fileInfo *)databuf)->fileLen;
    fileName=((fileInfo *)databuf)->fileName;


	cout<<"Start receive file ["<<fileName<<"]..."<<endl;
	cout<<"File Length:"<<fileLen<<endl;
    //cout<<databuf<<endl;
	iRet=closesocket(s);
	if(iRet){
		int errorCode=WSAGetLastError();
		cout<<"Close socket Error("<<errorCode<<")!"<<endl;
		return -1;
	}
	return 0;
}
三、问题总结

上面的代码很水(勿喷),TCP的通信机制这了不再细说了,以上代码基本上初步实现了对待传输文件最基本的信息的发送,但需要总结一下的是:

1)我最初是将文件名称和文件长度分开发送的,首先发送的文件长度再发送文件名称,代码如下:

//send file length
	char *sendBuf[100];
	itoa(fileSize,sendBuf,10);
    send(s,sendBuf,sizeof(sendBuf),0);

	//send file name
	send(s,fileName.c_str(),fileName.size(),0);
相应的接收端也执行两次接受操作,但是接受端执行两次接受后所接受到的文件名称和长度都是发送端第一次发送的文件长度,不知道为什么,百思不得其解!

2)另外需要注意的一点,想作为报文发送出去而定义的结构体一定要保证结构体的内部数据时连续存储的,否则发送出去肯定发时错误的结果,我在这里出现的错误是:

因为习惯了使用STL写代码所以本来在fileInfo中定义文件名直接使用了string filename:

struct fileInfo{
	int fileLen;
	char fileName[256];
	/*string fileName;*/
};

注释部分明显是不对的,因为string内部的存储数据时系统另外分配的,这回导致fileName的实际数据并不是和整个结构体连续储存的,发送到服务器解析出来时错误的。



最后

以上就是老实招牌为你收集整理的TCP文件传输(一)的全部内容,希望文章能够帮你解决TCP文件传输(一)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部