server:
int main()
{
USHORT nPort = 4567; // 此服务器监听的端口号
// 创建监听套节字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(nPort);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
// 绑定套节字到本地机器
if(::bind(sListen, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf(" Failed bind() \n");
return -1;
}
// 进入监听模式
::listen(sListen, 5);
// select模型处理过程
// 1)初始化一个套节字集合fdSocket,添加监听套节字句柄到这个集合
fd_set fdSocket; // 所有可用套节字集合
FD_ZERO(&fdSocket);
FD_SET(sListen, &fdSocket);
while(TRUE)
{
// 2)将fdSocket集合的一个拷贝fdRead传递给select函数,
// 当有事件发生时,select函数移除fdRead集合中没有未决I/O操作的套节字句柄,然后返回。
fd_set fdRead = fdSocket;
int nRet = ::select(0, &fdRead, NULL, NULL, NULL);
if(nRet > 0)
{
// 3)通过将原来fdSocket集合与select处理过的fdRead集合比较,
// 确定都有哪些套节字有未决I/O,并进一步处理这些I/O。
for(int i=0; i<(int)fdSocket.fd_count; i++)
{
if(FD_ISSET(fdSocket.fd_array[i], &fdRead))
{
if(fdSocket.fd_array[i] == sListen) // (1)监听套节字接收到新连接
{
if(fdSocket.fd_count < FD_SETSIZE)
{
sockaddr_in addrRemote;
int nAddrLen = sizeof(addrRemote);
SOCKET sNew = ::accept(sListen, (SOCKADDR*)&addrRemote, &nAddrLen);
FD_SET(sNew, &fdSocket);
printf("接收到连接(%s)\n", ::inet_ntoa(addrRemote.sin_addr));
}
else
{
printf(" Too much connections! \n");
continue;
}
}
else
{
char szText[256];
int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);
if(nRecv > 0) // (2)可读
{
szText[nRecv] = '\0';
printf("接收到数据:%s \n", szText);
}
else // (3)连接关闭、重启或者中断
{
::closesocket(fdSocket.fd_array[i]);
FD_CLR(fdSocket.fd_array[i], &fdSocket);
}
}
}
}
}
else
{
printf(" Failed select() \n");
break;
}
}
return 0;
}
client: 阻塞模式 send函数
14 个解决方案
#1
send的时候,如果数据比较多,是有可能被分解成多次发送过去的.
#2
客户端的一次数据发送代码也贴上吧。
#3
对TCP了解不深,TCP是流式的(没定界符),不像UDP是数据报(有严格的定界符)
#4
TCP协议在发送和接收时,会对数据进行重新组合,可能多次发送的数据一起接收到,也可能一次发送的数据会分多次到达,每次只能接收到一部分,TCP保证数据有序到达,但发送与接收不是一一对应的,在程序设计上应该多次接收,直到所需数据全部收到(或遇到错误)为止。
#5
你是如何确认数据客户端是一次性发完数据的?
#6
目前我还没有考虑这个问题,但我想应该socket api提供的函数应该具备这些功能。
#7
是你的理解不彻底,Socket的send和recv并不是说客户端一次send多少字节,服务器不能接收到多少字节,所以无论接收还是发送都是循环接收和发送。
你的代码:
int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);
这个应该是循环接收,因为有时网络影响不一定一次性全部接收完数据。
你的代码:
int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);
这个应该是循环接收,因为有时网络影响不一定一次性全部接收完数据。
#8
TCP协议是面向流的协议, 不是面向消息协议。
TCP协议只确保发送出去的码流正确且有序的被上层应用收到,但是没有定义是一个消息一个消息组包好了再让上层应用接受。
select / socket 框架也支持SCTP协议,这个协议是一个类TCP协议,目前在电信领域被广泛运用,如果创建socket时制定用SCTP协议进行传输,可以保证消息收到的时候是经过组包的,也就是收到的是一个完整的消息,不会被中间截断。
如果一定要用TCP协议,则需要自己封装一层组包的代码,这种代码网上很多,自己写也不难,大体思路是消息采用 长度+内容的格式,每次对socket进行预读,根据长度字段,读满一个消息后,再将消息发到上层。
TCP协议只确保发送出去的码流正确且有序的被上层应用收到,但是没有定义是一个消息一个消息组包好了再让上层应用接受。
select / socket 框架也支持SCTP协议,这个协议是一个类TCP协议,目前在电信领域被广泛运用,如果创建socket时制定用SCTP协议进行传输,可以保证消息收到的时候是经过组包的,也就是收到的是一个完整的消息,不会被中间截断。
如果一定要用TCP协议,则需要自己封装一层组包的代码,这种代码网上很多,自己写也不难,大体思路是消息采用 长度+内容的格式,每次对socket进行预读,根据长度字段,读满一个消息后,再将消息发到上层。
#9
顶楼上
解决TCP粘包问题是基础,做法楼上有提。基本上网络上代码很多,但完全解决这问题的很少。
多看,多想,多写,多跟踪调试。
解决TCP粘包问题是基础,做法楼上有提。基本上网络上代码很多,但完全解决这问题的很少。
多看,多想,多写,多跟踪调试。
#10
你要自己控制接收的长度和发送的长度,不能依靠api自己
#11
你用哪种模型都出现这情况.这很大程度是由于发送方和接收方之间的网络环境,或两方系统硬件资源等限制.
1.发送方带宽很高,发送速度很快,但接收方带宽很低,这种不平衡就可以导致发送方将大量数据都发送成功到接收方,但接收方需要慢慢的接收(由于带宽问题)
2.接收方的受制于可用资源(例如当前很多网络程序在收发数据),那么到达接收方的数据,由于系统内核缓冲区不能同时处理完,也会被分开几次处理完.
在理想环境中(足够资源,足够带宽环境中),很少出现你所说情况(你可以在自己的内网测试下,内网的环境就理想很多了).但实际的网络,根本不存在"理想状态".
#12
类似SOAP,加包长校验,循环收发。
#13
自己在发送数据的时候加上包头。。
包头是固定大小的 里面标示了接下来数据的大小
读的时候先读头(头的大小固定)根据头中的 数据大小 读接下来的数据
就OK了
包头是固定大小的 里面标示了接下来数据的大小
读的时候先读头(头的大小固定)根据头中的 数据大小 读接下来的数据
就OK了
#14
楼上正解!
#1
send的时候,如果数据比较多,是有可能被分解成多次发送过去的.
#2
客户端的一次数据发送代码也贴上吧。
#3
对TCP了解不深,TCP是流式的(没定界符),不像UDP是数据报(有严格的定界符)
#4
TCP协议在发送和接收时,会对数据进行重新组合,可能多次发送的数据一起接收到,也可能一次发送的数据会分多次到达,每次只能接收到一部分,TCP保证数据有序到达,但发送与接收不是一一对应的,在程序设计上应该多次接收,直到所需数据全部收到(或遇到错误)为止。
#5
你是如何确认数据客户端是一次性发完数据的?
#6
目前我还没有考虑这个问题,但我想应该socket api提供的函数应该具备这些功能。
#7
是你的理解不彻底,Socket的send和recv并不是说客户端一次send多少字节,服务器不能接收到多少字节,所以无论接收还是发送都是循环接收和发送。
你的代码:
int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);
这个应该是循环接收,因为有时网络影响不一定一次性全部接收完数据。
你的代码:
int nRecv = ::recv(fdSocket.fd_array[i], szText, strlen(szText), 0);
这个应该是循环接收,因为有时网络影响不一定一次性全部接收完数据。
#8
TCP协议是面向流的协议, 不是面向消息协议。
TCP协议只确保发送出去的码流正确且有序的被上层应用收到,但是没有定义是一个消息一个消息组包好了再让上层应用接受。
select / socket 框架也支持SCTP协议,这个协议是一个类TCP协议,目前在电信领域被广泛运用,如果创建socket时制定用SCTP协议进行传输,可以保证消息收到的时候是经过组包的,也就是收到的是一个完整的消息,不会被中间截断。
如果一定要用TCP协议,则需要自己封装一层组包的代码,这种代码网上很多,自己写也不难,大体思路是消息采用 长度+内容的格式,每次对socket进行预读,根据长度字段,读满一个消息后,再将消息发到上层。
TCP协议只确保发送出去的码流正确且有序的被上层应用收到,但是没有定义是一个消息一个消息组包好了再让上层应用接受。
select / socket 框架也支持SCTP协议,这个协议是一个类TCP协议,目前在电信领域被广泛运用,如果创建socket时制定用SCTP协议进行传输,可以保证消息收到的时候是经过组包的,也就是收到的是一个完整的消息,不会被中间截断。
如果一定要用TCP协议,则需要自己封装一层组包的代码,这种代码网上很多,自己写也不难,大体思路是消息采用 长度+内容的格式,每次对socket进行预读,根据长度字段,读满一个消息后,再将消息发到上层。
#9
顶楼上
解决TCP粘包问题是基础,做法楼上有提。基本上网络上代码很多,但完全解决这问题的很少。
多看,多想,多写,多跟踪调试。
解决TCP粘包问题是基础,做法楼上有提。基本上网络上代码很多,但完全解决这问题的很少。
多看,多想,多写,多跟踪调试。
#10
你要自己控制接收的长度和发送的长度,不能依靠api自己
#11
你用哪种模型都出现这情况.这很大程度是由于发送方和接收方之间的网络环境,或两方系统硬件资源等限制.
1.发送方带宽很高,发送速度很快,但接收方带宽很低,这种不平衡就可以导致发送方将大量数据都发送成功到接收方,但接收方需要慢慢的接收(由于带宽问题)
2.接收方的受制于可用资源(例如当前很多网络程序在收发数据),那么到达接收方的数据,由于系统内核缓冲区不能同时处理完,也会被分开几次处理完.
在理想环境中(足够资源,足够带宽环境中),很少出现你所说情况(你可以在自己的内网测试下,内网的环境就理想很多了).但实际的网络,根本不存在"理想状态".
#12
类似SOAP,加包长校验,循环收发。
#13
自己在发送数据的时候加上包头。。
包头是固定大小的 里面标示了接下来数据的大小
读的时候先读头(头的大小固定)根据头中的 数据大小 读接下来的数据
就OK了
包头是固定大小的 里面标示了接下来数据的大小
读的时候先读头(头的大小固定)根据头中的 数据大小 读接下来的数据
就OK了
#14
楼上正解!