C++ 从C/C+中的TCP套接字读取数据的正确方式是什么+;?

C++ 从C/C+中的TCP套接字读取数据的正确方式是什么+;?,c++,c,tcp,C++,C,Tcp,这是我的密码: // 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>

这是我的密码:

// 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;
}
//并非所有标题都与代码段相关。
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
字符*缓冲区;
stringstream readStream;
bool readData=true;
while(读取数据)
{

cout您在哪里为您的
缓冲区
分配内存?您调用
bzero
的行调用未定义的行为,因为缓冲区不指向任何有效的内存区域

char *buffer = new char[ BUFFER_SIZE ];
// do processing

// don't forget to release
delete[] buffer;
几点提示:

您需要处理一个返回值0,它告诉您远程主机关闭了套接字

对于非阻塞套接字,还需要检查错误返回值(-1),并确保errno不是预期的EINPROGRESS

您肯定需要更好的错误处理-您可能正在泄漏“buffer”指向的缓冲区。我注意到,您没有在这个代码段中分配任何位置

还有人说,如果read()填充整个缓冲区,那么缓冲区就不是以null结尾的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.
}
fd\u集读取集;
结构timeval超时;
timeout.tv_sec=60;//一分钟后超时
timeout.tv_usec=0;
FD_零(读取集和读取集);
FD_集(socketFileDescriptor和read_集);
int r=select(socketFileDescriptor+1,&read\u set,NULL,NULL,&timeout);
如果(r0){
//套接字已准备好读取-对其调用read()。
}
根据预期接收的数据量,重复扫描整个消息以查找“end;”标记的方式效率非常低。最好使用状态机(状态为“e”->“n”->“d”->”;)来完成此操作,以便只查看每个传入字符一次


<>和认真地,你应该考虑找一个图书馆来为你做这一切。不容易得到它。

不知道你的全部应用,很难说什么是解决这个问题的最佳方法,但是一个常用的技术是使用一个从一个固定长度字段开始的标题,它表示你的其余部分的长度。消息

假设您的头文件只包含一个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;
    }
}
这有几个假设:

  • INT在发送方和接收方上的大小相同
  • 发送方和接收方的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;

我希望这会有所帮助。

如果您确实按照德克斯的建议创建了缓冲区,那么:

  int readResult = read(socketFileDescriptor, buffer, BUFFER_SIZE);
可能会完全填充缓冲区,可能会覆盖提取到stringstream时所依赖的终止零字符。您需要:

  int readResult = read(socketFileDescriptor, buffer, BUFFER_SIZE - 1 );

这是我在使用套接字时经常提到的一篇文章


它将向您展示如何可靠地使用“select()”,并在底部包含一些其他有用的链接,以获取有关套接字的更多信息。

1)其他人(尤其是直接)注意到需要为缓冲区分配一些内存空间。对于较小的N值(例如,N),只需添加到上面几篇文章中的内容:


read()——至少在我的系统上是如此——返回ssize\u t。这与size\u t相似,只是有符号。在我的系统上,它是一个长字符,而不是int。如果使用int,可能会收到编译器警告,具体取决于您的系统、编译器以及您打开的警告。

对于任何非普通应用程序(即应用程序必须接收和处理不同长度的不同类型的消息),您特定问题的解决方案不一定只是一个编程解决方案,而是一种约定,即协议

为了确定应该传递给
read
调用的字节数,您应该建立应用程序接收的通用前缀或头。这样,当套接字第一次具有可用的读取时,您就可以决定预期的内容

二进制示例可能如下所示:

#包括
#包括
#包括
#包括
#包括
枚举消息类型{
贺电(吴富),
留言栏,
};
结构消息头{
uint32_t型;
uint32_t长度;
};
/**
*尝试继续读取'socket',直到'bytes'编号
*读取的字节数为。成功时返回truthy,失败时返回falsy。
*
*类似于@greve的ReadXBytes。
*/
int readExpected(int套接字、void*目标、大小\u t字节)
{
/*
*无法将空指针作为递增指针递增
*由指向类型的宽度完成-
*而虚空没有宽度
*
*您可以在GCC中使用,但它不是很便于携带
*/
char*destinationBytes=目的地;
while(字节){
ssize_t readBytes=读取(套接字、目标字节、字节);
if(readBytes<1)
返回0;
destinationBytes+=readBytes;
字节-=读取字节;
}
返回1;
}
int main(int argc,字符**argv)
{
int-selectedFd;
//使用'select'或'poll'等待套接字
//收到“selectedFd”上的消息,开始阅读
char*fooMessage;
结构{
uint32_t a;
uint32_t b;
}条形码信息;
接收到struct MessageHeader;
如果(!readExp
  int readResult = read(socketFileDescriptor, buffer, BUFFER_SIZE - 1 );
#define BUFFER_SIZE 4096
char buffer[BUFFER_SIZE]
mharrison@mharrison-KATANA:~$ gcc -o padding padding.c
mharrison@mharrison-KATANA:~$ ./padding 
sizeof(A): 8