接收端使用Select模型,监听用Socket使用默认属性,接受客户端连接后,
接收数据包的Socket设置接收缓冲32K.
测试结果:
发送慢接收快(通过收发包间隔Sleep来控制),没有问题。
但是发送快,接收慢,导致接收的数据包变大(本来10M的文件,接收后超过10M)。
发送端根据send返回值来判断,是否要重发数据包,以及移动开始发包的索引和还可发送的字节数。
看资料介绍send返回值,不表示实际发出的字节,好像只表示把数据拷贝到发送缓冲区。
是否每次发包前还要getsockopt来判断下缓冲区是否满了?
6 个解决方案
#1
//发送端设置收发数据包等待时间2秒
//设置发送缓冲区为32K
#define BUF_SIZE 1024
//设置异步发送接收等待时间
int nNetTimeout= 2000; //2秒
setsockopt(sockServer, SOL_SOCKET, SO_SNDTIMEO, (char *)&nNetTimeout,sizeof(int));
setsockopt(sockServer, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int));
//设置强制关闭Socket前等待时间
linger m_sLinger;
m_sLinger.l_onoff = 1;
m_sLinger.l_linger = 2; //2秒
setsockopt(sockServer,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));
//设置发送缓冲区
int nSendBuf = 32*1024; //32K
setsockopt(sockServer, SOL_SOCKET, SO_SNDBUF, (const char* )&nSendBuf, sizeof(int));
//连接TCP服务器
int err = connect(sockServer, (SOCKADDR*)&addrServer, sizeof(SOCKADDR));
//发送文件
int ClientSendFile(SOCKET sock, char *strVideoPath)
{
int byteRead = 0;
int iSendSize = 0;
int iCurIndex = 0;
FILE *pfRead = fopen(strVideoPath, "rb");//打开要发送的文件
unsigned char *pBuf = new unsigned char[BUF_SIZE + 10];
while(TRUE)
{
byteRead = fread (pBuf, 1, BUF_SIZE, pfRead);
if (byteRead <= 0)
{
break; //读文件结束
}
int iRetryTimes = 0; //重发次数
iCurIndex = 0; //缓冲区发包起始位置
iSendSize = send(sock, (const char *)pBuf, byteRead, 0);
if (iSendSize != byteRead) //数据包没发完
{
while(byteRead > 0) //继续重发
{
if (iSendSize > 0)
{
//如果已发出一部分,就修改索引位置和数据包长度
//否则, iSendSize<=0 参数不变
iCurIndex += iSendSize; //开始发包位置
byteRead -= iSendSize; //还没发完的字节
}
if (byteRead > 0 //有数据可发
&& iCurIndex < BUF_SIZE) //没越界
{
iRetryTimes++;
iSendSize = send(sock, (const char *)(pBuf+iCurIndex), byteRead, 0);
Sleep(500); //重发时,放慢发包速度
}
if (iRetryTimes >= 5)
{
break; //至多重发5次
}
} //while(byteRead > 0) //继续重发
}
Sleep(1); //发包间隔时间
} //while(TRUE)
return 0;
}
#2
//线程内接受文件关键代码, Select模型
//服务端监听绑定的Socket使用默认属性
//接受客户端连接后,设置接收数据的Socket缓冲区为32K.
//监听Socket使用默认属性
SOCKADDR_IN localAddr;
localAddr.sin_family = AF_INET;
localAddr.sin_port = htons(iPort);
localAddr.sin_addr.S_un.S_addr = INADDR_ANY;
bind(serverListen, (SOCKADDR*)&localAddr, sizeof(localAddr));
DWORD WINAPI LoopRecvFile(void *p)
{
SOCKET fd;
int iRet = -1;
int *pPort = (int*)p;
//iRet = BindSocket(*pPort, fd);
//iRet = ListenStart(fd, Max_FSClient);
SOCKET sockRemote;
SOCKADDR_IN addrRemote;
int addrLen = sizeof(addrRemote);
char buf[BUF_SIZE] = {'\0'};
int maxsock = fd;
struct timeval tv;
for (int j = 0; j < Max_FSClient; j++)
{
myFSClient_Info[j].iClientPort
= myFSClient_Info[j].iClientSockID = 0;
memset(myFSClient_Info[j].strClientIP, '\0', STRPARA_LEN);
}
fd_set fdsr;
int i = 0;
int conn_amount = 0; //统计客户端连接数量
FILE *pfRecv = fopen("test.mp4", "wb"); //【保存文件名】
while (loopRecvFileExit == 0) //1结束
{
FD_ZERO(&fdsr);
FD_SET(fd, &fdsr);
tv.tv_sec = 0;
tv.tv_usec = 100;
for(i = 0 ; i < Max_FSClient; i++ )
{
if (myFSClient_Info[i].iClientSockID > 0)
{
//【把可用的Socket加入Socket数组中】
FD_SET(myFSClient_Info[i].iClientSockID, &fdsr);
}
}
//【查询可读的Socket】
int ret = select(maxsock+1, &fdsr, NULL, NULL, &tv);
if (ret < 0 ) //查询失败
{
break;
}
else if (ret == 0) //没有数据可读
{
continue;
}
for (i = 0 ; i < Max_FSClient; i ++ )
{
if (FD_ISSET(myFSClient_Info[i].iClientSockID, &fdsr))
{
memset(buf, '\0', BUF_SIZE);
ret = recv(myFSClient_Info[i].iClientSockID, buf, BUF_SIZE, 0);
if (ret <= 0) //客户端下线
{
closesocket(myFSClient_Info[i].iClientSockID);
FD_CLR(myFSClient_Info[i].iClientSockID, &fdsr);
myFSClient_Info[i].iClientSockID = 0 ;
conn_amount--;
}
else //接收正常
{
fwrite( buf, ret, 1, pfRecv);
fflush(pfRecv);
}
}
}
if (FD_ISSET(fd, &fdsr))
{
SOCKET new_fd = accept(fd, (SOCKADDR*)&addrRemote, &addrLen);
if (new_fd <= 0) //接受客户端连接
{
continue;
}
if (conn_amount < Max_FSClient)
{
for (int k = 0; k < Max_FSClient; k++)
{
if (myFSClient_Info[k].iClientSockID <= 0)
{
myFSClient_Info[k].iClientSockID = new_fd;
strcpy(myFSClient_Info[k].strClientIP, inet_ntoa(addrRemote.sin_addr));
myFSClient_Info[k].iClientPort = (int)ntohs(addrRemote.sin_port);
conn_amount++; //已接受的客户端连接数量
int nRecvBuf = 32 * 1024; //接收字节流的Socket, 缓冲区设置为32K
setsockopt(myFSClient_Info[k].iClientSockID, SOL_SOCKET, SO_RCVBUF, (const char* )&nRecvBuf, sizeof(int));
if (new_fd > maxsock)
{
maxsock = new_fd;
}
break;
}
}
}
else //超过最大连接数量
{
for (int iTest = 0; iTest < Max_FSClient; iTest++)
{
if (myFSClient_Info[iTest].iClientSockID == new_fd)
{
myFSClient_Info[iTest].iClientPort
= myFSClient_Info[iTest].iClientSockID
= 0;
memset(myFSClient_Info[iTest].strClientIP, '\0', STRPARA_LEN);
}
}
send(new_fd, " bye " , 4 , 0 );
closesocket(new_fd);
}
}
Sleep(100); //【测试收包速度慢】
} //while (TRUE)
return 0;
}
#3
发送部分的代码有问题。
你传输的视频文件一般有多大?上100M了么?
如果文件较小,我给你个发送的例子。
你传输的视频文件一般有多大?上100M了么?
如果文件较小,我给你个发送的例子。
#4
发送部分的代码有问题。
#5
不用判断缓冲区的情况,你可以认为返回值就代表发送出去了多少字节。先发送文件的长度给接收端,用于判断是否收到整个文件。
#6
谢谢楼上各位朋友的提醒,我再试下看看。
#1
//发送端设置收发数据包等待时间2秒
//设置发送缓冲区为32K
#define BUF_SIZE 1024
//设置异步发送接收等待时间
int nNetTimeout= 2000; //2秒
setsockopt(sockServer, SOL_SOCKET, SO_SNDTIMEO, (char *)&nNetTimeout,sizeof(int));
setsockopt(sockServer, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int));
//设置强制关闭Socket前等待时间
linger m_sLinger;
m_sLinger.l_onoff = 1;
m_sLinger.l_linger = 2; //2秒
setsockopt(sockServer,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));
//设置发送缓冲区
int nSendBuf = 32*1024; //32K
setsockopt(sockServer, SOL_SOCKET, SO_SNDBUF, (const char* )&nSendBuf, sizeof(int));
//连接TCP服务器
int err = connect(sockServer, (SOCKADDR*)&addrServer, sizeof(SOCKADDR));
//发送文件
int ClientSendFile(SOCKET sock, char *strVideoPath)
{
int byteRead = 0;
int iSendSize = 0;
int iCurIndex = 0;
FILE *pfRead = fopen(strVideoPath, "rb");//打开要发送的文件
unsigned char *pBuf = new unsigned char[BUF_SIZE + 10];
while(TRUE)
{
byteRead = fread (pBuf, 1, BUF_SIZE, pfRead);
if (byteRead <= 0)
{
break; //读文件结束
}
int iRetryTimes = 0; //重发次数
iCurIndex = 0; //缓冲区发包起始位置
iSendSize = send(sock, (const char *)pBuf, byteRead, 0);
if (iSendSize != byteRead) //数据包没发完
{
while(byteRead > 0) //继续重发
{
if (iSendSize > 0)
{
//如果已发出一部分,就修改索引位置和数据包长度
//否则, iSendSize<=0 参数不变
iCurIndex += iSendSize; //开始发包位置
byteRead -= iSendSize; //还没发完的字节
}
if (byteRead > 0 //有数据可发
&& iCurIndex < BUF_SIZE) //没越界
{
iRetryTimes++;
iSendSize = send(sock, (const char *)(pBuf+iCurIndex), byteRead, 0);
Sleep(500); //重发时,放慢发包速度
}
if (iRetryTimes >= 5)
{
break; //至多重发5次
}
} //while(byteRead > 0) //继续重发
}
Sleep(1); //发包间隔时间
} //while(TRUE)
return 0;
}
#2
//线程内接受文件关键代码, Select模型
//服务端监听绑定的Socket使用默认属性
//接受客户端连接后,设置接收数据的Socket缓冲区为32K.
//监听Socket使用默认属性
SOCKADDR_IN localAddr;
localAddr.sin_family = AF_INET;
localAddr.sin_port = htons(iPort);
localAddr.sin_addr.S_un.S_addr = INADDR_ANY;
bind(serverListen, (SOCKADDR*)&localAddr, sizeof(localAddr));
DWORD WINAPI LoopRecvFile(void *p)
{
SOCKET fd;
int iRet = -1;
int *pPort = (int*)p;
//iRet = BindSocket(*pPort, fd);
//iRet = ListenStart(fd, Max_FSClient);
SOCKET sockRemote;
SOCKADDR_IN addrRemote;
int addrLen = sizeof(addrRemote);
char buf[BUF_SIZE] = {'\0'};
int maxsock = fd;
struct timeval tv;
for (int j = 0; j < Max_FSClient; j++)
{
myFSClient_Info[j].iClientPort
= myFSClient_Info[j].iClientSockID = 0;
memset(myFSClient_Info[j].strClientIP, '\0', STRPARA_LEN);
}
fd_set fdsr;
int i = 0;
int conn_amount = 0; //统计客户端连接数量
FILE *pfRecv = fopen("test.mp4", "wb"); //【保存文件名】
while (loopRecvFileExit == 0) //1结束
{
FD_ZERO(&fdsr);
FD_SET(fd, &fdsr);
tv.tv_sec = 0;
tv.tv_usec = 100;
for(i = 0 ; i < Max_FSClient; i++ )
{
if (myFSClient_Info[i].iClientSockID > 0)
{
//【把可用的Socket加入Socket数组中】
FD_SET(myFSClient_Info[i].iClientSockID, &fdsr);
}
}
//【查询可读的Socket】
int ret = select(maxsock+1, &fdsr, NULL, NULL, &tv);
if (ret < 0 ) //查询失败
{
break;
}
else if (ret == 0) //没有数据可读
{
continue;
}
for (i = 0 ; i < Max_FSClient; i ++ )
{
if (FD_ISSET(myFSClient_Info[i].iClientSockID, &fdsr))
{
memset(buf, '\0', BUF_SIZE);
ret = recv(myFSClient_Info[i].iClientSockID, buf, BUF_SIZE, 0);
if (ret <= 0) //客户端下线
{
closesocket(myFSClient_Info[i].iClientSockID);
FD_CLR(myFSClient_Info[i].iClientSockID, &fdsr);
myFSClient_Info[i].iClientSockID = 0 ;
conn_amount--;
}
else //接收正常
{
fwrite( buf, ret, 1, pfRecv);
fflush(pfRecv);
}
}
}
if (FD_ISSET(fd, &fdsr))
{
SOCKET new_fd = accept(fd, (SOCKADDR*)&addrRemote, &addrLen);
if (new_fd <= 0) //接受客户端连接
{
continue;
}
if (conn_amount < Max_FSClient)
{
for (int k = 0; k < Max_FSClient; k++)
{
if (myFSClient_Info[k].iClientSockID <= 0)
{
myFSClient_Info[k].iClientSockID = new_fd;
strcpy(myFSClient_Info[k].strClientIP, inet_ntoa(addrRemote.sin_addr));
myFSClient_Info[k].iClientPort = (int)ntohs(addrRemote.sin_port);
conn_amount++; //已接受的客户端连接数量
int nRecvBuf = 32 * 1024; //接收字节流的Socket, 缓冲区设置为32K
setsockopt(myFSClient_Info[k].iClientSockID, SOL_SOCKET, SO_RCVBUF, (const char* )&nRecvBuf, sizeof(int));
if (new_fd > maxsock)
{
maxsock = new_fd;
}
break;
}
}
}
else //超过最大连接数量
{
for (int iTest = 0; iTest < Max_FSClient; iTest++)
{
if (myFSClient_Info[iTest].iClientSockID == new_fd)
{
myFSClient_Info[iTest].iClientPort
= myFSClient_Info[iTest].iClientSockID
= 0;
memset(myFSClient_Info[iTest].strClientIP, '\0', STRPARA_LEN);
}
}
send(new_fd, " bye " , 4 , 0 );
closesocket(new_fd);
}
}
Sleep(100); //【测试收包速度慢】
} //while (TRUE)
return 0;
}
#3
发送部分的代码有问题。
你传输的视频文件一般有多大?上100M了么?
如果文件较小,我给你个发送的例子。
你传输的视频文件一般有多大?上100M了么?
如果文件较小,我给你个发送的例子。
#4
发送部分的代码有问题。
#5
不用判断缓冲区的情况,你可以认为返回值就代表发送出去了多少字节。先发送文件的长度给接收端,用于判断是否收到整个文件。
#6
谢谢楼上各位朋友的提醒,我再试下看看。