c++ - 接收数据包发送后,等待功能等待

标签 c++ linux sockets go camera

有一个源(摄像机)将通知(运动警报)发送到我为其预订IP的服务器的端口8085。也许在这样的谈判中,我不能称其为“服务器”,但这是我们的服务器提供其他一些服务。
我已经编写了两个程序来接收和回传当前的运动状态,目前仅在屏幕上。接收到的数据包是XML形状的,我可以解析它并找到所需的信息。为了进行测试,我只提取标记为UtcTime的时间。另一台计算机使用Onvif设备管理器订阅了同一台摄像机,因此我可以检查是否错过了一些“时间”。这些程序之一在GoLang中,而一个在C++中。前者按预期工作,而后者则不然。也许这(C++)是我在套接字编程方面的第一次经验。在C++中,我没有使用任何其他库,并且我使用了传统的套接字编程方法,也不需要在客户现场使用任何其他库。问题在于,ODM会回荡新的时间(或说通知消息)以及GoLang代码,而C++代码会停留在accept函数(accepting ... message)上。我给你们两个人调查。

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "strings"
)

func count() (f func() int) {
    var counter int
    f = func() int {
        counter++
        return counter
    }
    return
}

func main() {
    http.HandleFunc("/", Server)
    http.ListenAndServe(":8085", nil)
}

func Server(w http.ResponseWriter, r *http.Request) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        log.Fatal(err)
    }
    str := string(body)
    for {
        index := strings.Index(str, "UtcTime")
        if index == -1 {
            break
        }
        part := str[index+20 : index+28]
        fmt.Printf("%s\n", part)
        str = str[index+28:]
    }
}

这是GoLang中正常工作的代码。现在,C++代码无法按预期运行并坚持接受:
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string>

#define DBG printf("%s:%d\n", __FILE__, __LINE__)
#define DIE die(__FILE__, __LINE__)

void die(const char *file, int line)
{
    printf("%s:%d: %s\n", file, line, strerror(errno));
    exit(1);
}

std::string extractTime(const char *utc)
{
    char buf[80];
    memcpy(buf, utc + 11, 8);
    buf[9] = 0;
    return buf;
}

int main()
{
    printf("creating socket...\n");
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1)
        DIE;
    int reuseaddr = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1)
        DIE;

    printf("binding...\n");
    sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8085);
    addr.sin_addr.s_addr = INADDR_ANY;
    if (bind(sock, (sockaddr *)&addr, sizeof(addr)) == -1)
        DIE;

    printf("listening...\n");
    if (listen(sock, SOMAXCONN) == -1)
        DIE;
    socklen_t size = sizeof(addr);
    while (true)
    {
        printf("accepting...\n");
        int new_sock = accept(sock, 0, 0);
        if (new_sock < 0)
            DIE;
        const int buf_size = 80;
        char buf[buf_size * 2 + 1];
        memset(buf, 0, buf_size);
        int read_size;
        std::string time;
        while (true)
        {
            memcpy(buf, buf + buf_size, buf_size);
            read_size = recv(new_sock, buf + buf_size, buf_size, 0);
            if (read_size < 0)
                DIE;
            buf[buf_size + read_size] = 0;
            char *p = strstr(buf, "UtcTime");
            if (p && (p - buf < buf_size))
            {
                char buf2[80];
                char *p2 = strstr(p + 9, "\"");
                if (p2)
                {
                    memcpy(buf2, p + 9, p2 - p - 9);
                    buf2[p2 - p - 9] = 0;
                    time = extractTime(buf2);
                    printf("%s\n", time.c_str());
                }
            }
            if (strstr(buf, "</SOAP-ENV:Envelope>"))
                break;
        }
    }
    return 0;
}

笔记:
  • 接受功能键,然后按Ctrl + C终止程序。下次我运行该程序(C++)时,我得到了以前无法获得的数据包!
  • OS是Linux(Ubuntu 12.04)。 GoLang代码可在同一操作系统上使用。

  • 更新:
    C++代码现在使用一个单独的过程来接收数据包:
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <errno.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <netinet/in.h>
    #include <string>
    
    #define DBG printf("%s:%d\n", __FILE__, __LINE__)
    #define DIE die(__FILE__, __LINE__)
    
    void die(const char *file, int line)
    {
        printf("%s:%d: %s\n", file, line, strerror(errno));
        exit(1);
    }
    
    std::string extractTime(const char *utc)
    {
        char buf[80];
        memcpy(buf, utc + 11, 8);
        buf[9] = 0;
        return buf;
    }
    
    void receive(int s2)
    {
        const int buf_size = 80;
        char buf[buf_size * 2 + 1];
        memset(buf, 0, sizeof(buf));
        int read_size;
        std::string time;
        FILE *fp = fopen("dump.bin", "ab");
        if (!fp)
            DIE;
        while (true)
        {
            memcpy(buf, buf + buf_size, buf_size);
            read_size = recv(s2, buf + buf_size, buf_size, 0);
            if (read_size < 0)
                DIE;
            if (read_size == 0)
                break;
            if (fwrite(buf + buf_size, buf_size, 1, fp) != 1)
                DIE;
            buf[buf_size + read_size] = 0;
            //printf("%d bytes received: '%s'\n", read_size, buf + buf_size);
            char *p = strstr(buf, "UtcTime");
            if (p && (p - buf < buf_size))
            {
                char buf2[80];
                char *p2 = strstr(p + 9, "\"");
                if (p2)
                {
                    memcpy(buf2, p + 9, p2 - p - 9);
                    buf2[p2 - p - 9] = 0;
                    time = extractTime(buf2);
                    printf("%s\n", time.c_str());
                }
            }
            if (read_size < buf_size)
                break;
        }
        fclose(fp);
        close(s2);
    }
    
    int main()
    {
        printf("creating socket...\n");
        int s1 = socket(AF_INET, SOCK_STREAM, 0);
        if (s1 == -1)
            DIE;
        int reuseaddr = 1;
        if (setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1)
            DIE;
    
        printf("binding...\n");
        sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(8085);
        addr.sin_addr.s_addr = INADDR_ANY;
        if (bind(s1, (sockaddr *)&addr, sizeof(addr)) == -1)
            DIE;
    
        printf("listening...\n");
        if (listen(s1, 0) == -1)
            DIE;
        socklen_t size = sizeof(addr);
        while (true)
        {
            printf("accepting...\n");
            int s2 = accept(s1, 0, 0);
            if (s2 < 0)
                DIE;
            int pid = fork();
            if (pid == 0)
            {
                receive(s2);
                break;
            }
        }
        return 0;
    }
    

    更新:
    我也检查过C#:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Net.Sockets;
    using System.Net;
    
    namespace tcplistener
    {
        class Program
        {
            static void Main(string[] args)
            {
                const int port = 8081;
    
                TcpListener tcpListener = new TcpListener(IPAddress.Any, port);
                tcpListener.Start();
    
                while (true)
                {
                    Socket socketConnection = tcpListener.AcceptSocket();
                    var buf = new byte[80000];
                    var size = socketConnection.Receive(buf);
                    Console.WriteLine("{0} bytes received", size);
                    Console.WriteLine("{0}", Encoding.UTF8.GetString(buf, 0, size).Substring(0, 80));
                    socketConnection.Disconnect(true);
                }
            }
        }
    }
    

    尽管此代码也无法按预期工作,但我可以找到一些线索。您会看到我将端口更改为8081。这是因为8085已满!这就是我的结论,您应该确认我是否正确。第一次,当我运行程序时,我得到以下输出:
    2919 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    5395 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    7871 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    13408 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    16503 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    18979 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    21455 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    23931 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    26407 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    28883 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    31359 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33835 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    43739 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    45260 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    36500 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    45260 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    42340 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    33580 bytes received
    POST /behnama-subscription HTTP/1.1
    Host: 192.168.14.127:8081
    SOAPAction: http
    

    精彩!似乎Receive不会读出(不会移动)它得到的内容。下次我再次调用接收时,接收到的数据包将保留在此处。第三次读取新接收到的数据包以及两个旧数据包!缓冲区大小逐渐增加,直到足够大为止。此后,大小会保持很高,而每次可能会减小或增大!直到我不再收到任何新包装。接受不返回,端口变满且无用。如果我再次运行该程序,我将不再有任何答案,它会停留在AcceptSocket上,或者立即接受整个缓冲区,第二次调用AcceptSocket会停留。
    因此,乍一看,我可以说问题似乎是由于recv没有释放接收到的数据包引起的。我认为对于C++程序也是如此。但是GoLang代码呢?我认为这可能有用,因为缓冲区的大小不受限制,因为它在C++或C#代码中。也许如果我让它运行,则系统内存将满。缓冲区的大小可能是障碍。无论如何,假设我的假设是正确的,你会怎么想?我的代码中有什么问题不会导致缓冲区释放?那我该怎么办?

    最佳答案

    您的C++变体有一些有趣的缺陷:

  • 初始化buf_size的前80个(buf)字符,然后从未初始化的后80个字符(第一个memcpy)中读取。
  • 您将memcpy从缓冲区的后半部分开始到第一个字节的80个字节,而不管缓冲区中实际上有多少个有用/已初始化的字节(两个字节)。
  • 调用一次accept,以获得连接的套接字,然后读取直到错误并退出。这会破坏侦听套接字,而其余连接不被接受。如果得到EOF(read_size为0),则您将永久旋转(可能)而不退出,因为以后的每个read也会返回0。
  • 如果在缓冲区的任何位置都找到字符串"UtcTime",则您将搜索一个双引号,该双引号会在9个字节后开始,即使"UtcTime"距离缓冲区末尾少于9个字节也是如此。

  • 我怀疑如果修复所有这些问题,则效果可能会更好。通常,服务器应剥离线程或进程以处理每个新的传入连接,并使主线程/进程返回accept以等待其他套接字。 (使用Go编写代码非常容易,因为Go具有内置的所有正确的东西。)

    关于c++ - 接收数据包发送后,等待功能等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59663534/

    相关文章:

    c++ - 在未完成的类层次结构中的 "final"类上使用虚拟继承

    php - 处理、解析和流式传输大型文本文件的实用方法是什么?

    Python subprocess.check_output 转换为 windows

    java - 无法在 Java 中通过套接字发送大文件

    linux - 我可以监控 Linux 套接字缓冲区满度吗?

    java - 有没有办法强制程序留在 RAM 中

    c++ - float 转换 C++

    c++ - 使用 QtMobility 检测漫游(切换接入点)?

    java - 网络服务失败。 java.net.SocketTimeoutException : Read timed out - Using Jboss 7. 1