Что нового

Непонятная работа TCPSend

joker2d

Новичок
Сообщения
182
Репутация
0
Использую пример клиент сервера из справки, немного его изменив

Код:
While 1
            ; Запрос данных для передачи
            $sData = 1
            TCPSend($iSocket, StringToBinary($sData, 4))
            Sleep(10000)
            $sData = 2
            TCPSend($iSocket, StringToBinary($sData, 4))

            ; Если происходит сбой отправки данных с @error, это означает, что сокет отключен. Соответственно выход из цикла.
            If @error Then ExitLoop
        WEnd


в результате сервер получает почему то иногда 21 вместо 1 или 2 как я ожидал
почему так происходит ?

список значений который получает сервер примерно такой

192.168.0.8 > 21
192.168.0.8 > 21
192.168.0.8 > 1
192.168.0.8 > 2
192.168.0.8 > 21
192.168.0.8 > 21
192.168.0.8 > 1

пример с функцией TCPRecv я взял полностью из справки ничего не изменив

почему иногда посылается 21, я же всегда шлю 1 или 2 ?
 
M

max1300

Гость
может эти числа 1 и 2 смешиваются между собой :scratch:
 

firex

AutoIT Гуру
Сообщения
943
Репутация
208
AutoIt собирает в буфере все принятые данные, при вызове TCPRecv буфер очищается и функция возвращает все данные разом.

Схематично:
Код:
>>Send 1
>>Send 2
>>Recv=12
>>Send 1
>>Recv 1
>>Send 2
>>Recv 2

Вам необходимо правильно обрабатывать поступающие данные, примеры:
1) Каждый пакет должен начинаться и заканчиваться определенными байтами, так вы сможете отделить один пакет от другого.
2) Статический размер единичного пакета.
3) Использовать синхронизированный обмен данными:
3-1) [Client]>Запрос>[Server]
3-2) [Client]=Ожидание ответа ( если ответ не приходит в течении N ms - обрываем соединение с сервером ).
3-3) [Server]=Обработка полученных данных ( если данные не верны - обрываем соединение с клиентом ).
3-4) [Server]>Ответ>[Client]
3-n) 3-1
 

winstan

Эксплотатор)
Сообщения
406
Репутация
79
20141209-ehk6-42kb.jpg

когда срабатывает второй сенд сразуже срабатывает первый т.к. задержка есть только после отправки первого, а после второго сразу выполняеться цикл с начала и эти данные записываються в поток сразу, далее сервер не успевает прочитать 2 как в потоке поевляеться ещё и 1 и принимает их как один пакет данных
если поставить слипо после второго сенда должно быть нормально
Код:
While 1
            ; Запрос данных для передачи
            $sData = 1
            TCPSend($iSocket, StringToBinary($sData, 4))
            Sleep(10000)
            $sData = 2
            TCPSend($iSocket, StringToBinary($sData, 4))
            Sleep(10000) дополнительный слип

            ; Если происходит сбой отправки данных с @error, это означает, что сокет отключен. Соответственно выход из цикла.
            If @error Then ExitLoop
        WEnd
 
Автор
J

joker2d

Новичок
Сообщения
182
Репутация
0
firex
А можно всю схему на примере autoit показать ?

1) Каждый пакет должен начинаться и заканчиваться определенными байтами, так вы сможете отделить один пакет от другого.
То есть какой то разделитель в конце вставлять типо точки с запятой или я не так понял ?

2) Статический размер единичного пакета.
3) Использовать синхронизированный обмен данными:

Вообще не понял о чем речь сколько не пытался, пример на autoit все бы разъяснил

winstan
Спасибо, почему не работает у меня теперь понятно, кодинга со sleep хотелось бы избежать, хотелось бы более красивое решение
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
joker2d сказал(а):
Вообще не понял о чем речь сколько не пытался, пример на autoit все бы разъяснил

Представьте кассу с конвеером в супермаркете. Как кассир определяет, когда нужно выбить чек тому или иному покупателю? Либо, когда на конвеере встречается разделитель, либо, когда образуется пустой участок на конвеере. Тоже самое и здесь, TCPSend() посылает последовательные данные, которые накапливаются в буфере. TCPRecv() забирает все данные из буфера в момент ее вызова, т.е. ровно столько, сколько TCPRecv() успела послать. Таким образом получается несинхронизированная передача данных. Чтобы навести порядок, вам нужно после каждой переданной порции данных посылать какой-нибудь символ (сигнатуру) для разделения данных в буфере.

Более правильный подход - синхронизированная или управляемая передача данных, как уже писали выше. Скрипт, принимающий данные - клиент, посылающий - сервер. Клиент посылает запрос (любой уникальный символ или число) на прием данных. Сервер в ответ на этот запрос начинает передавать необходимые данные. Если размер данных заранее не известен, то сервер перым делом, например, в первых четырех байтах, передает размер всех данных. Клиент же считывает сначала четыре байта, а затем непосредственно данные до получение требуемого размера.

В вашем же простейшем примере достаточно просто указать требуемое количество считываемых символов в TCPRecv(), и можно вообще убрать задержку:

Код:
TCPRecv($ConnectedSocket, 1)


P.S

Вот более сложный пример.
 
Автор
J

joker2d

Новичок
Сообщения
182
Репутация
0
Yashied сказал(а):
Если размер данных заранее не известен, то сервер перым делом, например, в первых четырех байтах, передает размер всех данных. Клиент же считывает сначала четыре байта, а затем непосредственно данные до получение требуемого размера.

Общую схему понял спасибо! вот только вопрос как в первых 4 байтах записать размер всех данных и потом считать их?
 

winstan

Эксплотатор)
Сообщения
406
Репутация
79
joker2d сказал(а):
Yashied сказал(а):
Если размер данных заранее не известен, то сервер перым делом, например, в первых четырех байтах, передает размер всех данных. Клиент же считывает сначала четыре байта, а затем непосредственно данные до получение требуемого размера.

Общую схему понял спасибо! вот только вопрос как в первых 4 байтах записать размер всех данных и потом считать их?
через минут 20 напишу код


Добавлено:
Сообщение автоматически объединено:

клиент
Код:
TCPStartup()
$iSocket = TCPConnect("127.0.0.1",65432)
While 1
   ; Запрос данных для передачи
   $sData = 1
   MyTCPsend($iSocket,$sData)
   Sleep(10000)
   $sData = 2
   MyTCPsend($iSocket,$sData)

   MyTCPsend($iSocket,"asdasdasd")

            ; Если происходит сбой отправки данных с @error, это означает, что сокет отключен. Соответственно выход из цикла.
   If @error Then ExitLoop
WEnd

Func MyTCPsend($soc,$data)
   $size = BinaryLen($data)

   TCPSend($soc,$size)
   TCPSend($soc,$data)
   MsgBox(0,$size,$data)
EndFunc

сервер
Код:
Local $hGUI = GUICreate("111", 210, 110)
	$pole = GUICtrlCreateEdit("",5,5,200,100)
	GUISetState(@SW_SHOW, $hGUI)

	TCPStartup()

	Local $iListenSocket = TCPListen("127.0.0.1",65432, 100)

		Do ; Wait for someone to connect (Unlimited).
		; Accept incomming connexions if present (Socket to close when finished; one socket per client).
		$iSocket = TCPAccept($iListenSocket)

		; If an error occurred display the error code and return False.
		If @error Then
			$iError = @error
			MsgBox(BitOR($MB_SYSTEMMODAL, $MB_ICONHAND), "", "Server:" & @CRLF & "Could not accept the incoming connection, Error code: " & $iError)
			Return False
		EndIf

		Until $iSocket <> -1 ;if different from -1 a client is connected.

		 GUICtrlSetData($pole,"+++++++++++++")
		While 1
		   $size = TCPRecv($iSocket, 4)
;~ 		   MsgBox(0,"размер входящих данных",$size)
		   if $size >0 Then
		   $data = TCPRecv($iSocket,$size)
		   GUICtrlSetData($pole, GUICtrlRead($pole)&@CRLF&$data)
		   EndIf
		WEnd



Добавлено:
Сообщение автоматически объединено:

нужно будет ещё доработать чтобы нормально передавались числа а не выходили в фотмате памяти 0x01000000
 
Автор
J

joker2d

Новичок
Сообщения
182
Репутация
0
winstan
А почему в функции MyTCPsend переменная $data не переводиться в бинарные данные StringToBinary, как это делаеться во всех примерах по клиент серверу или это не обязательно ?

И почему переменная $size будет записана именно в первых 4 байтах, мож я там какие то очень большие данные буду передавать ? тоже не очень понятен этот момент
 

winstan

Эксплотатор)
Сообщения
406
Репутация
79
joker2d [?]
А почему в функции MyTCPsend переменная $data не переводиться в бинарные данные StringToBinary, как это делаеться во всех примерах по клиент серверу или это не обязательно ?
не вижу в этом особого смысла

И почему переменная $size будет записана именно в первых 4 байтах, мож я там какие то очень большие данные буду передавать ? тоже не очень понятен этот момент
больше чем 2гигабайта?
 

winstan

Эксплотатор)
Сообщения
406
Репутация
79
joker2d [?]
к примеру если 1 символ передаю то размер будет записан в 4 байта ?
это зависит от кодировки, но обычно 1 символ это один байт


Добавлено:
Сообщение автоматически объединено:

но передача одной цифры, уже будет рассматриваться как передача int 32 т.е. 4 байта
 
Верх