функция accept ожидает отправки пакета - PullRequest
0 голосов
/ 09 января 2020

Есть источник (камера), который отправляет уведомления (сигналы тревоги движения) на порт 8085 сервера, для которого я подписал свой IP. Может быть, в таких переговорах я не могу назвать это «сервером», но наш сервер обслуживает некоторые другие вещи. Я написал две программы для получения и отображения текущего состояния движения, пока только на экране. Полученные пакеты имеют форму XML, которую я могу проанализировать и найти необходимую информацию. Для тестирования я просто извлекаю время, помеченное как UtcTime. Другая машина подписалась на ту же камеру с помощью Onvif Device Manager, чтобы я мог проверить, не пропустил ли я «раз». Одна из этих программ находится на GoLang, а другая на C ++. Первый работает как положено, а второй нет. Возможно, этот (C ++) был моим первым опытом в программировании сокетов. Я не использовал никакой дополнительной библиотеки в то время как в C ++, и я использовал традиционный метод программирования сокетов, чтобы не требовалось использовать любую дополнительную библиотеку на сайте клиента. Проблема заключается в том, что ODM отображает новые времена (или, скажем, уведомления), а также код GoLang, в то время как код C ++ придерживается функции accept (принятие ... сообщения). Я даю вам обоих для расследования.

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;
}

Примечания:

  1. принимает функции, и я завершаю программу нажатием Ctrl + C. При следующем запуске программы (C ++) я получаю пакеты, которые раньше не мог получить!
  2. ОС - 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

Wonderful! Кажется, что Receive не считывает (не удаляется) то, что получает. Полученный пакет останется там в следующий раз, когда я снова позвоню «Получить». В третий раз читается только что полученный пакет, а также два старых! Размер буфера постепенно увеличивается, пока не станет достаточно большим. После этого размер остается высоким, хотя он может уменьшаться с каждым разом или расти! Пока я не получу новый пакет больше. Accept не возвращается, и порт становится полным и бесполезным. Если я снова запускаю программу, я больше не получаю ответа, и он остается в AcceptSocket ИЛИ весь буфер принимается сразу, а второй вызов в AcceptSocket останавливается. Таким образом, я могу сказать, что проблема заключается в том, что полученный пакет не был освобожден программой recv. Я думаю, что то же самое происходит с программой на C ++. Но как насчет кода GoLang? Я думаю, что это может работать, потому что размер буфера не ограничен, как в кодах C ++ или C#. Возможно, если я оставлю это работающим, системная память заполнится. Размер буфера может быть барьером. Во всяком случае, если предположить, что мои предположения верны, что я могу, как вы думаете, В чем проблема в моем коде, которая не приводит к освобождению буфера? И что я могу сделать?

1 Ответ

1 голос
/ 10 января 2020

Ваш вариант C ++ имеет несколько интересных fl aws:

  • Вы инициализируете первые 80 (buf_size) символов buf, но затем читаете из неинициализированных следующих 80 (первый memcpy).
  • Вы memcpy 80 байтов из второй половины буфера в первую, независимо от того, сколько полезных / инициализированных байтов на самом деле в буфере (любая половина ).
  • Вы вызываете accept один раз, чтобы получить подключенный сокет, затем читаете до появления ошибки и выходите. Это разрушает сокет прослушивания, оставшиеся соединения не принимаются. Если вы получите EOF (read_size равно 0), вы будете вращаться (вероятно) вечно, не выходя, поскольку каждое будущее read также будет возвращать 0.
  • Если вы найдете строку "UtcTime" где-нибудь в буфере вы ищете двойную кавычку, которая начинается, начиная с 9 байт позже, даже если "UtcTime" находится на расстоянии менее 9 байт от конца буфера.

Я подозреваю, что если вы исправите все эти все может работать лучше. Как правило, сервер должен выделять поток или процесс для обработки каждого нового входящего соединения, а основной поток / процесс go возвращается к accept для ожидания дополнительных сокетов. (Все это намного проще кодировать в Go, в котором есть все необходимое для этого.)

...