这是我的代码:
// Not all headers are relevant to the code snippet. #include#include #include #include #include #include #include #include char *buffer; stringstream readStream; bool readData = true; while (readData) { cout << "Receiving chunk... "; // Read a bit at a time, eventually "end" string will be received. bzero(buffer, BUFFER_SIZE); int readResult = read(socketFileDescriptor, buffer, BUFFER_SIZE); if (readResult < 0) { THROW_VIMRID_EX("Could not read from socket."); } // Concatenate the received data to the existing data. readStream << buffer; // Continue reading while end is not found. readData = readStream.str().find("end;") == string::npos; cout << "Done (length: " << readStream.str().length() << ")" << endl; }
你可以告诉它有点C和C++.BUFFER_SIZE是256 - 我应该增加大小吗?如果是这样,该怎么办?有关系吗?
我知道如果因为某种原因没有收到"结束",这将是一个无限循环,这是不好的 - 所以如果你能提出一个更好的方法,也请这样做.
在不知道您的完整应用程序的情况下,很难说解决问题的最佳方法是什么,但一种常见的技术是使用以固定长度字段开头的标头,该字段表示消息其余部分的长度.
假设您的标头仅包含一个4字节的整数,表示消息其余部分的长度.然后简单地执行以下操作.
// This assumes buffer is at least x bytes long, // and that the socket is blocking. void ReadXBytes(int socket, unsigned int x, void* buffer) { int bytesRead = 0; int result; while (bytesRead < x) { result = read(socket, buffer + bytesRead, x - bytesRead); if (result < 1 ) { // Throw your error. } bytesRead += result; } }
然后在代码中
unsigned int length = 0; char* buffer = 0; // we assume that sizeof(length) will return 4 here. ReadXBytes(socketFileDescriptor, sizeof(length), (void*)(&length)); buffer = new char[length]; ReadXBytes(socketFileDescriptor, length, (void*)buffer); // Then process the data as needed. delete [] buffer;
这做了一些假设:
ints在发送方和接收方上的大小相同.
发送方和接收方的Endianess都是相同的.
您可以控制双方的协议
发送消息时,您可以预先计算长度.
由于通常希望明确知道您通过网络发送的整数的大小,因此在头文件中定义它们并明确使用它们,例如:
// These typedefs will vary across different platforms // such as linux, win32, OS/X etc, but the idea // is that a Int8 is always 8 bits, and a UInt32 is always // 32 bits regardless of the platform you are on. // These vary from compiler to compiler, so you have to // look them up in the compiler documentation. typedef char Int8; typedef short int Int16; typedef int Int32; typedef unsigned char UInt8; typedef unsigned short int UInt16; typedef unsigned int UInt32;
这会将上述内容改为:
UInt32 length = 0; char* buffer = 0; ReadXBytes(socketFileDescriptor, sizeof(length), (void*)(&length)); buffer = new char[length]; ReadXBytes(socketFileDescriptor, length, (void*)buffer); // process delete [] buffer;
我希望这有帮助.
几个指针:
您需要处理返回值0,它告诉您远程主机关闭了套接字.
对于非阻塞套接字,还需要检查错误返回值(-1)并确保errno不是EINPROGRESS,这是预期的.
你肯定需要更好的错误处理 - 你可能会泄漏'缓冲区'指向的缓冲区.我注意到,您没有在此代码段中的任何位置分配.
如果你的read()填满整个缓冲区,其他人就你的缓冲区如何不是一个空终止的C字符串提出了一个很好的观点.这确实是一个问题,也是一个严重问题.
您的缓冲区大小有点小,但只要您不尝试读取超过256个字节或任何为其分配的内容,它就应该有效.
如果您担心在远程主机向您发送格式错误的消息(可能的拒绝服务攻击)时进入无限循环,那么您应该在套接字上使用带有超时的select()来检查可读性,并且只读取数据可用,如果select()超时则纾困.
这样的事可能适合你:
fd_set read_set; struct timeval timeout; timeout.tv_sec = 60; // Time out after a minute timeout.tv_usec = 0; FD_ZERO(&read_set); FD_SET(socketFileDescriptor, &read_set); int r=select(socketFileDescriptor+1, &read_set, NULL, NULL, &timeout); if( r<0 ) { // Handle the error } if( r==0 ) { // Timeout - handle that. You could try waiting again, close the socket... } if( r>0 ) { // The socket is ready for reading - call read() on it. }
根据您希望接收的数据量,重复扫描整个消息的方式为"结束"; 令牌效率很低.使用状态机(状态为'e' - >'n' - >'d' - >';')可以做得更好,这样您只需查看每个传入的字符一次.
说真的,你应该考虑找一个图书馆为你做这一切.要做到这一点并不容易.