使用NDK进行网络文件传输

来源:互联网 发布:数据挖掘岗很难进吗 编辑:程序博客网 时间:2024/05/22 10:50
 想要通过网络传输文件,搜了不少代码,找到一个何意的真不容易。Marat Bedretdinov给了一个关于聊天程序的代码,里面包含NetLib模块,对网络提供了一个很好的界面,提供了多线程和事件通知。但是很复杂,源文件就有49个,还木有时间细看。有兴趣的可以看看。

Sebastien_Lachance写了一个名为NDK (Network Development Kit 2.0) 的小工具,可以方便的开发客户服务器程序。提供了一个服务器、客户端的框架,基于MFC的CSocket的,不足的是只支持单线程,多线程也不安全。NDK隐藏了基于网络开发的复杂性,只用和3个类进行交互:CNDKServer、CNDKClient、CNDKMessage。先简单看下这些类:

CNDKServer

Attributes:

·         BOOL IsStarted() const;

·         long GetPort() const;

·         long GetNbUsers() const;

·         void GetUserIds(CLongArray& alIds) const;

Operations:

·         BOOL StartListening(long lPort);

·         void Stop();

·         BOOL SendMessageToUser(long lUserId, CNDKMessage& message);

·         BOOL SendMessageToAllUsers(CNDKMessage& message);

·         BOOL SendMessageToSomeUsers(const CLongArray& alUserIds, CNDKMessage& message);

·         BOOL SendMessageToAllUsersExceptFor(long lUserId, CNDKMessage& message);

·         BOOL SendMessageToAllUsersExceptFor(const CLongArray& alUserIds, CNDKMessage& message);

·         BOOL DisconnectUser(long lUserId);

·         void DisconnectAllUsers();

·         BOOL PingUser(long lUserId);

·         void PingAllUsers();

Callbacks:

·         virtual BOOL OnIsConnectionAccepted() = 0;

·         virtual void OnConnect(long lUserId) = 0;

·         virtual void OnMessage(long lUserId, CNDKMessage& message) = 0;

·         virtual void OnDisconnect(long lUserId, NDKServerDisconnection disconnectionType) = 0;

·         virtual void OnPing(long lUserId, long lNbMilliseconds);

CNDKClient

Attributes:

·         BOOL IsConnected() const;

·         BOOL GetIpAndPort(CString& strIp, long& lPort) const;

Operations:

·         BOOL OpenConnection(const CString& strServerIp, long lPort);

·         void CloseConnection();

·         BOOL SendMessageToServer(CNDKMessage& message);

·         BOOL PingServer();

Callbacks:

·         virtual void OnMessage(CNDKMessage& message) = 0;

·         virtual void OnDisconnect(NDKClientDisconnection disconnectionType) = 0;

·         virtual void OnPing(long lNbMilliseconds);

CNDKMessage

Attributes:

·         void SetId(long lId);

·         long GetId() const;

·         int GetNbElements() const;

Operations:

·         void Add(TYPE typeData);

·         void SetAt(long lIndex, TYPE typeData);

·         void GetAt(long lIndex, TYPE& typeData) const;

·         void GetNext(TYPE& typeData);

TYPE 可以是 UCHAR, char, USHORT, short, UINT, int, long, float, double, CString, 或者 LPVOID,这为我们开发各种应用提供了方便。有兴趣的可以读读其源码。通过使用CArchive来实现常用数据类型的序列化和反序列化,然后通过网络进行传输。

CNDKMessage有一个属性ID来代表消息的类型,重载纯虚函数virtual void OnMessage(CNDKMessage& message) = 0时,可以根据消息类型不同而进行不同的处理。比如我们做一个聊天程序可以定义如下消息:

enum ChatMessage{ChatUserJoin,ChatUserQuit,ChatText,ChatBigMessage};

然后在OnMessage中进行处理:

void CChatServerDlg::OnMessage(long lUserId, CNDKMessage& message){switch (message.GetId()){case ChatUserJoin:{……}break;case ChatText:{……}break;case ChatBigMessage:{……}break;}}

如何利用这个框架来实现文件的传输?由于一次传输的数据包不能过大,文件肯定是需要分片的。定义如下的消息类型:

// Defines use for CNDKMessage#define SERVER_FILES           0#define REQUEST_FILE           1#define START_TRANSFERT        2#define REQUEST_NEXT_FILE_PART 3#define NEXT_FILE_PART         4#define TRANSFERT_COMPLETED    5// Define use when allocating the buffer to send a file part#define BUFFER_SIZE 1024

SERVER_FILES 用以获取服务器上可用的文件列表。REQUEST_FILE用于请求某个文件,可以多个。START_TRANSFERT用于获取文件大小,以便分配磁盘空间。REQUEST_NEXT_FILE_PART客户端请求下一个文件块。NEXT_FILE_PART服务器发送下一个文件块。TRANSFERT_COMPLETED 文件传输结束。BUFFER_SIZE表示文件的分片大小。

服务器端的代码如下:

// Called whenever a message is received from a user.void CNDKFileTransferServerDlg::OnMessage(long lUserId,                               CNDKMessage& message){    switch (message.GetId())    {    // The client requests a file to download    case REQUEST_FILE:        {            CString strFileName;            message.GetAt(0, strFileName);            m_fileUpload.Open(strFileName, CFile::modeRead |                               CFile::shareDenyWrite);            // Send the file length            message.SetId(START_TRANSFERT);            message.SetAt(0, (int)m_fileUpload.GetLength());            SendMessageToUser(lUserId, message);            CString strActivity;            strActivity.Format(IDS_UPLOAD_FILE, strFileName);            AddActivity(strActivity);        }        break;    // The client asks for the next file part to download    case REQUEST_NEXT_FILE_PART:        {            m_unBufferLength = m_fileUpload.Read(m_byteBuffer, BUFFER_SIZE);            if (m_unBufferLength != 0)            {                // Send the file part                message.SetId(NEXT_FILE_PART);                message.SetAt(0, m_byteBuffer, m_unBufferLength);            }            else            {                // When there is no more read bytes,                // send the acknowledgment that the file is completed                message.SetId(TRANSFERT_COMPLETED);                // Close the file if it is opened.                if (m_fileUpload.m_hFile != INVALID_HANDLE_VALUE)                    m_fileUpload.Close();                CString strActivity;                strActivity.Format(IDS_UPLOAD_FILE_COMPLETED);                AddActivity(strActivity);            }            SendMessageToUser(lUserId, message);        }        break;    }}

客户端首先请求一个文件:

// Open the fileif (m_fileDownload.Open(strFileNameToCreate,     CFile::modeCreate | CFile::modeWrite)){    // Ask the server to start the download    CNDKMessage message(REQUEST_FILE);    message.Add(strFileName);    SendMessageToServer(message);    m_bIsDownloading = TRUE;    UpdateUI();}

然后重载CNDKClient中的virtual void OnMessage(CNDKMessage& message) = 0;

// Called when a message is received.void CNDKFileTransferClientDlg::OnMessage(CNDKMessage& message){    switch (message.GetId())    {    case SERVER_FILES:        {            // Add the file name in the list            for (int nFileIndex = 0;                  nFileIndex < message.GetNbElements(); nFileIndex++)            {                CString strFileName;                message.GetAt(nFileIndex, strFileName);                m_listServerFiles.AddString(strFileName);            }            UpdateUI();        }        break;    case START_TRANSFERT:        {            message.GetAt(0, m_nFileSize);                        m_progressDownload.SetRange32(0, m_nFileSize);                        // Ask the server for the first file part            message.SetId(REQUEST_NEXT_FILE_PART);            SendMessageToServer(message);        }        break;    case NEXT_FILE_PART:        {            message.GetAt(0, m_byteBuffer, m_unBufferLength);            m_fileDownload.Write(m_byteBuffer, m_unBufferLength);            m_progressDownload.OffsetPos(m_unBufferLength);            // Ask the server for the first file part            CNDKMessage requestMessage(REQUEST_NEXT_FILE_PART);            SendMessageToServer(requestMessage);        }        break;    case TRANSFERT_COMPLETED:        m_fileDownload.Close();        AfxMessageBox(IDS_FILE_DOWNLOADED_SUCCESSFULLY);        UpdateUI();        break;    }}

更多信息和源码参见:

http://www.codeproject.com/KB/IP/peer_to_peer_communicator.aspx

http://www.codeproject.com/KB/IP/ndk.aspx

http://www.codeproject.com/KB/IP/NDKFileTransfer.aspx

 

 

原创粉丝点击