socket文件接收流程
一、socket接收数据操作
1、接收数据包类型
//文本和文件
enum DataPackage {DP_TEXT, DP_FILE};
2、数据发送命令
//开始发送,发送过程中, 结束发送, 接受文件发送,拒绝接收文件,取消文件发送或接收
enum SendCmd {SC_BEGIN, SC_SENDING, SC_END, SC_ACCEPT, SC_DENY, SC_CANCEL};
3、对方对发送文件的回答,接收,拒绝和取消(发送过程中)
enum RequestType {RT_ACCEPT, RT_DENY, RT_CANCEL, RT_UNKNOWN};
4 定义数据包结构,在文件开始发送时,数据缓冲区中前128个字节用于存储文件名,其后是文件数据,而在文件发送过程中,
数据缓冲区中均是文件数据
5、数据包结构体 CDataPakage
class CDataPackage
{
public:
DataPackage m_Type; //数据包类型
SendCmd m_Cmd; //文件发送命令
DWORD m_dwSize; //数据报结构大小
DWORD m_dwFileSize; //整个文件大小
DWORD m_dwData; //m_Data的大小
BYTE m_Data[]; //数据缓冲区
};
6、定义变量值
CClientSock m_ClientSock; //定义本地套接字
HGLOBAL m_hGlobal; //全局堆句柄
BOOL m_bSending; //文件是否处于发送过程中
7、文件接收流程、步骤
(1)、自定义设置一次接收文件大小nMaxLen、全局缓存变量hGlobal、接收缓冲区变量指针pBuffer、实际接收大小nFact、
以及数据包是否完整bCompleted和是否第一次标记bFirstRec
(2)、计算数据包的大小 nPackage
(3)、如果是第一次接收,认为数据包中一定包含完整的数据包结构信息,数据不一定完整
获取缓冲区数据(pBuffer):
3.1、如果实际接收大小正好是一个数据包的大小,直接写入文件,标记本次完成bCompleted=true,下一次(bFirstRec=true)
,进行下一次接收(步骤三)
nFact - nPackage(包结构大小) < CDataPackage->m_dwData
3.2、数据包不完整,可能大于一个数据包也可能小于一个数据包
3.2.1、当小于数据包大小(数据包中没有包含完整的数据,产生了分包)nFact - nPackage(包结构大小)< CDataPackage->m_dwData
将数据存入全局变量hGlobal,合并到下一次数据中,此时设置bFirstRec = FALSE
3.2.2、当大于数据包大小 (数据包中包含不止一个数据包,产生了粘包)
先写入一个数据包到文件,在计算剩余数据包的大小nLeaving,循环nLeaving:
3.2.2.1 nLeaving > nPackage数据包含有完整的数据包结构(即可以获取数据包的其他信息nPackage(包结构大小))
如果数据中包含一个或一个以上的数据包,直接写入数据包到文件,nLeaving-=nPackage+CDataPackage->m_dwData;
如果剩余数据小于一个数据包,操作和 3.2.1步骤相同,此时bCompleted = FALSE; bFirstRec = FALSE;
3.2.2.1 nLeaving < nPackage 步骤和3.2.1相同此时nLeaving = 0; bCompleted = FALSE;
(4)、如果是第不是第一次接收
4.1、如果之前接收的数据包是完整的(bCompleted=true)时,返回到步骤(3)
4.2、如果之前的数据包产生了分包或粘包(bCompleted == FALSE),继续接收数据
4.2.1将全局变量hGlobal值取出到dwSize:
4.2.1.1、dwSize<nPackage(如果数据包结构不完整,先接收数据包的结构),将这次实际接收值nFact与dwSize求和与nPackage比较,
4.2.1.1.1、dwSize + nFact < nPackage(当前的数据添加到堆中仍不能描述完整的数据包结构),将dwSize + nFact字节大小数据值
放入全局变量hGlobal操作和 3.2.1步骤相同,此时bCompleted = FALSE; bFirstRec = FALSE;返回
4.2.1.1.2 dwSize + nFact>nPackage当前的数据添加到堆中能够描述完整的数据包结构
先填充数据包结构,从nFact中取出数据nNeedPackage=nPackage-dwSize,在拼装成一个完整的数据包结构,
这样可以通过完整的包结构取出这次接收到的数据大小和数据:
如果nFact-nNeedPackage<CDataPackage->m_dwData(pBuffer中包含的数据不是一个完整的数据包数据),
将dwSize + nFact字节大小放入全局变量hGlobal
如果nFact-nNeedPackage>CDataPackage->m_dwData(pBuffer中包含了完整的数据包数据,并且可能包含
多个数据包),将dwSize字节的数据和CDataPackage->m_dwData放入同一个数据包,将数据包写入到文件,执行步骤和3.2.2相同
4.2.1.2、dwSize >= nPackage数据包结构完整,接收数据,将dwSize大小字节,拼装成一个完整的数据包结构,
这样可以通过完整的包结构取出这次接收到的数据大小和数据:
4.2.1.2.1、dwSize + nFact >= CDataPackage->m_dwData+nPackage(如果之前接收的数据与现在接收的数据能够
构成至少一个完整的数据包),将dwSize字节的数据和CDataPackage->m_dwData+nPackage-dwSize放入同
一个数据包,将数据包写入到文件,接下来执行步骤和3.2.2相同
4.2.1.2.2、接收的数据与现在接收的数据不能构成一个完整的数据包,还需要继续接收数据,将dwSize + nFact字节大小
放入全局变量hGlobal
4.3、继续执行接收操作,后来步骤和以上相同
8、写文件操作
8.1、m_Cmd =SC_BEGIN开始发送如果第一次写,读取文件名,创建文件,即开头的128个字节是文件名,
将后面的(除128个字节外的数据写入文件), 发送接收标记;
如果此时点击取消文件接收,想客户端发送取消标记
8.2、如果文件时文件发送中,则继续写文件
8.3、m_Cmd =SC_END 设置结束标记 m_bSending = FALSE;
8.4、m_Cmd = SC_CANCEL对方取消了文件发送