Что нового

Многоканальное подключение по TCP

S1R1US

скрипт мне запили!
Сообщения
50
Репутация
6
Доброго времени суток. Подскажите, как реализовать сервер для подключения через TCP/IP на несколько открытых сокетов одновременно?
В примерах справки нашёл элементарные сервер и клиент, в них есть подключение по одному сокету, а как сделать на сколько...

на таком примере:
Код:
Local $g_IP = "127.0.0.1"

; Start The TCP Services
;==============================================
TCPStartup()

; Create a Listening "SOCKET"
;==============================================
Local $MainSocket = TCPListen($g_IP, 65432, 100)
If $MainSocket = -1 Then Exit

;  look for client connection
;--------------------
While 1
    Local $ConnectedSocket = TCPAccept($MainSocket)
    If $ConnectedSocket >= 0 Then
        MsgBox(4096, "", "my server - Client Connected")
        Exit
    EndIf
WEnd
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Вот тут я игрался с многоканальными подключениями. http://autoit-script.ru/index.php/topic,11436.0.html
 
Автор
S

S1R1US

скрипт мне запили!
Сообщения
50
Репутация
6
C2H5OH
Не то...мне нужна не куча подключений на один сокет, а на разные сокеты сервера, то есть к примеру у тебя данные идут по основному сокету(как я понял), мне нужно открыть сразу несколько сокетов(одновременно) и получать разные данные по разным каналам. Своего рода распределенка по типу передаваемой инфы.
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
А какая разница?
Вместо одного $MainSocket заводим массив и тогда

Код:
For $i = 0 To $PlayerNumber-1
    If $PlayersConnected[$i] = -1 Then
         $PlayersConnected[$i] = TCPAccept($MainSocket[$i]) ; если i-тый игрок не подключенн, то пробуем подключить
    Else
        $recv = TCPRecv($PlayersConnected[$i], 1024)    ; получаем данные от подключившегося игрока
        If @error Then $PlayersConnected[$i] = -1          ; если игрок оторвался, то обнуляем слот
        If $recv <> "" Then $data[$i] = $recv
    EndIf
Next
 
Автор
S

S1R1US

скрипт мне запили!
Сообщения
50
Репутация
6
C2H5OH
Просто у тебя подключение идёт по одному сокету, а значит все данные идут в 1 поток, а мне нужно чтобы этих потоков было например 2-3, и чтобы через 1-й шли только сообщения, через 2-й шли только бинарные данные, через 3-й всё остальное... Так проще работать с получаемой инфой. Хочу узнать как это сделать на AutoIT, от этого зависит дальнейшая судьба небольшой программы которую я ваяю)
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Я ж тебе предложижил решение. Заводишь несколько MainSocket-ов, каждый на свой порт, и работаешь с ними со всеми.
Код:
Local $g_IP = "127.0.0.1"

; Start The TCP Services
;==============================================
TCPStartup()

; Create a Listening "SOCKET"
;==============================================
Global $SocketNumber = 3
Global $Ports[$SocketNumber] = [65432, 65433, 65434]
Global $MainSocket[$SocketNumber]
Global $ConnectedSocket[$SocketNumber]
Global $data[$SocketNumber]

For $i = 0 To $SocketNumber-1
	$MainSocket[$i] = TCPListen($g_IP, $Ports[$i], 100)
	If $MainSocket[$i] = -1 Then Exit
	$ConnectedSocket[$i] = -1
	$data[$i] = ""
Next

While 1
	For $i = 0 To $SocketNumber-1
    If $ConnectedSocket[$i] = -1 Then
         $ConnectedSocket[$i] = TCPAccept($MainSocket[$i])
    Else
        $recv = TCPRecv($ConnectedSocket[$i], 1024)
        If @error Then $ConnectedSocket[$i] = -1
		If $recv <> "" Then $data[$i] = $recv
	EndIf
	ToolTip("Получено:"&@CR&"Socket1 - "&$data[0]&@CR&"Socket2 - "&$data[1]&@CR&"Socket3 - "&$data[2])
Next
Wend




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

Просто у тебя подключение идёт по одному сокету, а значит все данные идут в 1 поток

S1R1US,
ты не разобрался в скрипте. У меня данные не идут по одному сокету. Я просто открываю несколько сокетов на один порт.
Соответственно, от каждого подключившегося клиента идёт свой поток.
 
Автор
S

S1R1US

скрипт мне запили!
Сообщения
50
Репутация
6
Попробовал изменить свой "сервер мессенджер" для многоканального подключения и наткнулся на след. проблемы:
- отправка идёт только по 1 подключенному сокету;
- сервер шлёт 1 сообщение, а следом второе высылается пустым по следующему сокету(подозреваю, что нужно не очищать поле Input покамест не завершится последняя отправка);
- программма не закрывается кнопкой закрыть, значит я не туда поместил цикл For, из Вашего примера.
C2H5OH подскажите нубу)
Код:
#RequireAdmin
#cs ----------------------------------------------------------------------------

	AutoIt Version: 3.3.8.0
	Author:         S1R1US[B.S.]

	Script Function:
	MTRD Server.

#ce ----------------------------------------------------------------------------
;SERVER!! Start Me First !!!!!!!!!!!!!!!
#include <Array.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <ListBoxConstants.au3>
#include <EditConstants.au3>
#include <GuiEdit.au3>
Global $g_IP = @IPAddress1

; Start The TCP Services
;==============================================
TCPStartup()

; Create a Listening "SOCKET"
;==============================================
Global $SocketNumber = 3
Global $Ports[$SocketNumber] = [33891, 33892, 33893]
Global $MainSocket[$SocketNumber]
Global $ConnectedSocket[$SocketNumber]
Global $data[$SocketNumber]

For $i = 0 To $SocketNumber-1
    $MainSocket[$i] = TCPListen($g_IP, $Ports[$i], 100)
    If $MainSocket[$i] = -1 Then Exit
	$RogueSocket = $ConnectedSocket[$i]-1
    $data[$i] = ""
Next

; Create a GUI for chatting
;==============================================
$GOOEY = GUICreate("messenger Admin - " & @ComputerName, 670, 600, @DesktopWidth / 2 + 100, @DesktopHeight / 2 - 350)
$edit = GUICtrlCreateEdit("", 10, 10, 480, 400, $ES_AUTOVSCROLL + $WS_VSCROLL + $ES_NOHIDESEL + $ES_WANTRETURN + $ES_READONLY)
$input = GUICtrlCreateInput("", 10, 420, 480, 130)
$butt = GUICtrlCreateButton("ОТПРАВИТЬ", 10, 560, 80, 30)
$ochistka = GUICtrlCreateButton("ОЧИСТИТЬ", 90, 560, 80, 30)
$clientlist = GUICtrlCreateList("", 500, 10, 160, 580, $GUI_SS_DEFAULT_LIST + $LBS_NOINTEGRALHEIGHT) ; создаем элемент списка
GUICtrlSetData($clientlist, 'Администратор', 'Администратор')
GUICtrlSetLimit(-1, 200) ; ограничить прокрутку по горизонтали
GUISetState()

GUICtrlSetData($edit, "", 1); вставить начальную строку в поле Edit
GUICtrlSetData($input, "Status Online", 1); вставить начальную строку в поле Edit чата
GUICtrlSetState($butt, $GUI_DEFBUTTON); установка кнопки Отправить кнопкой по умолчанию

; GUI Message Loop
;==============================================
While 1
	$msg = GUIGetMsg()
    For $i = 0 To $SocketNumber-1
	; GUI Closed
	;--------------------
	If $msg = $GUI_EVENT_CLOSE Then ExitLoop

	; User Pressed SEND
	;--------------------
	If $msg = $butt Then
		If $ConnectedSocket[$i] > -1 Then
			$ret = TCPSend($ConnectedSocket[$i], ">>Администратор " & "(" & @HOUR & ":" & @MIN & ":" & @SEC & ")" & ": " & GUICtrlRead($input))
			If @error Then
				; ERROR OCCURRED, CLOSE SOCKET AND RESET ConnectedSocket to -1
				;----------------------------------------------------------------
				TCPCloseSocket($ConnectedSocket[$i])
				WinSetTitle($GOOEY, "", "MTRD Server " & @ComputerName)
				$ConnectedSocket[$i] = -1
			ElseIf $ret > 0 Then
				; UPDATE EDIT CONTROL WITH DATA WE SENT
				;----------------------------------------------------------------
				_GUICtrlEdit_AppendText($edit, ">>Администратор " & " (" & @HOUR & ":" & @MIN & ":" & @SEC & ")" & ": " & GUICtrlRead($input) & @CRLF)
			EndIf
		EndIf
		GUICtrlSetData($input, "")
	EndIf

	If $RogueSocket > 0 Then
		$recv = TCPRecv($RogueSocket, 2048)
		If Not @error Then
			TCPCloseSocket($RogueSocket)
			$RogueSocket[$i] = -1
		EndIf
	EndIf

	; If no connection look for one
	;--------------------
	If $ConnectedSocket[$i] = -1 Then
		$ConnectedSocket[$i] = TCPAccept($MainSocket[$i])
		If $ConnectedSocket[$i] >= 0 Then
			WinSetTitle($GOOEY, "", "Server Running - " & @ComputerName)
		EndIf

		; If connected try to read some data
		;--------------------
	Else
		; EXECUTE AN UNCONDITIONAL ACCEPT IN CASE ANOTHER CLIENT TRIES TO CONNECT
		;----------------------------------------------------------------
		$RogueSocket = TCPAccept($MainSocket[$i])
		If $RogueSocket > 0 Then
			TCPSend($RogueSocket, "~~rejected")
		EndIf
		$recv = TCPRecv($ConnectedSocket[$i], 1024)
		If $recv <> "" And $recv <> "~~bye" Then
			; UPDATE EDIT CONTROL WITH DATA WE RECEIVED
			;----------------------------------------------------------------
			_GUICtrlEdit_AppendText($edit, ">" & $recv & @CRLF)

		ElseIf @error Or $recv = "~~bye" Then
			; ERROR OCCURRED, CLOSE SOCKET AND RESET ConnectedSocket to -1
			;----------------------------------------------------------------
			WinSetTitle($GOOEY, "", "server Online " & @IPAddress1)
			TCPCloseSocket($ConnectedSocket[$i])
			$ConnectedSocket[$i] = -1
		EndIf
	EndIf
	Next
WEnd

GUIDelete($GOOEY)

Func OnAutoItExit()
	;ON SCRIPT EXIT close opened sockets and shutdown TCP service
	;----------------------------------------------------------------------
	If $ConnectedSocket[$i] > -1 Then
		TCPSend($ConnectedSocket[$i], "~~bye")
		Sleep(2000)
		TCPRecv($ConnectedSocket[$i], 512)
		TCPCloseSocket($ConnectedSocket[$i])
	EndIf
	TCPCloseSocket($MainSocket[$i])
	TCPShutdown()
EndFunc   ;==>OnAutoItExit

Func SOCKET2IP($SHOCKET)
	Local $sockaddr = DllStructCreate("short;ushort;uint;char[8]")

	$a = DllCall("Ws2_32.dll", "int", "getpeername", "int", $SHOCKET, "ptr", DllStructGetPtr($sockaddr), _
			"int_ptr", DllStructGetSize($sockaddr))
	If Not @error And $a[0] = 0 Then
		$a = DllCall("Ws2_32.dll", "str", "inet_ntoa", "int", DllStructGetData($sockaddr, 3))
		If Not @error Then $a = $a[0]
	Else
		$a = 0
	EndIf

	; release Struct not really needed as it is a local
	$sockaddr = 0

	Return $a
EndFunc   ;==>SOCKET2IP
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Я с GUI не того... Не дружу. :(
В своём примере я использую другой метод закрытия GUI.
Код:
Global $continue_battle = True  ; переменная-флаг

HotKeySet("{F10}", "stop_Battle")

Func stop_Battle()
    $continue_battle = False
EndFunc

; . . .
Local $BattleGUI = GUICreate("Battle", 700, 505)
GUICtrlCreateLabel(" F10 - Stop", 570, 450, 70, 20, 1)

While $continue_battle
; ...
WEnd
GUIDelete($BattleGUI)

Насчет отправки сообщений.
Я не понимаю что ты делаешь. Попробуй хотя бы описать что нужно сделать.
Ты всё время закрываешь и открываешь сокеты. Зачем?
Что это за такое вольное обращение с необъявленной переменной неизвестного типа?
Код:
If $RogueSocket > 0 Then
        $recv = TCPRecv($RogueSocket, 2048)
        If Not @error Then
            TCPCloseSocket($RogueSocket)
            $RogueSocket[$i] = -1
        EndIf
    EndIf



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

Хотя нет, я вот нормально обрабатываю GUIGetMsg и рассылаю пакеты по TCP/IP
Код:
While 1
    $msg = GUIGetMsg()
    Switch $Msg
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $Button1
            _BATTLE()
            RESET_Players()
            GUISwitch ($ServerGUI)
        Case $Button2
            RESET_Players()
        Case Else
            For $i = 0 To $PlayerNumber-1
                If $PlayersConnected[$i] = -1 Then
                    $PlayersConnected[$i] = TCPAccept($MainSocket) ; если i-тый игрок не подключенн, то пробуем подключить
                    ;ExitLoop   ; <- если включить ExitLoop
                                ; то игроки будут подключаться по порядку начиная с 1-го сокета
                                ; но при обрыве игрока его слот будет уже недоступен
                Else
                    If $PlayersIP[$i] == "" Then    ; если по игроку ещё нет статистики, то получаем её
                        $PlayersIP[$i] = SocketToIP($PlayersConnected[$i])
                        GUICtrlSetData($PlayerStats[$i],$PlayersIP[$i])
                    EndIf
                    $recv = TCPRecv($PlayersConnected[$i], 1024)    ; получаем данные от подключившегося игрока
                    If @error Then
                        $PlayersConnected[$i] = -1          ; если игрок оторвался, то обнуляем слот
                        GUICtrlSetData($PlayerNames[$i],"пусто")
                        GUICtrlSetData($PlayerStats[$i],"")
                    EndIf
                        If $recv <> "" Then GUICtrlSetData($PlayerNames[$i],$recv)
                EndIf
            Next
    EndSwitch
WEnd
 
Автор
S

S1R1US

скрипт мне запили!
Сообщения
50
Репутация
6
Почему необъявленной? Вот (см начало кода):
Код:
$RogueSocket = $ConnectedSocket[$i]-1

Закрываются они только если происходит ошибка
Код:
@error
насколько я понимаю.
Сделал по примеру из справки, серверную часть, на которую идут сообщения. Идея заключалась в том чтобы создать мессенджер, затем файлообменник + администратор кит(выполнение стандартных операций).
Я не пойму почему он шлёт не всем сразу...
Если с этим такая большая проблема, думаю можно реализовать функцию путём создания на сервере файла log.txt куда будут вписываться данные при отправке, и соответственно чтение из этого файла будет осуществляться клиентами... Очень жаль что не получилось реализовать отправку...на самом деле...
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Потому что
Код:
For $i = 0 To $SocketNumber-1


нужно поставить после
Код:
If $msg = $butt Then


А к $RogueSocket ты пересмотри своё отношение...
 
Автор
S

S1R1US

скрипт мне запили!
Сообщения
50
Репутация
6
C2H5OH
Код:
While 1
	$msg = GUIGetMsg()
    ; GUI Closed
	;--------------------
	If $msg = $GUI_EVENT_CLOSE Then ExitLoop
	; User Pressed SEND
	;--------------------
	If $msg = $butt Then
		For $i = 0 To $SocketNumber-1 ; вставил сюда цикл с Вашего примера
		If $ConnectedSocket[$i] > -1 Then
			$ret = TCPSend($ConnectedSocket[$i], ">>Администратор " & "(" & @HOUR & ":" & @MIN & ":" & @SEC & ")" & ": " & GUICtrlRead($input))
			If @error Then
				; ERROR OCCURRED, CLOSE SOCKET AND RESET ConnectedSocket to -1
				;----------------------------------------------------------------
				TCPCloseSocket($ConnectedSocket[$i])
				WinSetTitle($GOOEY, "", "Server " & @ComputerName)
				$ConnectedSocket[$i] = -1
			ElseIf $ret > 0 Then
				; UPDATE EDIT CONTROL WITH DATA WE SENT
				;----------------------------------------------------------------
				_GUICtrlEdit_AppendText($edit, ">>Администратор " & " (" & @HOUR & ":" & @MIN & ":" & @SEC & ")" & ": " & GUICtrlRead($input) & @CRLF)
			EndIf
		EndIf
		Next
		GUICtrlSetData($input, "")
	EndIf

	If $RogueSocket > 0 Then
		$recv = TCPRecv($RogueSocket, 2048)
		If Not @error Then
			TCPCloseSocket($RogueSocket)
			$RogueSocket[$i] = -1
		EndIf
	EndIf

	; If no connection look for one
	;--------------------
	For $i = 0 To $SocketNumber-1 ; вставил сюда цикл с Вашего примера
	If $ConnectedSocket[$i] = -1 Then
		$ConnectedSocket[$i] = TCPAccept($MainSocket[$i])
		If $ConnectedSocket[$i] >= 0 Then
			WinSetTitle($GOOEY, "", "Server Running - " & @ComputerName)
		EndIf

		; If connected try to read some data
		;--------------------
	Else
		; EXECUTE AN UNCONDITIONAL ACCEPT IN CASE ANOTHER CLIENT TRIES TO CONNECT
		;----------------------------------------------------------------
		$RogueSocket = TCPAccept($MainSocket[$i])
		If $RogueSocket > 0 Then
			TCPSend($RogueSocket, "~~rejected")
		EndIf
		$recv = TCPRecv($ConnectedSocket[$i], 1024)
		If $recv <> "" And $recv <> "~~bye" Then
			; UPDATE EDIT CONTROL WITH DATA WE RECEIVED
			;----------------------------------------------------------------
			_GUICtrlEdit_AppendText($edit, ">" & $recv & @CRLF)

		ElseIf @error Or $recv = "~~bye" Then
			; ERROR OCCURRED, CLOSE SOCKET AND RESET ConnectedSocket to -1
			;----------------------------------------------------------------
			WinSetTitle($GOOEY, "", "server Online " & @IPAddress1)
			TCPCloseSocket($ConnectedSocket[$i])
			$ConnectedSocket[$i] = -1
		EndIf
	EndIf
	Next
WEnd
Вроде бы всем отправляется теперь, но хочу понять, что нужно пересмотреть в куске кода с $RogueSocket, в какую сторону копать, декларировать его глобальных константах или что то иное?
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Ты используешь $RogueSocket и как переменную, и как массив.
Не понимаю почему AutoIt такое пропускает и не выдаёт сообщение об ошибке.


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

Я понял.
У тебя $RogueSocket всегда равно -1
И до того места где ты используешь её как массив интерпретатор просто не доходит
Код:
If $RogueSocket > 0 Then
        $recv = TCPRecv($RogueSocket, 2048)
        If Not @error Then
            TCPCloseSocket($RogueSocket)
            $RogueSocket[$i] = -1
        EndIf
    EndIf
 
Автор
S

S1R1US

скрипт мне запили!
Сообщения
50
Репутация
6
C2H5OH
Исправил
Код:
$RogueSocket[$i] = -1
на
Код:
$RogueSocket = -1

Можно ли тем же путём, что я открыл массив сокетов на сервере, поднять на каждом клиенте по 1 дополнительному сокету для прослушивания и пересылать на другие клиенты получаемые пакеты от одного клиента? Или лучше будет реализовать эту функцию как то иначе - например, с помощью рассылки в предварительно сформированный массив подключенных пользователей?
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Сокет двунаправленый. Если не запутаешься с порядком отправки-получения пакетов, то можно в тот же сокет и писать. Можно и второй сокет открыть - один для чтения, один для отправки (хотя запутаться с порядком отправки-получения те же шансы...)
У тебя и так всё избыточно. Но если ресурсов не жалко, то можно и под каждый пакет отдельный сокет открывать.

Я не понимаю что у тебя в скрипте вообще делает переменная
Код:
$RogueSocket

Она ж ВСЕГДА равна -1
 
Автор
S

S1R1US

скрипт мне запили!
Сообщения
50
Репутация
6
C2H5OH
Переменная присутствовала в сервере из справки :smile: поэтому она и в моём скрипте есть. Хотя вот если убрать кусок кода и правда ничего не меняется...
Я вот как вижу решение: мне отправляют по главному сокету, а я пересылаю всем по дополнительному. Пересылку то я думаю несложно реализовать, а вот открыть дополнительный сокет на каждом и затем подключить туда сервер ко всем сразу...


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

Я кажется разобрался, переменная ведь слушает подключения к главному сокету.


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

Вообщем не стал изобретать велосипед, но всё же добавил логирование + организовал полную пересылку получаемых пакетов(сообщений) всем подключенным клиентам.

Код:
If $ConnectedSocket[$i] = -1 Then
		$ConnectedSocket[$i] = TCPAccept($MainSocket[$i])
		If $ConnectedSocket[$i] >= 0 Then
			WinSetTitle($GOOEY, "", "Server Running - " & @ComputerName)
		EndIf

		; If connected try to read some data
		;--------------------
	Else
		; EXECUTE AN UNCONDITIONAL ACCEPT IN CASE ANOTHER CLIENT TRIES TO CONNECT
		;----------------------------------------------------------------
		$RogueSocket = TCPAccept($MainSocket[$i])
		If $RogueSocket > 0 Then
			TCPSend($RogueSocket, "~~rejected")
		EndIf
		$recv = TCPRecv($ConnectedSocket[$i], 1024)
		If $recv <> "" And $recv <> "~~bye" Then
			; UPDATE EDIT CONTROL WITH DATA WE RECEIVED
			;----------------------------------------------------------------
			_GUICtrlEdit_AppendText($edit, ">" & $recv & @CRLF)
			$hFile = FileOpen(@ScriptDir & "\send.log", 1) ; Проверяет, является ли файл открытым, перед тем как использовать функции чтения/записи в файл
			If $hFile = -1 Then
			Sleep(1000)
			FileOpen(@ScriptDir & "\send.log")
			FileWrite(@ScriptDir & "\send.log", $recv & @CRLF) ; логируем получаемые сообщения
			FileClose($hFile)
			EndIf
			FileWrite(@ScriptDir & "\send.log", $recv & @CRLF)
			FileClose($hFile)
			sleep(100)
			For $i = 0 To $SocketNumber-1 ; Пересылка получаемых пакетов всем подключенным клиентам
				If $ConnectedSocket[$i] > -1 Then
					TCPSend($ConnectedSocket[$i], $recv)
				EndIf
			Next
		ElseIf @error Or $recv = "~~bye" Then
			; ERROR OCCURRED, CLOSE SOCKET AND RESET ConnectedSocket to -1
			;----------------------------------------------------------------
			WinSetTitle($GOOEY, "", "Server Online " & @IPAddress1)
			TCPCloseSocket($ConnectedSocket[$i])
			$ConnectedSocket[$i] = -1
		EndIf
	EndIf
OffTopic:
P.S. Большое человеческое спасибо C2H5OH за немалый вклад и наставления :beer:. AutoIT the Best! :IL_AutoIt_1:
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Та не очень велосипед у тебя получился, грубо сработано как-то...
Если на стиль можно закрыть глаза (сам скрипт ведь никто не оценивает, смотрят как работает программа), то вот то, что ты под каждого клиента держишь отдельный порт - это уже проблема, на мой взгляд.
Я сначала подумал что ты собираешься через разные порты гонять данные разного типа. Идея мне понравилась, я и влез в тему. А оказалось что ты просто разобраться с клиентами не можешь...
У тебя всё как-то притянуто за уши. Пременные водятся, назначение которых ты сам не понимаешь. Куда ты торопишься? Почитай справку, разбери примеры, а потом уже начинай что-то ваять.
 
Верх