最近在做网络抓包和文件传输,今天遇到一个奇怪的现象,自己查了半天也没搞清楚,导致花了一个下午时间就只实现了文件名和文件长度的发送,首先给出文件传输的客户端和服务器端代码:
一、客户端
相关头文件定义:
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(); } }; #endif2)传输类定义
#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;*/ };