首页 | 编程语言 | 网站建设 | 游戏天堂 | 冲浪宝典 | 网络安全 | 操作系统 | 软件时空 | 硬件指南 | 病毒相关 | IT 认证
软讯网络 > 编程语言 > .NET > C#.NET > WinSock实现多线程网络文件传输程序(二)(MFC+WinSock 附源代码)
【标  题】:WinSock实现多线程网络文件传输程序(二)(MFC+WinSock 附源代码)
【关键字】:WinSock,MFC+WinSock
【来  源】:http://blog.csdn.net/geniusdot/archive/2007/03/05/1520925.aspx

WinSock实现多线程网络文件传输程序(二)(MFC+WinSock 附源代码)

 

  

上回对这个程序做了个简单的介绍,这回我来讲讲具体实现细节.

 

 

首先最主要的是加载套接字库

StdAfx.h中我们加入如下语句:

#include <afxsock.h>

然后初始化套接字库,App类中的InitInstance()函数中加入如下语句:

 

if(!AfxSocketInit())
    {
        AfxMessageBox("
加载套接字库失败!
");
        
return
 FALSE;
    }

 

需要注意的是当程序连接的时候还要连接Ws2_32.lib这个库.接下来的工作我们都在DialogView类中完成,

对于对话框上控件的摆放和命名可自由搭配.其中每个按钮都有命令响应函数.两个进度条分别关联了两个成员变量.剩下的一个ip地址框,三个编辑框,用作用户和程序交流信息.

 

其中打开文件按钮的命令响应函数如下

void CWinSockDlg::OnOpen() 
{
    
    CFileDialog fileDlg(TRUE);                
//创建打开文件对话框

    fileDlg.m_ofn.lpstrTitle="打开文件";
    fileDlg.m_ofn.lpstrFilter="All Files(*.*)\0*.*\0\0"; 
//设定筛选

    if(IDOK==fileDlg.DoModal())
    {
        m_sendfname=fileDlg.GetPathName();     
        ]
        
//如果用户选择确定设置发送文件名并对对话框显示做响应调整

    
        (GetDlgItem(IDC_EDIT1))->SetWindowText(m_sendfname);
    
        (GetDlgItem(IDC_EDITSEND))->SetWindowText("
准备好发送
");
        
    }
    
    
}

 

准备接收文件按钮和发送文件按钮响应函数如下

void CWinSockDlg::OnRecv() 
{
    sockrecv* sr=
new sockrecv;    //声明一个发送参数结构体
    sr->hwnd=m_hWnd;            //将主窗口句柄传送给它
    HANDLE handl=CreateThread(NULL,0,SocketRecv,(LPVOID)sr,0,NULL);//创建线程用于接收
    CloseHandle(handl);
    
}

void CWinSockDlg::OnSend() 
{
    socksend* ss=
new socksend;  //声明一个发送参数结构体
    
    //
取得IP地址控件上的地址值,设定发送文件名,传递主窗口句柄
    ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(ss->dwip);
    ss->sendfname=m_sendfname;
    ss->hwnd=m_hWnd;
    
    HANDLE handl=CreateThread(NULL,0,SocketSend,(LPVOID)ss,0,NULL);
    CloseHandle(handl);
    
}

 

下面是接收和发送参数结构体的定义:

struct sockrecv
{
    HWND hwnd;
};
struct socksend
{
    HWND hwnd;
    DWORD dwip;
    LPCSTR sendfname;
};

 

下面是最关键的接收线程函数,和发送线程函数:

DWORD WINAPI CWinSockDlg::SocketRecv(LPVOID lpParameter)
{
    HWND hwnd=((sockrecv*)lpParameter)->hwnd;
//将参数传递到本地变量
    CString recvfname;
    
    SOCKET socketrecv=socket(AF_INET,SOCK_STREAM,0); 
//创建套接字
    if(INVALID_SOCKET==socketrecv)
    {
        closesocket(socketrecv);
        ::MessageBox(hwnd,"
套接字创建失败!","警告",MB_OK);
        
return FALSE;
    }
    
    SOCKADDR_IN addrSrv;
    addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
    addrSrv.sin_family=AF_INET;
    addrSrv.sin_port=htons(8848);
    
    
int ret1;
    ret1=bind(socketrecv,(SOCKADDR*)&addrSrv,
sizeof(SOCKADDR)); //绑定套接字
    if(SOCKET_ERROR==ret1)
    {
        closesocket(socketrecv);
        ::MessageBox(hwnd,"
绑定失败!","警告",MB_OK);
        
        
return FALSE;
    }
    
    
int ret2;


    ret2=listen(socketrecv,5);                    
//开始监听
    if(SOCKET_ERROR==ret2)
    {
        closesocket(socketrecv);
        ::MessageBox(hwnd,"
监听失败!","警告",MB_OK);
        
return FALSE;
    }
    
    SOCKADDR_IN addrconn;
    
int len=sizeof(SOCKADDR);
    
    
    
    ::PostMessage(hwnd,UM_PRGRRDY,0,0);        
//向主窗口发送消息
    
    
    SOCKET sockConn=accept(socketrecv,(SOCKADDR*)&addrconn,&len);   
//开始接收
    if(INVALID_SOCKET ==sockConn)        
    {
        closesocket(socketrecv);
        ::MessageBox(hwnd,"
接收失败!","警告",MB_OK);
        
return FALSE;
    }
    
    
    ::PostMessage(hwnd,UM_PRGRBGN,0,0);            
//向主窗口发送消息
    
    BOOL bRet = TRUE; 
    
    
int dataLength, cbBytesRet, cbLeftToReceive;
    
    BYTE* recdData = NULL;
    
    CFile destFile;
    CFileException fe;
    BOOL bFileIsOpen = FALSE;
    

    
    
//当接收到向本地发送文件的请求时弹出文件保存对话框
    CFileDialog fileDlg(FALSE);
    fileDlg.m_ofn.lpstrTitle="
保存将接收到的文件";
    fileDlg.m_ofn.lpstrFilter="All Files(*.*)\0*.*\0\0";
    
if(IDOK==fileDlg.DoModal())
    {
        recvfname=fileDlg.GetPathName();
    }
    
    
    
//用从文件保存对话框中得到的文件名在指定位置创建文件
    if( !( bFileIsOpen = destFile.Open( recvfname, CFile::modeCreate | 
        CFile::modeWrite | CFile::typeBinary, &fe ) ) )
    {
        TCHAR strCause[256];
        fe.GetErrorMessage( strCause, 255 );
        TRACE( "GetFileFromRemoteSender encountered an error while opening the local file "
            " File name = %s Cause = %s m_cause = %d m_IOsError = %d ",
            fe.m_strFileName, strCause, fe.m_cause, fe.m_lOsError );
        
        
        
        bRet = FALSE;
        
goto PreReturnCleanup;
    }
    
    
//首先接收文件的长度信息
    cbLeftToReceive = sizeof( dataLength );   
    
    
do
    {
        BYTE* bp = (BYTE*)(&dataLength) + 
sizeof(dataLength) - cbLeftToReceive;
        cbBytesRet = recv(sockConn, (
char*)bp, cbLeftToReceive,0);
        
        
        
if ( cbBytesRet == SOCKET_ERROR || cbBytesRet == 0 )
        {
            
int iErr = ::GetLastError();
            TRACE( "GetFileFromRemoteSite returned a socket error while getting file length "
                " Number of Bytes received (zero means connection was closed) = %d "
                " GetLastError = %d ", cbBytesRet, iErr );
            
            
            
            bRet = FALSE;
            
goto PreReturnCleanup;
        }
        
        
        
        cbLeftToReceive -= cbBytesRet;
        
    }
    
while ( cbLeftToReceive > 0 );
    
    
    
    dataLength = ntohl( dataLength );  
//将文件的长度信息转化为本地字节序
    
    
    //
准备开始接收文件
    recdData = new byte[RECV_BUFFER_SIZE];
    cbLeftToReceive = dataLength;
    
    
do
    {    
        
//向主窗口发送消息主要用于控制进度条
        ::PostMessage(hwnd,UM_PRGRECV,0,(LPARAM)cbLeftToReceive);
        
int iiGet, iiRecd;
        
        iiGet = (cbLeftToReceive<RECV_BUFFER_SIZE) ? 
cbLeftToReceive : RECV_BUFFER_SIZE ;
        iiRecd = recv(sockConn, (
char*)recdData, iiGet,0 );
        
        
        
if ( iiRecd == SOCKET_ERROR || iiRecd == 0 )
        {
            
int iErr = ::GetLastError();
            TRACE( "GetFileFromRemoteSite returned a socket error while getting chunked file data "
                " Number of Bytes received (zero means connection was closed) = %d "
                " GetLastError = %d ", iiRecd, iErr );
            
            
            
            bRet = FALSE;
            
goto PreReturnCleanup;
        }
        
        
        destFile.Write( recdData, iiRecd); 
        cbLeftToReceive -= iiRecd;
        
    } 
    
while ( cbLeftToReceive > 0 );
    
    ::PostMessage(hwnd,UM_PRGROVER,0,0);   
//向主窗口发送消息
    
PreReturnCleanup:                           
//文件接收结束标签
    
    delete[] recdData;                       
//释放内存
    
    
if ( bFileIsOpen )
        destFile.Close();
    
    
    closesocket(sockConn);
    closesocket(socketrecv);
    
    
return bRet;
    
}

下面是发送线程函数:

 

DWORD WINAPI CWinSockDlg::SocketSend(LPVOID lpParameter)
{
    
//将参数传递给本地变量
    HWND hwnd=((socksend*)lpParameter)->hwnd;
    CString sendfname=((socksend*)lpParameter)->sendfname;
    DWORD dwip=((socksend*)lpParameter)->dwip;
    
    SOCKET socketsend=socket(AF_INET,SOCK_STREAM,0);
//创建Socket
    
    SOCKADDR_IN addrSrv;
    addrSrv.sin_addr.S_un.S_addr=htonl(dwip);
    addrSrv.sin_family=AF_INET;
    addrSrv.sin_port=htons(8848);
    
    
int ret1;
    ret1=connect(socketsend,(SOCKADDR*)&addrSrv,
sizeof(SOCKADDR));//连接指定地址
    
    
if(SOCKET_ERROR==ret1)
    {
        closesocket(socketsend);
        ::MessageBox(hwnd,"
连接失败!","警告",MB_OK);
        
return FALSE;
    }
    
    
    ::PostMessage(hwnd,UM_PRGSBGN,0,0);                
//向主窗口发送消息


    //
声明用于文件发送的本地变量
    BOOL bRet = TRUE;
    
    
int fileLength, cbLeftToSend;
    
    
    BYTE* sendData = NULL;
    
    CFile sourceFile;
    CFileException fe;
    BOOL bFileIsOpen = FALSE;
    

    
//打开参数传递进来的文件用于发送
    if( !( bFileIsOpen = sourceFile.Open( sendfname, 
        CFile::modeRead | CFile::typeBinary, &fe ) ) )
    {
        TCHAR strCause[256];
        fe.GetErrorMessage( strCause, 255 );
        TRACE( "SendFileToRemoteRecipient encountered an error while opening the local file "
            " File name = %s Cause = %s m_cause = %d m_IOsError = %d ",
            fe.m_strFileName, strCause, fe.m_cause, fe.m_lOsError );
        
        
        
        bRet = FALSE;
        
goto PreReturnCleanup;
    }
    
    
    
//首先传递将要被发送文件的长度给接收端
    fileLength = sourceFile.GetLength();
    fileLength = htonl( fileLength );
    
    cbLeftToSend = 
sizeof( fileLength );
    
    
do
    {
        
int cbBytesSent;
        BYTE* bp = (BYTE*)(&fileLength) + 
sizeof(fileLength) - cbLeftToSend;
        cbBytesSent = send(socketsend, (
const char*)bp, cbLeftToSend,0 );
        
        
        
if ( cbBytesSent == SOCKET_ERROR )
        {
            
int iErr = ::GetLastError();
            TRACE( "SendFileToRemoteRecipient returned a socket error while sending file length "
                " Number of Bytes sent = %d "
                " GetLastError = %d ", cbBytesSent, iErr );
            
            
            
            bRet = FALSE;
            
goto PreReturnCleanup;
        }
        
        
        cbLeftToSend -= cbBytesSent;
    }
    
while ( cbLeftToSend>0 );
    
    
    
//开始发送晚间
    sendData = new BYTE[SEND_BUFFER_SIZE]; 
    
    cbLeftToSend = sourceFile.GetLength();
    
    
do
    {
        ::PostMessage(hwnd,UM_PRGSEND,0,(LPARAM)cbLeftToSend);
        
        
int sendThisTime, doneSoFar, buffOffset;
        
        sendThisTime = sourceFile.Read( sendData, SEND_BUFFER_SIZE );
        buffOffset = 0;
        
        
do
        {
            
            
            doneSoFar = send(socketsend,(
const char*) (sendData + buffOffset), 
                sendThisTime,0 ); 
            
            
            
if ( doneSoFar == SOCKET_ERROR )
            {
                
int iErr = ::GetLastError();
                TRACE( "SendFileToRemoteRecipient returned a socket error while sending chunked file data "
                    " Number of Bytes sent = %d "
                    " GetLastError = %d ", doneSoFar, iErr );
                
                
                
                bRet = FALSE;
                
goto PreReturnCleanup;
            }
            
            
            
            buffOffset += doneSoFar;
            sendThisTime -= doneSoFar;
            cbLeftToSend -= doneSoFar;
        }
        
while ( sendThisTime > 0 );
        
    }
    
while ( cbLeftToSend > 0 );
    
    
    ::PostMessage(hwnd,UM_PRGSOVER,0,0);
//向主窗口发送消息发送完毕
    
    PreReturnCleanup:                    
//结束标签后释放内存
    
    
    
    
    delete[] sendData;
    
    
if ( bFileIsOpen )
        sourceFile.Close();
    
    
    closesocket(socketsend);
    
    
return bRet;
    
    
}

到这里主要的文件传送和接收功能已经实现了,剩下的就只有几个控制进度条,和传输接送状态的用户自定义消息了,在对话框的View类中定义如下消息:

 

#define UM_PRGRECV  WM_USER+10
#define
 UM_PRGSEND  WM_USER+11
#define
 UM_PRGROVER WM_USER+12
#define
 UM_PRGSOVER WM_USER+13
#define
 UM_PRGRRDY  WM_USER+14
#define
 UM_PRGRBGN  WM_USER+15
#define UM_PRGSBGN  WM_USER+17

然后分别写这些消息的响应函数:

 

afx_msg void CWinSockDlg::onprgrecv(WPARAM wParam,LPARAM lParam)
{
    
//
控制接收进度条的消息响应函数

    int prgnow=(int)lParam;                //将剩余接收字节作为参数传递给函数
    if(m_beginprgrecv)                    //判断是否第一次接到此消息
    {
        m_prgrecv.SetRange32(0,prgnow); 
//
设置进度条范围

        m_prgrecvpre=prgnow;            //给成员变量赋值    
        m_beginprgrecv=FALSE;            //
改变标志

    }
    
int thistran=m_prgrecvpre-prgnow;   //
本次传输字节数

    int now=m_prgrecv.GetPos();         //先前的总传输量
    m_prgrecv.SetPos(thistran+now);        //设置进度条位置

}

afx_msg 
void CWinSockDlg::onprgrover(WPARAM wParam,LPARAM lParam)
{
    
//
恢复接收状态

    m_prgrecv.SetPos(0);
    m_prgrecvpre=0;
    m_beginprgrecv=TRUE;

    (GetDlgItem(IDC_EDITRECV))->SetWindowText("
尚未准备好接收
");
    
}

afx_msg 
void
 CWinSockDlg::onprgsend(WPARAM wParam,LPARAM lParam)
{
    
//
请参考接收消息响应函数解释

    int prgnow=(int)lParam;
    
if
(m_beginprgsend)
    {
        m_prgsend.SetRange32(0,prgnow);
        m_prgsendpre=prgnow;
        m_beginprgsend=FALSE;
    }
    
int
 thistran=m_prgsendpre-prgnow;
    
int
 now=m_prgsend.GetPos();
    m_prgsend.SetPos(thistran+now);

}

afx_msg 
void
 CWinSockDlg::onprgsover(WPARAM wParam,LPARAM lParam)
{
    
//
恢复发送状态

    m_prgsend.SetPos(0);
    m_prgsendpre=0;
    m_beginprgsend=TRUE;
    (GetDlgItem(IDC_EDITSEND))->SetWindowText("
尚未准备好发送
");
}


afx_msg 
void
 CWinSockDlg::onprgrrdy(WPARAM wParam,LPARAM lParam)
{
(GetDlgItem(IDC_EDITRECV))->SetWindowText("
准备好接收");//UM_PRGRRDY消息响应

}

afx_msg 
void
 CWinSockDlg::onprgrbgn(WPARAM wParam,LPARAM lParam)
{
(GetDlgItem(IDC_EDITRECV))->SetWindowText("
开始接收");  //UM_PRGRBGN消息响应


}
afx_msg 
void CWinSockDlg::onprgsbgn(WPARAM wParam,LPARAM lParam) 

//UM_PRGSBGN消息响应
{
(GetDlgItem(IDC_EDITSEND))->SetWindowText("
开始发送
");

}

到这里所有功能都实现了,是不是很简单.本实例源代码:下载

在.NET中使用简单的值绑定表达式:【上一篇】
动态生成验证码&计数器图片:【下一篇】
【相关文章】
  • Winsock完成端口编程与应用
  • WinSocket模型的探讨——漫谈
  • WinSocket模型的探讨——select模型
  • WinSock网络编程实用宝典
  • WinSocket出错
  • 如何确定 Winsock2 是否损坏并从损坏中恢复
  • 手动修复损坏的winsock2键值
  • 使用Windows API 开发Winsock程序(一)
  • WinSock学习笔记(一)
  • WinSocket工作模式(转)
  • 【随机文章】
  • QQ活跃天查询系统开通 快查查你的新等级!
  • yum配置
  • 从EJB会话bean访问EJB实体bean
  • 很酷的火焰
  • PE文件格式(图)
  • 关于 show sga 结果的描述
  • WEB入侵检测及简单实现
  • 在VB中异步执行程序
  • 流量牵引技术在防DOS攻击中的应用
  • Photoshop图像处理的使用心得
  • 【相关评论】
    没有相关评论
    【发表评论】
    姓名:
    邮件:
    随机码*
    评论*
          
    |  首 页  |  版权声明  |  联系我们   |  网站地图  |
    CopyRight © 2004-2007 软讯网络 All Rigths Reserved.