Есть источник (камера), который отправляет уведомления (сигналы тревоги движения) на порт 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;
}
Примечания:
- принимает функции, и я завершаю программу нажатием Ctrl + C. При следующем запуске программы (C ++) я получаю пакеты, которые раньше не мог получить!
- ОС - 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#. Возможно, если я оставлю это работающим, системная память заполнится. Размер буфера может быть барьером. Во всяком случае, если предположить, что мои предположения верны, что я могу, как вы думаете, В чем проблема в моем коде, которая не приводит к освобождению буфера? И что я могу сделать?