Я пытаюсь сделать несколько циклов, но почему-то они не работают. Я уже искал в Интернете, но ни одна из проблем, которые я нашел, не имеет такой же проблемы.
Итак, вот код, содержащий только необходимую информацию. Я в основном открываю сокет, даю ввод (i\n) и получаю вывод на первом этапе. Я хочу продолжать получать вывод до тех пор, пока на выходе не появятся определенные символы xxx. Затем я хочу перейти к оператору elif в следующем цикле.
def netcat(h, p):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((h,p))
i = 0
a = True
while a == True:
socket_list=[sys.stdin, s]
r,w,x = select.select(socket_list, [], [])
if i==0:
time.sleep(1)
message = s.recv(1024)
print(message)
s.send("i\n")
sys.stdout.flush()
while "xxx" not in message:
message = s.recv(1024)
print(message)
i+=1
elif i==1:
print("+++++++++++++++++++++++++++")
i+=1
print("hello")
server.close()
Я ожидаю, что код будет печатать сообщение из оператора if, затем печатать Привет, затем сообщение из оператора elif, а затем Привет снова и снова, потому что цикл while все еще активен. Таким образом, это ожидаемый результат:
message
hello
+++++++++++++++++++++++++++
hello
hello
hello
hello...
Что он действительно печатает, так это
message
hello
и тогда это заканчивается.
Я узнал, что если я прокомментирую следующие строки:
while "xxx" not in message:
message = s.recv(1024)
print(message)
он работает так, как ожидалось. Привет в конце кода снова и снова выводится на экран. Я просто не понимаю, почему этот второй цикл while имеет к этому какое-то отношение. Я был бы очень признателен за помощь здесь.
Так как был запрошен рабочий код, вот и полный код. Имя хоста и порт взяты из CTF, который все еще работает, поэтому вы будете взаимодействовать с CTF-сервером:
#!/usr/bin/env python
import socket
import time
import select
import sys
base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ = "
hostname = "18.188.70.152"
port = 36150
def netcat(h, p):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((h,p))
i = 0
a = True
b = True
while a == True:
socket_list=[sys.stdin, s]
r,w,x = select.select(socket_list, [], [])
if i==0:
time.sleep(1)
message = s.recv(1024)
print(message)
s.send("i\n")
sys.stdout.flush()
while "flag" not in message:
message = s.recv(1024)
print(message)
txtfile = message[9:38]
print(txtfile)
i+=1
elif i==1:
print("+++++++++++++++++++++++++++")
i+=1
print("hello")
server.close()
netcat(hostname, port)
Пожалуйста, выложите рабочий код. В вашем коде есть несколько проблем...
Кроме того, server
нижняя часть вашего кода не определена.
Я бы проверил ваш отступ, попытался установить и запустить autopep8 в вашем коде и посмотреть, решит ли это какие-либо из ваших проблем.
[править] пользователь обновил свой вопрос, и стало ясно, что это не ответ.
Вы смешиваете код на основе событий (select.select()
) с блокирующим синхронным кодом (ваш маленький цикл while
с s.recv()
).
Если вы хотите, чтобы ваш код не блокировался, каждый recv()
должен быть соединен с предшествующим select()
.
Не только это, но вы также должны проверить возвращаемые значения из select()
. Только s.recv()
, если s
было в первом возвращаемом списке. Если вы s.recv()
в любом другом случае, код также заблокируется при входящем звонке.
Обновлять:
Попробуйте что-нибудь вроде:
not_done = True
while not_done:
read_sockets, _, _ = select.select([sys.stdin, s], [], [])
if s in read_sockets:
message = s.recv(1024)
print(message)
... more code ...
if 'flag' in message:
... react on flag ...
if 'quit' in message:
not_done = False
... processing of other sockets or file descriptors ...
Важным моментом является то, что в ветке if есть только этот одинs.recv()
, который проверяет, было ли получено что-то обнаруженным select.
Внешний while
просто вернется в ту же if
ветку позже, когда будут получены дополнительные данные.
Обратите внимание, что обработка stdin
вместе с кодом сокета сложна и, вероятно, также будет заблокирована в какой-то момент. Скорее всего, вам придется сначала перевести терминал в необработанный режим или что-то в этом роде, а затем быть готовым обрабатывать частичные строки самостоятельно, а также, возможно, также повторять ввод обратно пользователю.
Обновлять:
Если вы хотите что-то сделать, пока сообщение не было получено, вы можете дать тайм-аут select()
, а затем выполнить другую обработку, если на сокете ничего не было получено. Что-то вроде этого:
say_hello_from_now_on = False
not_done = True
while not_done:
read_sockets, _, _ = select.select([s], [], [], 1)
if s in read_sockets:
message = s.recv(1024)
print(message)
say_hello_from_now_on = True
elif say_hello_from_now_on:
print("hello")
Эй, спасибо за быстрый ответ. Не могли бы вы привести пример того, как select нужно сочетать с recv()?
@Loeli, попробуйте что-то вроде плана, которым я обновил ответ.
Я попробовал это сейчас с вашим предложением и изменил цикл while, чтобы он содержал только один recv, но он все еще не работает. Если бы мне не понадобился стандартный ввод, как я мог бы улучшить код, чтобы он работал? Извините за все эти вопросы, но я немного новичок в этом.
Итак, вы хотите, чтобы он постоянно печатал приветствие, когда не было получено никакого сообщения? Вы можете добиться этого, используя вызов select()
с тайм-аутом. Если read_sockets
не содержит s
, просто выведите hello
. Или вы хотите, чтобы приветствие выводилось только после получения первого сообщения?
@Loeli Я также добавил использование двух других списков из звонка select()
, который я пропустил раньше, извините за это. С этой моей ошибкой код никогда бы не попал в recv
ветку if
.
«Привет» было просто там, чтобы я мог проверить, приостановлен ли цикл while или нет. То, что я хочу сделать, это распечатать полученные данные на экране, затем отправить серверу сообщение «i\n», снова распечатать получение на экране, затем отправить серверу «n\n», распечатать на экране и т.д. . Что я сделал сейчас, так это вставил s.shutdown(socket.SHUT_WR)
после внутреннего цикла while, и это сработало. Но я не уверен, что это правильный способ сделать это.
Правильный способ - не использовать внутренний цикл while. Вы просто полагаетесь на код, постоянно возвращающийся к вызову select, и если он получает 1 элемент, обрабатывайте только этот 1 элемент. А для более сложной обработки вы сохраняете состояние вместе с переменными, которые сообщают вам, как реагировать дальше.
Теперь у меня все работает, большое спасибо! Еще один вопрос, чтобы немного прояснить ситуацию: - функция recv(1024) остановит все, пока не получит 1024 бит? -что делает функция выбора и что происходит с таймером?
Нет, recv(1024)
не будет ждать 1024 байта. Он получит все, что находится в приемном буфере, но только вплоть до 1024 байта. Если данные поступают с достаточно высокой скоростью, recv(1024)
вернет только первые 1024 и оставит оставшиеся данные в приемном буфере для возврата вызова следующийrecv()
(т. е. они не будут потеряны, а только разделятся).
select.select()
должным образом ожидает, пока что-то произойдет со всеми файловыми дескрипторами и сокетами, которые вы ему предоставите. Это особенно важно, если у вас их несколько одновременно, потому что вы не знаете заранее, на каком из них что-то произойдет дальше. Но это также важно, если вы хотите параллельно выполнять другую периодическую работу, для чего и нужен тайм-аут. Без тайм-аута select()
будет ждать, пока что-то не произойдет, возможно, бесконечно. С тайм-аутом он установит ограничение на то, как долго он будет ждать, даже если не было активности на сокетах или файловых дескрипторах.
Еще одна информация: при запуске кода в терминале кажется, что цикл while все еще открыт, поскольку терминал показывает только пустое место в конце, но не показывает обычную строку username@machine:folders (в Linux).