1. Обязательно представиться на русском языке кириллицей (заполнить поле "Имя").
  2. Фиктивные имена мы не приветствуем. Ивановых и Пупкиных здесь уже достаточно.
  3. Не писать свой вопрос в первую попавшуюся тему - вместо этого создать новую тему.
  4. За поиск, предложение и обсуждение пиратского ПО и средств взлома - бан без предупреждения.
  5. Рекламу и частные объявления "куплю/продам/есть халтура" мы не размещаем ни на каких условиях.
  6. Перед тем как что-то написать - читать здесь и здесь, а студентам - обязательно здесь.
  7. Не надо писать в ЛС администраторам свои технические вопросы. Администраторы форума отлично знают как работает форум, а не все-все контроллеры, о которых тут пишут.

Связь через TCP сокеты

RS-485, ProfiBUS, 4-20 mA, Wi-Fi, GSM и так далее
Ответить

Автор темы
Joshua
здесь недавно
здесь недавно
Сообщения: 76
Зарегистрирован: 21 мар 2023, 02:12
Имя: Владимир
Страна: РФ
город/регион: Псков
Благодарил (а): 2 раза
Поблагодарили: 9 раз

Связь через TCP сокеты

Сообщение Joshua »

Здравствуйте.
Пытаюсь передавать одним пакетом 32 кБ между Siemens CPU317-2 PN/DP и Овен ПЛК-200-2 (вместо Овен сейчас стандартный симулятор Codesys на ПК) через сокеты TCP.
На сименсе использую FB63-66 (TCON,TSEND,TRCV,TDISCON), в Codesys библиотеку NBS.TCP_xx.
Отправка пакета из симулятора (с ПК) на сименс проходит без проблем. Все 32кб полностью заливаются без ошибок.
Проблема с обратной отправкой из сименса в Codesys на ПК. Wireshark показывает, что сименс отправляет пакет 32кб полностью, но в симулятор приходит только часть. Похоже, что функция NBS.TCP_Read отрабатывает несколько раз, с каждым разом указатель на блок данных для записи обнуляется и полученный пакет записывается частями каждый раз с начала блока данных.
NBS.TCP_Read крутится в основном цикле, никаких задержек и условий вызова перед ним нет.
В брэндмауэре windows исключения для программ добавлены, порты открыты.
Ниже в коде лог Wireshark. 10.16 - ПК; 10.120 - ПЛК. ПЛК шлет пакет TCP с флагом PSH, указывающий, что данные не должны складываться в буфер, возможно это влияет на то, как NBS.TCP_Read определяет конец посылки.
Собственно вопрос: почему данные не принимаются одним куском? Что может на это влиять?

Код: Выделить всё

7953	12.321344	192.168.10.16	192.168.10.120	TCP	7354	20001 → 20002 [ACK] Seq=1 Ack=1 Win=64240 Len=7300
7957	12.326423	192.168.10.120	192.168.10.16	TCP	60	20002 → 20001 [ACK] Seq=1 Ack=7301 Win=8192 Len=0
7958	12.326436	192.168.10.16	192.168.10.120	TCP	7354	20001 → 20002 [ACK] Seq=7301 Ack=1 Win=64240 Len=7300
7959	12.332485	192.168.10.120	192.168.10.16	TCP	60	20002 → 20001 [ACK] Seq=1 Ack=14601 Win=8192 Len=0
7960	12.332514	192.168.10.16	192.168.10.120	TCP	7354	20001 → 20002 [ACK] Seq=14601 Ack=1 Win=64240 Len=7300
7963	12.337461	192.168.10.120	192.168.10.16	TCP	60	20002 → 20001 [ACK] Seq=1 Ack=21901 Win=2676 Len=0
7964	12.337476	192.168.10.16	192.168.10.120	TCP	1514	20001 → 20002 [ACK] Seq=21901 Ack=1 Win=64240 Len=1460
7967	12.341526	192.168.10.120	192.168.10.16	TCP	60	[TCP Window Update] 20002 → 20001 [ACK] Seq=1 Ack=21901 Win=8192 Len=0
7968	12.341567	192.168.10.16	192.168.10.120	TCP	5894	20001 → 20002 [ACK] Seq=23361 Ack=1 Win=64240 Len=5840
7969	12.344612	192.168.10.120	192.168.10.16	TCP	60	20002 → 20001 [ACK] Seq=1 Ack=24821 Win=6732 Len=0
7970	12.344669	192.168.10.16	192.168.10.120	TCP	1514	20001 → 20002 [ACK] Seq=29201 Ack=1 Win=64240 Len=1460
7971	12.349413	192.168.10.120	192.168.10.16	TCP	60	20002 → 20001 [ACK] Seq=1 Ack=30661 Win=8192 Len=0
7972	12.349451	192.168.10.16	192.168.10.120	TCP	2160	20001 → 20002 [PSH, ACK] Seq=30661 Ack=1 Win=64240 Len=2106
7973	12.350549	192.168.10.120	192.168.10.16	TCP	60	20002 → 20001 [ACK] Seq=1 Ack=32767 Win=6086 Len=0
7974	12.352771	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=1 Ack=32767 Win=8192 Len=1460
7975	12.353660	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=1461 Ack=32767 Win=8192 Len=1460
7976	12.353670	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=2921 Win=64240 Len=0
7978	12.354778	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [ACK] Seq=2921 Ack=32767 Win=8192 Len=1460
7979	12.355757	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [ACK] Seq=4381 Ack=32767 Win=8192 Len=1460
7980	12.355765	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=5841 Win=64240 Len=0
7981	12.356690	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [ACK] Seq=5841 Ack=32767 Win=8192 Len=1460
7982	12.358582	192.168.10.120	192.168.10.16	TCP	946	20002 → 20001 [ACK] Seq=7301 Ack=32767 Win=8192 Len=892
7983	12.358587	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=8193 Win=64240 Len=0
7984	12.358702	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [ACK] Seq=8193 Ack=32767 Win=8192 Len=1460
7985	12.359282	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=9653 Ack=32767 Win=8192 Len=1460
7986	12.359286	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=11113 Win=64240 Len=0
7987	12.361730	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [ACK] Seq=11113 Ack=32767 Win=8192 Len=1460
7988	12.361730	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=12573 Ack=32767 Win=8192 Len=1460
7989	12.361762	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=14033 Win=64240 Len=0
7990	12.363716	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=14033 Ack=32767 Win=8192 Len=1460
7993	12.367505	192.168.10.120	192.168.10.16	TCP	946	20002 → 20001 [ACK] Seq=15493 Ack=32767 Win=8192 Len=892
7994	12.367520	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=16385 Win=64240 Len=0
7995	12.367672	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=16385 Ack=32767 Win=8192 Len=1460
7996	12.368292	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=17845 Ack=32767 Win=8192 Len=1460
7997	12.368297	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=19305 Win=64240 Len=0
7998	12.368680	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=19305 Ack=32767 Win=8192 Len=1460
7999	12.369297	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=20765 Ack=32767 Win=8192 Len=1460
8000	12.369303	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=22225 Win=64240 Len=0
8003	12.372705	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=22225 Ack=32767 Win=8192 Len=1460
8004	12.375586	192.168.10.120	192.168.10.16	TCP	946	20002 → 20001 [ACK] Seq=23685 Ack=32767 Win=8192 Len=892
8005	12.375598	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=24577 Win=64240 Len=0
8006	12.376143	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=24577 Ack=32767 Win=8192 Len=1460
8007	12.376682	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=26037 Ack=32767 Win=8192 Len=1460
8008	12.376690	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=27497 Win=64240 Len=0
8009	12.376785	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=27497 Ack=32767 Win=8192 Len=1460
8010	12.377647	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=28957 Ack=32767 Win=8192 Len=1460
8011	12.377655	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=30417 Win=64240 Len=0
8013	12.380808	192.168.10.120	192.168.10.16	TCP	1514	20002 → 20001 [PSH, ACK] Seq=30417 Ack=32767 Win=8192 Len=1460
8031	12.422079	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=31877 Win=64240 Len=0
8032	12.422696	192.168.10.120	192.168.10.16	TCP	944	20002 → 20001 [PSH, ACK] Seq=31877 Ack=32767 Win=8192 Len=890
8063	12.468514	192.168.10.16	192.168.10.120	TCP	54	20001 → 20002 [ACK] Seq=32767 Ack=32767 Win=63350 Len=0
Отправлено спустя 51 минуту 3 секунды:
Дополнение:
По выходу NBS.TCP_Read.xDone записываю количество полученных байт и количество пакетов TCP. Каждый раз при отправке получаю 3-5 пакетов с разным количеством байт, последний соответствует размеру, который показывает Wireshark для последнего TCP пакета (например 890 как в логе), но это всё равно кусочек от общих 32 кб.
Ошибок NBS.TCP_Read.xErros за время обмена не возникает.

AlexandrGr
освоился
освоился
Сообщения: 218
Зарегистрирован: 26 май 2022, 12:10
Имя: Александр
Страна: Россия
город/регион: lipetsk
Благодарил (а): 3 раза
Поблагодарили: 17 раз

Связь через TCP сокеты

Сообщение AlexandrGr »

Joshua писал(а): 14 апр 2023, 12:37 Собственно вопрос: почему данные не принимаются одним куском? Что может на это влиять?
А должны?

Автор темы
Joshua
здесь недавно
здесь недавно
Сообщения: 76
Зарегистрирован: 21 мар 2023, 02:12
Имя: Владимир
Страна: РФ
город/регион: Псков
Благодарил (а): 2 раза
Поблагодарили: 9 раз

Связь через TCP сокеты

Сообщение Joshua »

AlexandrGr писал(а): 14 апр 2023, 14:07А должны?
А почему нет? Даже обязаны.
TCP позволяет переслать за один раз 65 кб, протокол уже сам разбивает данные на пакетики в зависимости от MTU и ширины окна, а принимающая сторона получает и склеивает их обратно, потом только данные отдаются выше адресату. Для приложений это происходит прозрачно.
Конкретно CPU317-2 PN/DP может максимум обработать 32 кб. И ведь он их получает за один раз целиком. Я не разбиваю данные на пакеты перед отправкой, как отправил 32кб потоком, так сименс их и принял.

AlexandrGr
освоился
освоился
Сообщения: 218
Зарегистрирован: 26 май 2022, 12:10
Имя: Александр
Страна: Россия
город/регион: lipetsk
Благодарил (а): 3 раза
Поблагодарили: 17 раз

Связь через TCP сокеты

Сообщение AlexandrGr »

Я промолчу.
Аватара пользователя

keysansa
эксперт
эксперт
Сообщения: 2340
Зарегистрирован: 20 дек 2018, 04:45
Имя: Сергей
Страна: РБ/РФ
город/регион: РФ Сергиев Посад
Благодарил (а): 1998 раз
Поблагодарили: 176 раз

Связь через TCP сокеты

Сообщение keysansa »

AlexandrGr писал(а): 14 апр 2023, 17:25 Я промолчу.
Почему? Это же форум. Обмен мнениями.
ЗЫ.
Joshua писал(а): 14 апр 2023, 12:37 Собственно вопрос: почему данные не принимаются одним куском? Что может на это влиять?
Вы откройте сокет/стрим на стороне Owen. Если будете оперировать пакетами - MTU вас ограничит.

ЗЫ. Wireshark у вас на стороне Codesys. Т.е. по интерфейсу все приходит, а Codesys не может пакеты обработать?
В трансформаторной будке живет трансформаторная собака (с) Прозрачный гонщик.

Автор темы
Joshua
здесь недавно
здесь недавно
Сообщения: 76
Зарегистрирован: 21 мар 2023, 02:12
Имя: Владимир
Страна: РФ
город/регион: Псков
Благодарил (а): 2 раза
Поблагодарили: 9 раз

Связь через TCP сокеты

Сообщение Joshua »

Доброе утро keysansa.
Сокет у меня на стороне Codesys, т.е. как сервер. Сименс - клиент.
Wireshark показывает, что по интерфейсу приходит весь блок данных (Seq=32767 Ack=32767) за один сеанс обмена, естественно пакетами с размером MTU. Codesys не может принять данные как один блок, ему кажется, что это несколько посылок. Если бы он считал именно пакеты по MTU, то по логу выше получилось бы 24 пакета, а не 2-5 штук как у меня.
Пока игрался с настройками протокола TCP в Windows получилось стабильно принимать 32 кБ одним блоком данных, но при этом они смещены, будто что-то остаётся в приёмном буфере и передаётся в Codesys только в следующей посылке. Выглядит примерно так:
1 получение 20 кб, следующие 32 - 32 - 32 .. 32, но при этом со смещением в 12 кб.
Аватара пользователя

keysansa
эксперт
эксперт
Сообщения: 2340
Зарегистрирован: 20 дек 2018, 04:45
Имя: Сергей
Страна: РБ/РФ
город/регион: РФ Сергиев Посад
Благодарил (а): 1998 раз
Поблагодарили: 176 раз

Связь через TCP сокеты

Сообщение keysansa »

Joshua писал(а): 17 апр 2023, 09:19 Codesys не может принять данные как один блок, ему кажется, что это несколько посылок.
Покажите код.
ЗЫ. Вы похоже где-то сами пропускаете чтение.
В трансформаторной будке живет трансформаторная собака (с) Прозрачный гонщик.

Автор темы
Joshua
здесь недавно
здесь недавно
Сообщения: 76
Зарегистрирован: 21 мар 2023, 02:12
Имя: Владимир
Страна: РФ
город/регион: Псков
Благодарил (а): 2 раза
Поблагодарили: 9 раз

Связь через TCP сокеты

Сообщение Joshua »

keysansa писал(а): 18 апр 2023, 21:03 Покажите код.

Код: Выделить всё

PROGRAM PLC_PRG
VAR
	fbTcpServer: 		NBS.TCP_Server;					// ФБ TCP-сервера
	incomeLink:			CONNECTION;						// Структура для обработки входящих соединений
	sendDB:				transferDB;						// Блок данных для отправки
	rcvdDB:				transferDB;						// Блок данных для получения	
	xServer:			BOOL;							// Флаг разрешения работы сервера
	
	fbTime:				Util.DateTimeProvider;			// Запрашивам текущее время
	sendTimestamp:		ULINT;							// Временная метка начала отправки
	rcvdTimestamp:		ULINT;							// Временная метка получения данных
	transferTime:		ULINT;							// Время передачи туда-обратно
	
	fSend:				BOOL;							// Флаг отправки
	sDataLen: 			UDINT;							// Длина тестового пакета для отправки 
	
	fFillData:			BOOL;							// Флаг заполнения блока данными
	FillData:			BYTE;							// Байт для заполнения
	tpackets:			UDINT;							// Количество принятых пакетов
	bytesRecieved:		UDINT;				
	
END_VAR

VAR CONSTANT
	localServerPort:	UINT			:= 20001;		// Порт локального сервера
	DefaultDataLen:		UDINT			:= 32766;		// Максимальный размер пакета для CPU-317
	FillDataStep:		BYTE			:= 3;			// Добавка на каждый шаг
END_VAR

//---------------------
// Создаем сервер на заданном порту
IF xServer THEN
	fbTcpServer	
	(
		xEnable	:= TRUE,  
		ipAddr	:= , 
		uiPort	:= localServerPort 
	);
	sDataLen := DefaultDataLen;			// Размер данных для передачи
END_IF

// Обработчик входящего соединения
incomeLink.fbTcpConnection
(
	xEnable:=fbTcpServer.xBusy, 
	hServer:=fbTcpServer.hServer
);

// Заполняем пакет данными
IF fFillData THEN
	FillData := FillData + FillDataStep;
	MEM.MemFill(pMemoryBlock:= ADR (sendDB.DataBlock), uiLength:= SIZEOF (sendDB.DataBlock), byFillValue:= FillData);
	fSend := TRUE;
	fFillData := FALSE;
END_IF

// Отправка пакета
IF fSend AND incomeLink.fbTcpConnection.xBusy THEN
		tpackets := 0;
		transferTime := 0;
		sendTimestamp := fbTime.GetDateTime();
		incomeLink.fbTcpWrite
		(
			xExecute	:=	TRUE,
			hConnection	:=	incomeLink.fbTcpConnection.hConnection,
			pData		:=	ADR(sendDB.DataBlock),
			szSize		:=	sDataLen
		);
		IF incomeLink.fbTcpWrite.xDone THEN
			incomeLink.fbTcpWrite(xExecute:=FALSE);
			fSend := FALSE;
		END_IF
END_IF

// Получаем данные от клиента
incomeLink.fbTcpRead
(
	xEnable		:=	incomeLink.fbTcpConnection.xActive, 
	hConnection	:=	incomeLink.fbTcpConnection.hConnection,
	pData		:=	ADR(rcvdDB.DataBlock),
	szSize		:=	sDataLen
);

// Считаем полученные данные
IF incomeLink.fbTcpRead.xReady THEN
	rcvdTimestamp := fbTime.GetDateTime();
	transferTime := rcvdTimestamp - sendTimestamp; //ms
	tpackets := tpackets + 1;
	bytesRecieved := incomeLink.fbTcpRead.szCount;
END_IF
NBS.TCP_Read крутится в основном цикле, никаких проверок и задержек перед ним нет.
xServer и fFillData запускаю кнопкой из визуализации.

AlexandrGr
освоился
освоился
Сообщения: 218
Зарегистрирован: 26 май 2022, 12:10
Имя: Александр
Страна: Россия
город/регион: lipetsk
Благодарил (а): 3 раза
Поблагодарили: 17 раз

Связь через TCP сокеты

Сообщение AlexandrGr »

Между двумя приложениями по TCP соединению осуществляется обмен потоком 8-битовых байтов. Автоматически TCP не вставляет записи маркеров. Это называется сервисом потока байтов (byte stream service). Если приложение на одном конце записало сначала 10 байт, затем 20 байт и затем еще 50 байт, приложение на другом конце соединения не может сказать какого размера была каждая запись. На другом конце эти 80 байт могут быть считаны, например, за 4 раза по 20 байт за каждый раз. Один конец соединения помещает поток байт в TCP, и точно так же идентичный поток байт появляется на другом конце.
И еще.
https://ru.stackoverflow.com/questions/ ... %D0%B8-tcp

Автор темы
Joshua
здесь недавно
здесь недавно
Сообщения: 76
Зарегистрирован: 21 мар 2023, 02:12
Имя: Владимир
Страна: РФ
город/регион: Псков
Благодарил (а): 2 раза
Поблагодарили: 9 раз

Связь через TCP сокеты

Сообщение Joshua »

AlexandrGr, Спасибо. Обсуждение по предоставленной ссылке это ровно то, чего я до конца не понимал.
Там же упоминание про флаг PSH и про
Соответственно, применение голого ТСП без более высокоуровненого протокола который контролирует длину сообщения бессмысленно?
Я ошибочно полагал, что данные передаются в приложение только после того, как в буфер будет принята вся посылка целиком (до 65 кБ включительно). На деле это происходит непредсказуемым образом. TCP гарантирует только передачу пакетов из А в Б. Работа приёмного буфера и нагрузка (посылка) протоколом вообще никак не контролируется. То, что сименс у меня принимает всё за один раз без ошибок - случайность и совпадение. Просто плк больше ничем не занимается, и приёмный буфер всегда работает одинаково.

По правильному мне самому нужно контролировать начало, количество байт, конец и контрольную сумму посылки. Вопрос можно закрыть.
Аватара пользователя

keysansa
эксперт
эксперт
Сообщения: 2340
Зарегистрирован: 20 дек 2018, 04:45
Имя: Сергей
Страна: РБ/РФ
город/регион: РФ Сергиев Посад
Благодарил (а): 1998 раз
Поблагодарили: 176 раз

Связь через TCP сокеты

Сообщение keysansa »

Joshua писал(а): 19 апр 2023, 13:32 Я ошибочно полагал, что данные передаются в приложение только после того, как в буфер будет принята вся посылка целиком
Можно читать буфер, дожидаясь, пока он заполнится, можно читать кусками.
Это никак не коррелирует с вашим первым сообщением, что вы не можете прочитать данные.
Как-то так
В трансформаторной будке живет трансформаторная собака (с) Прозрачный гонщик.
Ответить

Вернуться в «Интерфейсы, протоколы, связь»