惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
E
Exploit-DB.com RSS Feed
SecWiki News
SecWiki News
Forbes - Security
Forbes - Security
N
News | PayPal Newsroom
S
Security @ Cisco Blogs
Schneier on Security
Schneier on Security
V
V2EX - 技术
S
Secure Thoughts
W
WeLiveSecurity
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
NISL@THU
NISL@THU
S
Securelist
S
Security Archives - TechRepublic
Know Your Adversary
Know Your Adversary
V
Vulnerabilities – Threatpost
Security Latest
Security Latest
Recent Commits to openclaw:main
Recent Commits to openclaw:main
G
GRAHAM CLULEY
H
Hacker News: Front Page
Microsoft Azure Blog
Microsoft Azure Blog
I
Intezer
Google Online Security Blog
Google Online Security Blog
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
T
The Exploit Database - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Webroot Blog
Webroot Blog
Jina AI
Jina AI
Engineering at Meta
Engineering at Meta
P
Proofpoint News Feed
The Cloudflare Blog
I
InfoQ
L
LangChain Blog
U
Unit 42
P
Proofpoint News Feed
S
Schneier on Security
S
Security Affairs
Y
Y Combinator Blog
T
Tenable Blog
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
量子位
Google DeepMind News
Google DeepMind News
Cyberwarzone
Cyberwarzone
博客园 - 聂微东
D
Darknet – Hacking Tools, Hacker News & Cyber Security
GbyAI
GbyAI
AWS News Blog
AWS News Blog

博客园 - liuym

TB 编程整理 三 ICE开发初级研究 二 示例程序一 - liuym - 博客园 一 VC2008环境中ICE的配置 无法定位序数 3109 于LIBEAY32.dll - liuym 转:Virtual List的使用方法 转:VC++编程之CListCtrl控件的使用 2 转:VC++编程之CListCtrl控件的使用 好用 转:删除,修改注册表中需要设置权限的项 转:软件能够修复硬盘吗?―硬盘损坏全分析 转:CWnd 对象怎么和 HWND 窗口句柄相互转化 VC 多文档窗口 子窗口最大化时切换窗口 窗口没有最大化显示的问题 转:Windows VC6编译安装Boost库 转:如何去了解、熟悉一个已经开发完的项目 进行维护、二次开发或者升级 递归创建文件夹 DLL中资源和主程序资源冲突 - liuym - 博客园 转:一种巧妙的删除程序自己的方法 转:双缓冲图形刷新技术 和 WGF双缓冲绘图框架 软件保护建议(转)
转:送给那些一心想要传送文件的朋友(TCP协议).cpp-from CSDN
liuym · 2010-06-03 · via 博客园 - liuym

//以下是测试结构
typedef struct tagRequest
{
    UINT nMagic;
    UINT ncbSize;
    
//
    
// 这中间可以写点别的
    
//
    
//
    
//数据长度
    UINT nContentLength;
    
}REQUEST,
*PREQUEST,*LPREQUEST;#define    REQUEST_MAGIC        0x12345678    
#define    REQUEST_SIZE         (sizeof(tagRequest))

typedef 

struct tagResponse
{
    UINT nMagic;
    UINT ncbSize;
    
//
    
// 这中间可以写点别的
    
//
    
//
    
//数据长度
    UINT nContentLength;
}RESPONSE,
*PRESPONSE,*LPRESPONSE;#define    RESPONSE_MAGIC        0x87654321
#define    RESPONSE_SIZE         (sizeof(tagResponse))void PopErrorMessage(DWORD dwErrorCode)
{
    LPVOID lpMsgBuf 
= NULL;
    
if( ::FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER 
| //这个是要自动分配一块内存,使用完要通过LocalFree来回收
        FORMAT_MESSAGE_FROM_SYSTEM | 
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dwErrorCode,
//这里是要翻译的代码
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 默认提示语言(也可以随便指定一种)
        (LPTSTR) &lpMsgBuf,
        
0,
        NULL 
        ) 
>0 )
    {
        
// 弹一个对话框出来.
        ::MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
        
// 这里释放掉返回的内存......................
        ::LocalFree( lpMsgBuf );
    }

}

//发送数据
int SendHunk(SOCKET h,char *lpBuf,int nBufLen)
{
    
int nSend = -1 ;
    
int nSendAll = 0;
    
    
if( h != SOCKET_ERROR && lpBuf != NULL && nBufLen > 0  )
    {
        
//循环发送,直到所有数据都发送完毕或出错就返回
        do {              
            nSend 
= ::send(h,                //已经连接的句柄
                           lpBuf+nSendAll,   //跳过已经发送的数据
                           nBufLen-nSendAll, //计算剩余要发送的数据长度
                           0);
            
            
if( nSend > 0 )
                nSendAll 
+=nSend;//累加已经成功发送的数据长度
            else
                
break;
            
        } 
while( nSendAll < nBufLen );
    }
    
    
return nSendAll;
}
//接收数据
int RecvHunk(SOCKET h,char *lpBuf,int nBufLen,int nWillLen)
{
    
int nRecv = -1 ;
    
int nRecvAll = 0;if( h != SOCKET_ERROR && lpBuf != NULL && nBufLen > 0  && nWillLen >0 && nBufLen >= nWillLen  )
    {
        
//循环接收,直到数据达到要接收的长度或出错就返回
        do {
            nRecv 
= ::recv(h, //已经连接的句柄
                           lpBuf+nRecvAll,//跳过已经接收的数据
                           nWillLen-nRecvAll,//计算剩余要接收的数据长度
                           0);
            
if( nRecv >0 )
                nRecvAll 
+= nRecv;//当前已经接收的长度
            else
                
break;
                
        } 
while( nRecvAll < nWillLen );
    }
return nRecvAll;
}
void Client()
{
    
//发送一个变长的数据块
    
    
//
    
//创建一个句柄(注:默认的情况下会返回一个阻塞句柄.)                
    SOCKET hSocket = ::socket(AF_INET,        // IPV4
                              SOCK_STREAM,    // 数据流 (像河水一样连绵不断,当然也有断流的时候.^-^)  
                              IPPROTO_TCP    // TCP协议(一部分书上说填0,让协议自己选择)
                              );
    
    
if( hSocket == SOCKET_ERROR )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
//定义一个结构用来保存要连接的远程地址和端口
    sockaddr_in stRemote = {0};
    
    stRemote.sin_family
=AF_INET; //IPV4
    stRemote.sin_addr.s_addr = inet_addr("127.0.0.1"); //远程地址(这里用的本机回路地址)
    stRemote.sin_port=htons(12345);//一个端口(这里是随便写的)
    
//连接远程主机      
    int nReConn = ::connect(hSocket,//前面创建的句柄
                            (SOCKADDR*)&stRemote, //上面的结构(已经填好远程主机的地址和端口)
                            sizeof(stRemote)      //结构体长度    
                            );if( nReConn == SOCKET_ERROR )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭句柄,并初化为一个错误值
        ::closesocket(hSocket);
        hSocket 
= SOCKET_ERROR;
        
//没办法继续干活了,退出吧!
        return ;
    }
char * szText[] = {"小鸟,小鸟,我是鸟巢,收到请回答!",
                      
"老鸟,老鸟,我是小鸟,收到请回答!",
                      
"呜呜,为什么没有回答呀?",
                      
"下雪了,该收衣服了!",
                      
"天冷了."};//随机选一个字符串发过去
    char * szRequestText = szText[rand()%(sizeof(szRequestText)/sizeof(szRequestText[0]))];

    tagRequest stReq 

= {0};

    stReq.nMagic 

= REQUEST_MAGIC; //结构体识别编码(随便什么都行,不一定要用数值)
    stReq.ncbSize = REQUEST_SIZE; //填上结构体大小,以识别目前能不能处理.
                                  
//有可能结构体大小发生变化,这种情通常出现在产品的不同版本中,本例不考虑这种情况.
    stReq.nContentLength = ::lstrlen(szRequestText)+1;// 要发送变长数据体长度 (这里的加1,是要把末尾的结束符'\0'也发送过去)//这里用的是封装后的send 具体到上面看吧
    
//发送请求头
    
//返回是已经发送的数据长度
    int nSend = ::SendHunk(hSocket, //前面创建的句柄
                            (char*)&stReq, // 请求结构体指针
                            REQUEST_SIZE  // 请示结构体长度
                            );if( nSend != REQUEST_SIZE )
    {
        
//如果返回的长度不等于结构体长度,表示出错了.//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭句柄,并初化为一个错误值
        ::closesocket(hSocket);
        hSocket 
= SOCKET_ERROR;
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
//发送完结体后,接着发送变长体数据
    nSend = ::SendHunk(hSocket, //前面创建的句柄
                        szRequestText, // 变长数据
                        stReq.nContentLength  // 变长数据长度
                        );if( nSend != stReq.nContentLength )
    {
        
//如果返回的长度不等于变长数据长度,表示出错了.
        
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭句柄,并初化为一个错误值
        ::closesocket(hSocket);
        hSocket 
= SOCKET_ERROR;//没办法继续干活了,退出吧!
        return ;
    }
//关闭句柄,并初化为一个错误值
    ::closesocket(hSocket);
    hSocket 
= SOCKET_ERROR;
}
void OnAccept(SOCKET hClient)
{    
    tagRequest stReq 
= {0};
    
    
//接收数据
    
//先接受请求头
    int nRecv = ::RecvHunk(hClient, //上面接收连接句柄
        (char*)&stReq,         //接收缓冲区
        REQUEST_SIZE, //可用的缓冲区大小
        REQUEST_SIZE   //将要接收的数据(这里填的请求头大小,也就是小于这个的将不与处理)
        );
    
    
if( nRecv != REQUEST_SIZE )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭连接句柄
        ::closesocket(hClient);
        hClient 
= SOCKET_ERROR;
        
        
//没办法继续干活了,退出吧!
        return ;
        
    }
    
    
//验证结构是否合法
    if( stReq.nMagic != REQUEST_MAGIC || stReq.ncbSize != REQUEST_SIZE)
    {
        
//哇,非法数据,警告!!这不是我们自己人!!!
        
        
//关闭连接句柄
        ::closesocket(hClient);
        hClient 
= SOCKET_ERROR;
        
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
//在这里可以验证一下其它请求头参数(注:本例不验证).
    
    
char * pszRequestText = new char[stReq.nContentLength];
    
    
if( pszRequestText )
    {
        
//再接收变长数据
        int nRecv = ::RecvHunk(hClient, //上面接收连接句柄
            pszRequestText,    //接收缓冲区
            stReq.nContentLength, //可用的缓冲区大小
            stReq.nContentLength   //将要接收的数据大小
            );
        
        
if( nRecv != stReq.nContentLength)
        {
            
//唉!数据呢?????
            
            
//关闭连接句柄
            ::closesocket(hClient);
            hClient 
= SOCKET_ERROR;
            
            
//回收上面申请的内存
            delete []pszRequestText;
            pszRequestText 
= NULL;
            
            
//没办法继续干活了,退出吧!
            return ;
        }
        
        
        
//
        ::MessageBox(NULL,pszRequestText,"瞧瞧发来是什么",MB_OK | MB_ICONINFORMATION);
        
        
//回收上面申请的内存
        delete []pszRequestText;
        pszRequestText 
= NULL;
        
        
    }
    
    
//关闭连接句柄
    ::closesocket(hClient);
    hClient 
= SOCKET_ERROR;
}

DWORD WINAPI ThreadOnAccept(LPVOID lpData)
{
    OnAccept((SOCKET)lpData);

return 0;
}
void Server()
{
    
//接收一个变长的数据块
    
    
//
    
//创建一个句柄(注:默认的情况下会返回一个阻塞句柄.)                
    SOCKET hSocket = ::socket(AF_INET,        // IPV4
        SOCK_STREAM,    // 数据流 (像河水一样连绵不断,当然也有断流的时候.^-^)  
        IPPROTO_TCP    // TCP协议(一部分书上说填0,让协议自己选择)
        );
    
    
if( hSocket == SOCKET_ERROR )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
    
//定义一个结构用来保存本机地址和端口
    sockaddr_in stLocal = {0};
    
    stLocal.sin_family
=AF_INET; //IPV4
    stLocal.sin_addr.s_addr = inet_addr("127.0.0.1"); //本机要使用IP地址(这里用的本机回路地址)
    stLocal.sin_port=htons(12345);//一个端口(这里是随便写的)
    
    
int nReBind = ::bind(hSocket,//前面创建的句柄
        (SOCKADDR*)&stLocal, //上面的结构(已经填好远程主机的地址和端口)
        sizeof(stLocal)      //结构体长度    
        );
    
    
    
if( nReBind == SOCKET_ERROR )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭句柄,并初化为一个错误值
        ::closesocket(hSocket);
        hSocket 
= SOCKET_ERROR;
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
//设置监听数量
    int nRelisten = ::listen(hSocket,//前面创建的句柄
                            SOMAXCONN //同时连接数,(注:这里的连接数,指的是同时可以接受的请求连接数.不是指可以连接到这个端口的数量)
                            );if( nRelisten == SOCKET_ERROR )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭句柄,并初化为一个错误值
        ::closesocket(hSocket);
        hSocket 
= SOCKET_ERROR;
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
do {
        
//定义一个结构用来保存要连接的远程地址和端口
        sockaddr_in stRemote = {0};
        
int nRemoteLen = sizeof(stRemote);
        
//等待远程连接到的到来.(从理论上讲,只要资源足够,可以接受足够多个连接).
        
//返回一个新的连接句柄
        SOCKET hSocketClient = ::accept(hSocket,//前面创建的句柄
            (SOCKADDR*)&stRemote, //保存已经连接远程主机地址
            &nRemoteLen            //保存已经连接远程主机地址长度(注:这个值在传入时表示可用的缓冲区长度,传出时表示已经保存的地址长度)
            );
        
        
if( hSocketClient == SOCKET_ERROR )
        {
            
//哇,出错了,//看看是什么错.
            DWORD dwErrorCode = ::WSAGetLastError();
            
//怎么是数字啊!!看不懂,翻译一下.^-^
            PopErrorMessage(dwErrorCode);
            
//关闭句柄,并初化为一个错误值
            ::closesocket(hSocket);
            hSocket 
= SOCKET_ERROR;
            
//没办法继续干活了,退出吧!
            return ;
        }
        
        
        HANDLE hThClient 
= ::CreateThread(NULL,0,ThreadOnAccept,
            (LPVOID)hSocketClient,
//这个句柄会在线程内关闭
            0,NULL);//关闭线句柄(注:线程不会退出的!!)
        ::CloseHandle(hThClient);
        hThClient 
= NULL;
            
    } 
while(TRUE);return;
}

DWORD WINAPI ThreadServer(LPVOID lpData)
{
    Server();

return 0;
}
//发送文件,(这里用的CFile类只发送最大4GB的文件)
//如果要发送大于4GB的文件,需要MFC类库版本支持,或者自己构造一相类似功能类,这里就不说的了,相信你轻松就能搞定的.)
//注:这个函数可以直持多线程分段发送,在这里就不举例了,自己在实际应用当中去实现吧!前提是你得对多线程有所了解才行哦.^-^
#define DEFAULT_BUFFER_SIZE     4096

UINT SendFile(SOCKET h,     

//SOCKET句柄(这里要独占,否则可以出现发送数据混乱,具体原因....见下一行
              
//这里是分段发送,而SOKCET本身是线程安全的,
              
//如果这时再有其它线程用这个句柄发送(接收的就无所为了<TCP是全双工....>),
              
//就会造成在当前的数据流中插入其它线程发送的数据,产生的问题就不可预测了.呵呵)
              LPCTSTR szPathFile,    //文件名
              UINT nStart /*= 0*/,    //文件块起点
              UINT nEnd /*= ~0*/ //文件块终点(这里的~0表示直到末尾)
)
{
    UINT nSend 
= 0 ;
    UINT nSendAll 
= 0;

    CFile f;

//共享读的方式打开文件
    if( f.Open(szPathFile,CFile::modeRead|CFile::shareDenyRead) )
    {
        
//不能超过文件末尾
        if( nEnd > f.GetLength() )
            nEnd 
= f.GetLength();//必须有交集 
        if( nStart < nEnd )
        {    
            
//计算要发送数据长度
            UINT nDataLen = nEnd - nStart;//移动到要发送的起点
            if( nStart == f.Seek(nStart,CFile::begin) )
            {
                ULONG nRead 
= 0;
                BYTE cBuf[DEFAULT_BUFFER_SIZE] 
= {0};
                
//得有数据才成(因为是无符号数,而CFile类本身,不会返回一个-1出来,固这里没写 " (nRead = (读文件)) > 0) " )
                while( nRead = f.Read(cBuf, min(DEFAULT_BUFFER_SIZE,nDataLen-nSendAll)) )
                {
                    
//发送
                    nSend = ::SendHunk(h,(char*)cBuf,nRead);
                    
//为什么这里直判断相等呢?是因为上面的SendHunk已经尝试发送全部了,包括接收端缓冲区不够再次接收的情况.
                    if( nSend == nRead )
                    {
                        
//已发送累加
                        nSendAll +=nSend;
                    }
                    
else
                    {    
                        
//发送失败,终止
                        break;
                    }
                }
            }
        }
//关闭文件
        f.Close();
    }
    
    
//返回已经发送的长度
    return nSendAll;
}
//****要注意哦,这里的注释和SendFile的注释不同哦****////接收文件,(这里用的CFile类只发送最大4GB的文件)
//如果要接收大于4GB的文件,需要MFC类库版本支持,或者自己构造一相类似功能类,这里就不说的了,相信你轻松就能搞定的.)

UINT RecvFile(SOCKET h,     
//SOCKET句柄(这里要独占,具体原因....见下一行    
              
//这里是分段发送,而SOKCET本身是线程安全的,
              
//如果这时再有其它线程用这个句柄接收(发送的就无所为了<TCP是全双工....>),
              
//就会造成在当前的数据流中的数据被其它线程接收到,产生的问题就不可预测了.呵呵)
              LPCTSTR szPathFile,//文件名
              UINT nStart ,    //写入文件块起点
              UINT nWillLen //将要接收的数据长度
)
{
    UINT nRecv 
= 0 ;
    UINT nRecvAll 
= 0;

    CFile f;

//打开文件
    if( f.Open(szPathFile,CFile::modeCreate|     //要创建新文件附加条件CFile::modeNoInherit
        CFile::modeWrite|     //只写
        CFile::modeNoInherit //已经存在就打开,不存在就创建
    ) )
    {
        
//移动到要写入的起点(如果返回值不等于nStart ,可能磁盘空间不足,因此要退出)
        
//当然了这么作并不能真正的验证空间是否足够.
        
//提示作法:nStart+
        if( nStart == f.Seek(nStart,CFile::begin) )
        {
            BYTE cBuf[DEFAULT_BUFFER_SIZE] 
= {0};do {
                
//接收数据
                nRecv = RecvHunk(h,(char*)cBuf,DEFAULT_BUFFER_SIZE,
                             min((nWillLen
-nRecvAll), DEFAULT_BUFFER_SIZE) //计算要接收的数据块(分接收)
                             );
            
//因为接收时已经尝试填满缓冲区才返回,而数据总量又不一定很整齐,这里"整齐"指的是 DEFAULT_BUFFER_SIZE 的倍数
                if( nRecv != 0 )
                {
                    
try
                    {
                        
//写到文件里 
                        f.Write(cBuf,nRecv);

                        nRecvAll 

+= nRecv; //累加计数
                    }
                    
//    catch (CFileException &e ) //在这里不处理异常了
                    catch(...)
                    {
                        
//都出错了还能干什么?退出吧!
                        break;
                    }
                }
                
else
                {
                    
break;
                }

            } 

while( nRecvAll < nWillLen );
        }
//关闭文件
        f.Close();
    }
//返回已经接收的长度
    return nRecvAll;
}

//以下是测试结构
typedef struct tagRequest
{
    UINT nMagic;
    UINT ncbSize;
    
//
    
// 这中间可以写点别的
    
//
    
//
    
//数据长度
    UINT nContentLength;
    
}REQUEST,
*PREQUEST,*LPREQUEST;#define    REQUEST_MAGIC        0x12345678    
#define    REQUEST_SIZE         (sizeof(tagRequest))

typedef 

struct tagResponse
{
    UINT nMagic;
    UINT ncbSize;
    
//
    
// 这中间可以写点别的
    
//
    
//
    
//数据长度
    UINT nContentLength;
}RESPONSE,
*PRESPONSE,*LPRESPONSE;#define    RESPONSE_MAGIC        0x87654321
#define    RESPONSE_SIZE         (sizeof(tagResponse))void PopErrorMessage(DWORD dwErrorCode)
{
    LPVOID lpMsgBuf 
= NULL;
    
if( ::FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER 
| //这个是要自动分配一块内存,使用完要通过LocalFree来回收
        FORMAT_MESSAGE_FROM_SYSTEM | 
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dwErrorCode,
//这里是要翻译的代码
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 默认提示语言(也可以随便指定一种)
        (LPTSTR) &lpMsgBuf,
        
0,
        NULL 
        ) 
>0 )
    {
        
// 弹一个对话框出来.
        ::MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
        
// 这里释放掉返回的内存......................
        ::LocalFree( lpMsgBuf );
    }

}

//发送数据
int SendHunk(SOCKET h,char *lpBuf,int nBufLen)
{
    
int nSend = -1 ;
    
int nSendAll = 0;
    
    
if( h != SOCKET_ERROR && lpBuf != NULL && nBufLen > 0  )
    {
        
//循环发送,直到所有数据都发送完毕或出错就返回
        do {              
            nSend 
= ::send(h,                //已经连接的句柄
                           lpBuf+nSendAll,   //跳过已经发送的数据
                           nBufLen-nSendAll, //计算剩余要发送的数据长度
                           0);
            
            
if( nSend > 0 )
                nSendAll 
+=nSend;//累加已经成功发送的数据长度
            else
                
break;
            
        } 
while( nSendAll < nBufLen );
    }
    
    
return nSendAll;
}
//接收数据
int RecvHunk(SOCKET h,char *lpBuf,int nBufLen,int nWillLen)
{
    
int nRecv = -1 ;
    
int nRecvAll = 0;if( h != SOCKET_ERROR && lpBuf != NULL && nBufLen > 0  && nWillLen >0 && nBufLen >= nWillLen  )
    {
        
//循环接收,直到数据达到要接收的长度或出错就返回
        do {
            nRecv 
= ::recv(h, //已经连接的句柄
                           lpBuf+nRecvAll,//跳过已经接收的数据
                           nWillLen-nRecvAll,//计算剩余要接收的数据长度
                           0);
            
if( nRecv >0 )
                nRecvAll 
+= nRecv;//当前已经接收的长度
            else
                
break;
                
        } 
while( nRecvAll < nWillLen );
    }
return nRecvAll;
}
void Client()
{
    
//发送一个变长的数据块
    
    
//
    
//创建一个句柄(注:默认的情况下会返回一个阻塞句柄.)                
    SOCKET hSocket = ::socket(AF_INET,        // IPV4
                              SOCK_STREAM,    // 数据流 (像河水一样连绵不断,当然也有断流的时候.^-^)  
                              IPPROTO_TCP    // TCP协议(一部分书上说填0,让协议自己选择)
                              );
    
    
if( hSocket == SOCKET_ERROR )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
//定义一个结构用来保存要连接的远程地址和端口
    sockaddr_in stRemote = {0};
    
    stRemote.sin_family
=AF_INET; //IPV4
    stRemote.sin_addr.s_addr = inet_addr("127.0.0.1"); //远程地址(这里用的本机回路地址)
    stRemote.sin_port=htons(12345);//一个端口(这里是随便写的)
    
//连接远程主机      
    int nReConn = ::connect(hSocket,//前面创建的句柄
                            (SOCKADDR*)&stRemote, //上面的结构(已经填好远程主机的地址和端口)
                            sizeof(stRemote)      //结构体长度    
                            );if( nReConn == SOCKET_ERROR )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭句柄,并初化为一个错误值
        ::closesocket(hSocket);
        hSocket 
= SOCKET_ERROR;
        
//没办法继续干活了,退出吧!
        return ;
    }
char * szText[] = {"小鸟,小鸟,我是鸟巢,收到请回答!",
                      
"老鸟,老鸟,我是小鸟,收到请回答!",
                      
"呜呜,为什么没有回答呀?",
                      
"下雪了,该收衣服了!",
                      
"天冷了."};//随机选一个字符串发过去
    char * szRequestText = szText[rand()%(sizeof(szRequestText)/sizeof(szRequestText[0]))];

    tagRequest stReq 

= {0};

    stReq.nMagic 

= REQUEST_MAGIC; //结构体识别编码(随便什么都行,不一定要用数值)
    stReq.ncbSize = REQUEST_SIZE; //填上结构体大小,以识别目前能不能处理.
                                  
//有可能结构体大小发生变化,这种情通常出现在产品的不同版本中,本例不考虑这种情况.
    stReq.nContentLength = ::lstrlen(szRequestText)+1;// 要发送变长数据体长度 (这里的加1,是要把末尾的结束符'\0'也发送过去)//这里用的是封装后的send 具体到上面看吧
    
//发送请求头
    
//返回是已经发送的数据长度
    int nSend = ::SendHunk(hSocket, //前面创建的句柄
                            (char*)&stReq, // 请求结构体指针
                            REQUEST_SIZE  // 请示结构体长度
                            );if( nSend != REQUEST_SIZE )
    {
        
//如果返回的长度不等于结构体长度,表示出错了.//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭句柄,并初化为一个错误值
        ::closesocket(hSocket);
        hSocket 
= SOCKET_ERROR;
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
//发送完结体后,接着发送变长体数据
    nSend = ::SendHunk(hSocket, //前面创建的句柄
                        szRequestText, // 变长数据
                        stReq.nContentLength  // 变长数据长度
                        );if( nSend != stReq.nContentLength )
    {
        
//如果返回的长度不等于变长数据长度,表示出错了.
        
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭句柄,并初化为一个错误值
        ::closesocket(hSocket);
        hSocket 
= SOCKET_ERROR;//没办法继续干活了,退出吧!
        return ;
    }
//关闭句柄,并初化为一个错误值
    ::closesocket(hSocket);
    hSocket 
= SOCKET_ERROR;
}
void OnAccept(SOCKET hClient)
{    
    tagRequest stReq 
= {0};
    
    
//接收数据
    
//先接受请求头
    int nRecv = ::RecvHunk(hClient, //上面接收连接句柄
        (char*)&stReq,         //接收缓冲区
        REQUEST_SIZE, //可用的缓冲区大小
        REQUEST_SIZE   //将要接收的数据(这里填的请求头大小,也就是小于这个的将不与处理)
        );
    
    
if( nRecv != REQUEST_SIZE )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭连接句柄
        ::closesocket(hClient);
        hClient 
= SOCKET_ERROR;
        
        
//没办法继续干活了,退出吧!
        return ;
        
    }
    
    
//验证结构是否合法
    if( stReq.nMagic != REQUEST_MAGIC || stReq.ncbSize != REQUEST_SIZE)
    {
        
//哇,非法数据,警告!!这不是我们自己人!!!
        
        
//关闭连接句柄
        ::closesocket(hClient);
        hClient 
= SOCKET_ERROR;
        
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
//在这里可以验证一下其它请求头参数(注:本例不验证).
    
    
char * pszRequestText = new char[stReq.nContentLength];
    
    
if( pszRequestText )
    {
        
//再接收变长数据
        int nRecv = ::RecvHunk(hClient, //上面接收连接句柄
            pszRequestText,    //接收缓冲区
            stReq.nContentLength, //可用的缓冲区大小
            stReq.nContentLength   //将要接收的数据大小
            );
        
        
if( nRecv != stReq.nContentLength)
        {
            
//唉!数据呢?????
            
            
//关闭连接句柄
            ::closesocket(hClient);
            hClient 
= SOCKET_ERROR;
            
            
//回收上面申请的内存
            delete []pszRequestText;
            pszRequestText 
= NULL;
            
            
//没办法继续干活了,退出吧!
            return ;
        }
        
        
        
//
        ::MessageBox(NULL,pszRequestText,"瞧瞧发来是什么",MB_OK | MB_ICONINFORMATION);
        
        
//回收上面申请的内存
        delete []pszRequestText;
        pszRequestText 
= NULL;
        
        
    }
    
    
//关闭连接句柄
    ::closesocket(hClient);
    hClient 
= SOCKET_ERROR;
}

DWORD WINAPI ThreadOnAccept(LPVOID lpData)
{
    OnAccept((SOCKET)lpData);

return 0;
}
void Server()
{
    
//接收一个变长的数据块
    
    
//
    
//创建一个句柄(注:默认的情况下会返回一个阻塞句柄.)                
    SOCKET hSocket = ::socket(AF_INET,        // IPV4
        SOCK_STREAM,    // 数据流 (像河水一样连绵不断,当然也有断流的时候.^-^)  
        IPPROTO_TCP    // TCP协议(一部分书上说填0,让协议自己选择)
        );
    
    
if( hSocket == SOCKET_ERROR )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
    
//定义一个结构用来保存本机地址和端口
    sockaddr_in stLocal = {0};
    
    stLocal.sin_family
=AF_INET; //IPV4
    stLocal.sin_addr.s_addr = inet_addr("127.0.0.1"); //本机要使用IP地址(这里用的本机回路地址)
    stLocal.sin_port=htons(12345);//一个端口(这里是随便写的)
    
    
int nReBind = ::bind(hSocket,//前面创建的句柄
        (SOCKADDR*)&stLocal, //上面的结构(已经填好远程主机的地址和端口)
        sizeof(stLocal)      //结构体长度    
        );
    
    
    
if( nReBind == SOCKET_ERROR )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭句柄,并初化为一个错误值
        ::closesocket(hSocket);
        hSocket 
= SOCKET_ERROR;
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
//设置监听数量
    int nRelisten = ::listen(hSocket,//前面创建的句柄
                            SOMAXCONN //同时连接数,(注:这里的连接数,指的是同时可以接受的请求连接数.不是指可以连接到这个端口的数量)
                            );if( nRelisten == SOCKET_ERROR )
    {
        
//哇,出错了,//看看是什么错.
        DWORD dwErrorCode = ::WSAGetLastError();
        
//怎么是数字啊!!看不懂,翻译一下.^-^
        PopErrorMessage(dwErrorCode);
        
//关闭句柄,并初化为一个错误值
        ::closesocket(hSocket);
        hSocket 
= SOCKET_ERROR;
        
//没办法继续干活了,退出吧!
        return ;
    }
    
    
do {
        
//定义一个结构用来保存要连接的远程地址和端口
        sockaddr_in stRemote = {0};
        
int nRemoteLen = sizeof(stRemote);
        
//等待远程连接到的到来.(从理论上讲,只要资源足够,可以接受足够多个连接).
        
//返回一个新的连接句柄
        SOCKET hSocketClient = ::accept(hSocket,//前面创建的句柄
            (SOCKADDR*)&stRemote, //保存已经连接远程主机地址
            &nRemoteLen            //保存已经连接远程主机地址长度(注:这个值在传入时表示可用的缓冲区长度,传出时表示已经保存的地址长度)
            );
        
        
if( hSocketClient == SOCKET_ERROR )
        {
            
//哇,出错了,//看看是什么错.
            DWORD dwErrorCode = ::WSAGetLastError();
            
//怎么是数字啊!!看不懂,翻译一下.^-^
            PopErrorMessage(dwErrorCode);
            
//关闭句柄,并初化为一个错误值
            ::closesocket(hSocket);
            hSocket 
= SOCKET_ERROR;
            
//没办法继续干活了,退出吧!
            return ;
        }
        
        
        HANDLE hThClient 
= ::CreateThread(NULL,0,ThreadOnAccept,
            (LPVOID)hSocketClient,
//这个句柄会在线程内关闭
            0,NULL);//关闭线句柄(注:线程不会退出的!!)
        ::CloseHandle(hThClient);
        hThClient 
= NULL;
            
    } 
while(TRUE);return;
}

DWORD WINAPI ThreadServer(LPVOID lpData)
{
    Server();

return 0;
}
//发送文件,(这里用的CFile类只发送最大4GB的文件)
//如果要发送大于4GB的文件,需要MFC类库版本支持,或者自己构造一相类似功能类,这里就不说的了,相信你轻松就能搞定的.)
//注:这个函数可以直持多线程分段发送,在这里就不举例了,自己在实际应用当中去实现吧!前提是你得对多线程有所了解才行哦.^-^
#define DEFAULT_BUFFER_SIZE     4096

UINT SendFile(SOCKET h,     

//SOCKET句柄(这里要独占,否则可以出现发送数据混乱,具体原因....见下一行
              
//这里是分段发送,而SOKCET本身是线程安全的,
              
//如果这时再有其它线程用这个句柄发送(接收的就无所为了<TCP是全双工....>),
              
//就会造成在当前的数据流中插入其它线程发送的数据,产生的问题就不可预测了.呵呵)
              LPCTSTR szPathFile,    //文件名
              UINT nStart /*= 0*/,    //文件块起点
              UINT nEnd /*= ~0*/ //文件块终点(这里的~0表示直到末尾)
)
{
    UINT nSend 
= 0 ;
    UINT nSendAll 
= 0;

    CFile f;

//共享读的方式打开文件
    if( f.Open(szPathFile,CFile::modeRead|CFile::shareDenyRead) )
    {
        
//不能超过文件末尾
        if( nEnd > f.GetLength() )
            nEnd 
= f.GetLength();//必须有交集 
        if( nStart < nEnd )
        {    
            
//计算要发送数据长度
            UINT nDataLen = nEnd - nStart;//移动到要发送的起点
            if( nStart == f.Seek(nStart,CFile::begin) )
            {
                ULONG nRead 
= 0;
                BYTE cBuf[DEFAULT_BUFFER_SIZE] 
= {0};
                
//得有数据才成(因为是无符号数,而CFile类本身,不会返回一个-1出来,固这里没写 " (nRead = (读文件)) > 0) " )
                while( nRead = f.Read(cBuf, min(DEFAULT_BUFFER_SIZE,nDataLen-nSendAll)) )
                {
                    
//发送
                    nSend = ::SendHunk(h,(char*)cBuf,nRead);
                    
//为什么这里直判断相等呢?是因为上面的SendHunk已经尝试发送全部了,包括接收端缓冲区不够再次接收的情况.
                    if( nSend == nRead )
                    {
                        
//已发送累加
                        nSendAll +=nSend;
                    }
                    
else
                    {    
                        
//发送失败,终止
                        break;
                    }
                }
            }
        }
//关闭文件
        f.Close();
    }
    
    
//返回已经发送的长度
    return nSendAll;
}
//****要注意哦,这里的注释和SendFile的注释不同哦****////接收文件,(这里用的CFile类只发送最大4GB的文件)
//如果要接收大于4GB的文件,需要MFC类库版本支持,或者自己构造一相类似功能类,这里就不说的了,相信你轻松就能搞定的.)

UINT RecvFile(SOCKET h,     
//SOCKET句柄(这里要独占,具体原因....见下一行    
              
//这里是分段发送,而SOKCET本身是线程安全的,
              
//如果这时再有其它线程用这个句柄接收(发送的就无所为了<TCP是全双工....>),
              
//就会造成在当前的数据流中的数据被其它线程接收到,产生的问题就不可预测了.呵呵)
              LPCTSTR szPathFile,//文件名
              UINT nStart ,    //写入文件块起点
              UINT nWillLen //将要接收的数据长度
)
{
    UINT nRecv 
= 0 ;
    UINT nRecvAll 
= 0;

    CFile f;

//打开文件
    if( f.Open(szPathFile,CFile::modeCreate|     //要创建新文件附加条件CFile::modeNoInherit
        CFile::modeWrite|     //只写
        CFile::modeNoInherit //已经存在就打开,不存在就创建
    ) )
    {
        
//移动到要写入的起点(如果返回值不等于nStart ,可能磁盘空间不足,因此要退出)
        
//当然了这么作并不能真正的验证空间是否足够.
        
//提示作法:nStart+
        if( nStart == f.Seek(nStart,CFile::begin) )
        {
            BYTE cBuf[DEFAULT_BUFFER_SIZE] 
= {0};do {
                
//接收数据
                nRecv = RecvHunk(h,(char*)cBuf,DEFAULT_BUFFER_SIZE,
                             min((nWillLen
-nRecvAll), DEFAULT_BUFFER_SIZE) //计算要接收的数据块(分接收)
                             );
            
//因为接收时已经尝试填满缓冲区才返回,而数据总量又不一定很整齐,这里"整齐"指的是 DEFAULT_BUFFER_SIZE 的倍数
                if( nRecv != 0 )
                {
                    
try
                    {
                        
//写到文件里 
                        f.Write(cBuf,nRecv);

                        nRecvAll 

+= nRecv; //累加计数
                    }
                    
//    catch (CFileException &e ) //在这里不处理异常了
                    catch(...)
                    {
                        
//都出错了还能干什么?退出吧!
                        break;
                    }
                }
                
else
                {
                    
break;
                }

            } 

while( nRecvAll < nWillLen );
        }
//关闭文件
        f.Close();
    }
//返回已经接收的长度
    return nRecvAll;
}