关于接收数据出现混乱的问题?

ksunbird 2008-03-05 11:49:30
发送的是一幅图像.可能是接收数据次序出现了混乱,图像上半部分清楚,下半部分模糊.请大家指教!另外小弟学c,c++不久,以前没写过多少程序,欢迎csdn上的高手指教,拍砖!^.^

注:发送端和接收端是在同一台机器上,接收部分是接收程序启动的一个线程(不知道这会不会对接收次序产生影响).

代码如下:

发送端
bool SendFile(const char *pData, int nSize)
{
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

char arr[4];
ConverterInt(nSize, arr);
send(sockClient, arr, 4, 0); // 发送"数据大小"信息

const int nPackageSize = 4096; // nLen代表每次数据的大小 --- 4k
int nTime = nSize / nPackageSize; // 循环发送数据的次数
for(int i = 0; i < nTime; i++)
{
int nResult = send(sockClient, pData + i * nPackageSize, nPackageSize, 0);
if (nResult == SOCKET_ERROR)
{
break; // 如果发送失败,跳出循环
}
}

int nLeave = nSize % nPackageSize;
if( nLeave != 0)
{
send(sockClient, pData + nTime * nPackageSize, nLeave, 0); // 发送剩余数据
}

closesocket(sockClient);

return true;
}

/**********************************************
* 把Int类型转化成4个长度的字符数组
* Int占用位数为32位时才有效,函数有些缺陷^.^
**********************************************/
static void ConverterInt(int nValue, char arr[])
{
arr[0] = (byte)(nValue); // 最低位字节数值
arr[1] = (byte)(nValue >> 8); // 右移8位
arr[2] = (byte)(nValue >> 16); // 右移16位
arr[3] = 0; // 代表最高位数值为0, 因为图像大小不会很大,所以把这个字节设为0
}

// 接收端程序
DWORD WINAPI FunListenProc(LPVOID lpParameter)
{
SOCKET sockSrv=socket(AF_INET, SOCK_STREAM, 0);

SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);

int nRet = bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
if(nRet == SOCKET_ERROR)
{
closesocket(sockSrv);
return -1;
}

int nResult = listen(sockSrv, 1); // 设置监听数为1,只允许一个客户程序连接到服务器
if(nResult == SOCKET_ERROR)
{
closesocket(sockSrv);
return -1;
}

SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);

while (true)
{
SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);

const int nRecv = 4;
char arr[nRecv];
int nCount = recv(sockConn, arr, nRecv, 0);
if(nCount == nRecv)
{
int nSize = (byte)arr[0] + ((byte)arr[1]) * 256 + ((byte)arr[2]) * 256 * 256;

if(nSize > 0)
{
char *pFile = new char[nSize];
const int nPackageSize = 4096; // nLen代表每次数据的大小 --- 4k
int nTime = nSize / nPackageSize; // 循环接收数据的次数

for(int i = 0; i < nTime; i++)
{
int nRecvSize = recv(sockConn, pFile + nPackageSize * i, nPackageSize, 0);
if(nRecvSize == SOCKET_ERROR)
{
break; // 接收数据出错,跳出循环
}
}

int nLeave = nSize % nPackageSize;
if( nLeave != 0)
{
recv(sockConn, pFile + nTime * nPackageSize, nLeave, 0); // 接收剩余数据
}

CFile file;
file.Open("Img.jpg", CFile::modeCreate | CFile::modeWrite);
file.Write(pFile, nSize); // 写入原文件类型
file.Close();

delete[] pFile;
pFile = NULL;
}
}


closesocket(sockConn);
}

return 0;
}
...全文
80 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
cnzdgs 2008-03-06
  • 打赏
  • 举报
回复
发送完数据不要立即断开连接,由接收方来断开。
还有,传输失败后没有进一步处理,例如显示错误信息让自己知道。
另外,发送int不用象你这样转换直接在send函数中写(char*)&nSize就可以了,recv也一样。
CrownLiu 2008-03-06
  • 打赏
  • 举报
回复
static void ConverterInt(int nValue, char arr[])
好像有点问题吧??

arr[0] = (byte)(nValue & 0x000000FF); // 最低位字节数值
arr[1] = (byte)(nValue & 0x0000FF00); // 右移8位
arr[2] = (byte)(nValue & 0x00FF0000); // 右移16位
arr[3] = 0; // 代表最高位数值为0, 因为图像大小不会很大,所以把这个字节设为0
ksunbird 2008-03-06
  • 打赏
  • 举报
回复
我说的是不要立即断开,等到接收方断开后发送方再关闭socket.发送端程序和接收端程序不是同一个程序,请问如何实现呢?

"刚看了一下,你没有用recv的返回值来计算接收数据的大小。对于TCP协议,不是你每次发多少对方就每次收多少的,recv的返回值才是真正接收到的大小,你可以用一个变量记录总共要接收的数据量,然后循环recv,缓冲区长度都给这个变量的值,每次接收成功后,该变量减去recv的返回值,直到为0结束循环。"

再想了想这段话,又有点感觉了,我觉得send的时候也不是发多少就能直接发送成功的.返回值才是真正发送的数据大小.通过循环发送数据,可以累加返回值,直到发完就断开连接,这样理解可以吧.
cnzdgs 2008-03-06
  • 打赏
  • 举报
回复
我说的是不要立即断开,等到接收方断开后发送方再关闭socket,我是担心数据没有发送成功连接就被断开了,不过现在看起来主要是接收的问题。
ksunbird 2008-03-06
  • 打赏
  • 举报
回复
谢谢回复,刚刚看了一下可以运行就结贴了.
程序还是有些问题.

看了您的下面这段留言,我估计能改好了.

"刚看了一下,你没有用recv的返回值来计算接收数据的大小。对于TCP协议,不是你每次发多少对方就每次收多少的,recv的返回值才是真正接收到的大小,你可以用一个变量记录总共要接收的数据量,然后循环recv,缓冲区长度都给这个变量的值,每次接收成功后,该变量减去recv的返回值,直到为0结束循环。"

还有就是我觉得"发送完数据不要立即断开连接,由接收方来断开"这句话可能不正确,因为建立了连接就要关闭.
cnzdgs 2008-03-06
  • 打赏
  • 举报
回复
问题还没解决完就结帖了。
cnzdgs 2008-03-06
  • 打赏
  • 举报
回复
刚看了一下,你没有用recv的返回值来计算接收数据的大小。对于TCP协议,不是你每次发多少对方就每次收多少的,recv的返回值才是真正接收到的大小,你可以用一个变量记录总共要接收的数据量,然后循环recv,缓冲区长度都给这个变量的值,每次接收成功后,该变量减去recv的返回值,直到为0结束循环。
ksunbird 2008-03-06
  • 打赏
  • 举报
回复
这是接收端程序,也是设成1k大小.

DWORD WINAPI FunListenProc(LPVOID lpParameter)
{
SOCKET sockSrv=socket(AF_INET, SOCK_STREAM, 0);

SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);

int nRet = bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
if(nRet == SOCKET_ERROR)
{
closesocket(sockSrv);
return -1;
}

int nResult = listen(sockSrv, 1); // 设置监听数为1,只允许一个客户程序连接到服务器
if(nResult == SOCKET_ERROR)
{
closesocket(sockSrv);
return -1;
}

SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);

while (true)
{
SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);

int nSize = 0;
recv(sockConn, (char*)&nSize, 4, 0);

if(nSize > 0)
{
char *pFile = new char[nSize];
const int nPackageSize = 1024; // nLen代表每次数据的大小 --- 1k
int nTime = nSize / nPackageSize; // 循环接收数据的次数

for(int i = 0; i < nTime; i++)
{
int nRecvSize = recv(sockConn, pFile + nPackageSize * i, nPackageSize, 0);
if(nRecvSize == SOCKET_ERROR)
{
break; // 接收数据出错,跳出循环
}
}

int nLeave = nSize % nPackageSize;
if( nLeave != 0)
{
recv(sockConn, pFile + nTime * nPackageSize, nLeave, 0); // 接收剩余数据
}

CFile file;
file.Open("Screen.jpg", CFile::modeCreate | CFile::modeWrite);
file.Write(pFile, nSize); // 写入原文件类型
file.Close();

delete[] pFile;
pFile = NULL;
}

closesocket(sockConn);
}

return 0;
}
ksunbird 2008-03-06
  • 打赏
  • 举报
回复
感谢CrownLiu和cnzdgs.

奇怪了,把每次发送和接收都改成1k大小就可以,(第一次运行不正常,后面全部都正常,还是有些不稳定).
bool SendFile(const char *pData, int nSize)
{
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

send(sockClient, (char*)&nSize, 4, 0); // 发送"数据大小"信息

const int nPackageSize = 1024; // nLen代表每次数据的大小 --- 1k
int nTime = nSize / nPackageSize; // 循环发送数据的次数
for(int i = 0; i < nTime; i++)
{
int nResult = send(sockClient, pData + i * nPackageSize, nPackageSize, 0);
if (nResult == SOCKET_ERROR)
{
break; // 如果发送失败,跳出循环
}
}

int nLeave = nSize % nPackageSize;
if( nLeave != 0)
{
send(sockClient, pData + nTime * nPackageSize, nLeave, 0); // 发送剩余数据
}

closesocket(sockClient);

return false;
}

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧