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的实际数据并不是和整个结构体连续储存的,发送到服务器解析出来时错误的。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值