c++ - Winsock 移动数据

标签 c++ winsock

我正在尝试使用 Winsock 通过网络发送图像帧。我知道数组长度和它们的维度,所以我只是使用一个足够恒定大小的 char 缓冲区。以下转换工作正常且实时:

char buffer[BUFFER_SIZE]; //BUFFER_SIZE = 1000000
...
UINT16 *pBuffer = NULL; //send buffer
UINT16 *pBuffer2 = NULL; //receive buffer
...
//copying to the buffer of correct length to avoid errors. 
//Empirically found that it seems contiguous
memcpy(&buffer[1], pBuffer, nBufferSize*2);//img is int16, nBufferSize*2 ~ 400000
buffer[0] = '~'; //adding special symbol to mark correct data stream

// here supposed to be send-receive with buffer

//convert received result back to UINT16
pBuffer2 = (UINT16*)&buffer[1];

这是来自 pBuffer2 的正确图像的样子: good depth image

现在我通过网络发送这个字符缓冲区:

client_send(buffer);

//receiving frame from server   

receive_data(buffer);

函数如下所示:

客户端(发送):

#define DEFAULT_BUFLEN 1000000 //1MB
...
int client_send(char *sendbuf) {

    // Send an initial buffer

        //iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
        iResult = send(ConnectSocket, sendbuf, DEFAULT_BUFLEN, 0);
        if (iResult == SOCKET_ERROR) {
            printf("send failed with error: %d\n", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            return 1;
        }

        printf("Bytes Sent: %ld\n", iResult);       

    return 0;
}

服务器部分(接收)- 我目前将代码截断到一个客户端:

const int BUFFER_SIZE = 1000000;//1MB
...
int receive_client(_client *current_client, char *buffer, int size)
{
    if (FD_ISSET(current_client->socket, &current_client->socket_data))
    {
        // Store the return data of what we have sent
        current_client->address_length = recv(current_client->socket, buffer, size, 0);

        if (current_client->address_length == 0)
        { // Data error on client
            disconnect_client(current_client);

            return (FALSE);
        }

        return (TRUE);
    }

    return (FALSE);
}

int receive_data(char* buffer)
{
    //char buffer[BUFFER_SIZE];

    for (int j = 0; j < MAX_CLIENTS; j++)
    {
        if (client[j].connected)
        {
            if (receive_client(&client[j], buffer, BUFFER_SIZE))
            {
                if (buffer[0] == '~') {
                    // if we have correct packet                    
                    return 1;
                    //buffer[0] = '/0';
                } 
            }
        }
    }

    return 0;
}

结果变得随机困惑,就像帧在 XY 平面上随机移动一样,并且随着时间的推移移动是恒定的(尽管不同 session 不同):

depth_ugly

我已确保正确的流以 ~ 符号开头,但我不知道该转变的性质。

最佳答案

所以解决方案是:

没有必要以额外的小块发送/接收,如果带宽允许,您可以使用每个数据包 1MB。问题是一开始发送/接收的数据包长度可能不同(等一下,我会解释什么是 buffer2):

SOCKET ESTABLISHED
Binding socket:0
Socket Bound to port : 4000
ACCEPTING CLIENT to array position [0] with IP ADDRESS 127.0.0.1
buffer received: -1, 
Bytes Sent: 1000000
buffer received: 992800, 
got beg. packet
buffer2 received: 7200, 
Bytes Sent: 1000000
buffer received: 1000000, 
got beg. packet
buffer2 received: 0, 
Bytes Sent: 1000000
buffer received: 1000000, 
got beg. packet
buffer2 received: 0, 
Bytes Sent: 1000000
Bytes Sent: 1000000
buffer received: 1000000, 
got beg. packet
buffer2 received: 0, 

所以解决方案是在图像的开头添加一个可靠的打开序列:[strbegin][...image data..] 并接收 1MB 数据包,在那里搜索打开序列:

/* BUFFER_SIZE = 1000000 */

int receive_client(_client *current_client, char *buffer, int size)
{
    if (FD_ISSET(current_client->socket, &current_client->socket_data))
    {
         char smallbuffer[BUFFER_SIZE]; //secondary incoming buffer
         current_client->address_length = recv(current_client->socket, smallbuffer, BUFFER_SIZE, 0); //recv returns the number of bytes received 

         if (current_client->address_length == 0)
            { // Data error on client
                disconnect_client(current_client);

                return (FALSE);
            }

            printf("buffer received: %d, \n", current_client->address_length);

            char * pch = strstr(smallbuffer, strbegin); //searching for the opening sequence

当您遇到它时,您需要将 smallbuffer 的其余部分复制到结果变量中并读取下一个数据包。但要小心,因为收到的数据包可能小于 1MB!所以你需要考虑接收到的字节数(数据没有丢失,它只是随着下一个 TCP block 到达):

if (pch != nullptr) { // if we encountered the beginning in some way

    printf("got beg. packet\n");

    int position = pch - smallbuffer; // pointer subtraction, http://stackoverflow.com/questions/7500892/get-index-of-substring

    int substringLength = (current_client->address_length) - position;

    memcpy(&buffer[0], pch, substringLength); // copy the rest of the data into buffer

    current_client->address_length = recv(current_client->socket, &buffer[substringLength], size-substringLength, 0); //read the rest of the message 

    printf("buffer2 received: %d, \n", current_client->address_length);

}

您可以在上面看到,此代码在几步之后与数据到达同步,在第一遍中读取了整个 1MB 消息。此代码中也可能出现一些丢帧,但我的实时传输不需要精度。

对了,我的服务器没有阻塞,否则程序挂了:

// This makes the server non blocking, hence it won't wait for a response
    unsigned long b = 1;
    ioctlsocket(server_socket, FIONBIO, &b);

编辑:好吧,在我从本地主机切换到真实 LAN 网络后,我发现传输的消息的实际大小约为 40KB。因此接收方需要修改才能从大量 block 中重新组合数据(请注意,当没有数据被读取时我没有打破循环,因为服务器更新现在比数据到达更快):

int receive_client(_client *current_client, char *buffer, int size)
{
    if (FD_ISSET(current_client->socket, &current_client->socket_data))
    {

        //attempt first read into secondary buffer
            current_client->address_length = recv(current_client->socket, smallbuffer, BUFFER_SIZE, 0); //recv returns the number of bytes received 

            if (current_client->address_length == 0)
            { // Data error on client
                disconnect_client(current_client);

                return (FALSE);
            }

            printf("buffer received: %d, \n", current_client->address_length);

            char * pch = strstr(smallbuffer, strbegin); //searching for the opening sequence

            if (pch != nullptr && (current_client->address_length > 0)) { // if we encountered the beginning in some way

                printf("got beg. packet\n");

                int position = pch - smallbuffer; // pointer subtraction, http://stackoverflow.com/questions/7500892/get-index-of-substring

                int substringLength = (current_client->address_length) - position;

                memcpy(&buffer[0], pch, substringLength); // copy the rest of the data into buffer

                int tmpsize = size - substringLength;   // how many we still need to read

                int count = 1;
                int where_we_are = substringLength;
                do  // repeat until buffer is filled
                {
                    current_client->address_length = recv(current_client->socket, smallbuffer, tmpsize, 0); //read the next chunk
                    substringLength = current_client->address_length; // how much we read this time

                    if (substringLength > 0){
                        memcpy(&buffer[where_we_are], smallbuffer, substringLength); //add it to buffer

                        where_we_are += substringLength;
                        tmpsize = tmpsize - substringLength;
                        count++;
                    }
                    else {
                        //tmpsize = 0; // nope, can't break - need to read until the end ! Seems like reading is performed faster than data arrival
                    }
                } while (tmpsize > 0);

                printf("buffer2 received: %d chunks \n", count);


            }


        return (TRUE);
    }

从里面看是怎样的:

buffer received: -1, 
buffer received: 182500, 
got beg. packet
buffer2 received: 356 chunks 
buffer received: -1, 
buffer received: -1, 
buffer received: -1, 
buffer received: -1, 
buffer received: -1, 
buffer received: -1, 
buffer received: -1, 
buffer received: 589840, 
got beg. packet
buffer2 received: 238 chunks 
buffer received: -1, 
buffer received: 589940, 
got beg. packet
buffer2 received: 168 chunks 
buffer received: -1, 
buffer received: 562936, 
got beg. packet
buffer2 received: 227 chunks 
buffer received: -1, 
buffer received: 590140, 
got beg. packet
buffer2 received: 204 chunks 
buffer received: -1, 
buffer received: 590240, 

关于c++ - Winsock 移动数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43005600/

相关文章:

c++ - OpenGL 文本忽略转义序列

c++ - Windows Server 2012 上的 SNMP Extension Agent 无法连接到它需要数据的端口

C++ 奇怪的 memcpy 行为

java - Socket Java 到 C++ - 消息的长度始终为 8192 个字符

c++ - Openssl需要使用CA捆绑文件(中级证书)

C++ 初始化结构体 vector 的正确方法

c++ - 比较两个字符串并从字符串 1 中删除相同的字符并使用 C++ 打印字符串 2

c++ - emplace_back + ofstream + windows -> 错误?

winapi - 在 Windows 上获取对等套接字的 PID

sockets - 检查套接字是否阻塞(特定于 Winsock)