当前位置:  开发笔记 > 编程语言 > 正文

在C/C++中从TCP套接字读取的正确方法是什么?

如何解决《在C/C++中从TCP套接字读取的正确方法是什么?》经验,为你挑选了2个好方法。

这是我的代码:

// 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 - 我应该增加大小吗?如果是这样,该怎么办?有关系吗?

我知道如果因为某种原因没有收到"结束",这将是一个无限循环,这是不好的 - 所以如果你能提出一个更好的方法,也请这样做.



1> grieve..:

在不知道您的完整应用程序的情况下,很难说解决问题的最佳方法是什么,但一种常见的技术是使用以固定长度字段开头的标头,该字段表示消息其余部分的长度.

假设您的标头仅包含一个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;

我希望这有帮助.



2> Ori Pessach..:

几个指针:

您需要处理返回值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' - >';')可以做得更好,这样您只需查看每个传入的字符一次.

说真的,你应该考虑找一个图书馆为你做这一切.要做到这一点并不容易.

推荐阅读
地之南_816
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有