这是我的代码:
// Not all headers are relevant to the code snippet.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
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;
}
Run Code Online (Sandbox Code Playgroud)
你可以告诉它有点C和C++.BUFFER_SIZE是256 - 我应该增加大小吗?如果是这样,该怎么办?有关系吗?
我知道如果因为某种原因没有收到"结束",这将是一个无限循环,这是不好的 - 所以如果你能提出一个更好的方法,也请这样做.
gri*_*eve 34
在不知道您的完整应用程序的情况下,很难说解决问题的最佳方法是什么,但一种常见的技术是使用以固定长度字段开头的标头,该字段表示消息其余部分的长度.
假设您的标头仅包含一个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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后在代码中
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;
Run Code Online (Sandbox Code Playgroud)
这做了一些假设:
由于通常希望明确知道您通过网络发送的整数的大小,因此在头文件中定义它们并明确使用它们,例如:
// 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;
Run Code Online (Sandbox Code Playgroud)
这会将上述内容改为:
UInt32 length = 0;
char* buffer = 0;
ReadXBytes(socketFileDescriptor, sizeof(length), (void*)(&length));
buffer = new char[length];
ReadXBytes(socketFileDescriptor, length, (void*)buffer);
// process
delete [] buffer;
Run Code Online (Sandbox Code Playgroud)
我希望这有帮助.
几个指针:
您需要处理返回值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.
}
Run Code Online (Sandbox Code Playgroud)
根据您希望接收的数据量,重复扫描整个消息的方式为"结束"; 令牌效率很低.使用状态机(状态为'e' - >'n' - >'d' - >';')可以做得更好,这样您只需查看每个传入的字符一次.
说真的,你应该考虑找一个图书馆为你做这一切.要做到这一点并不容易.
小智 5
如果您实际上按照 dirks 建议创建了缓冲区,那么:
int readResult = read(socketFileDescriptor, buffer, BUFFER_SIZE);
Run Code Online (Sandbox Code Playgroud)
可能会完全填充缓冲区,可能会覆盖您在提取到字符串流时所依赖的终止零字符。你需要:
int readResult = read(socketFileDescriptor, buffer, BUFFER_SIZE - 1 );
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
87675 次 |
| 最近记录: |