select.select() 不接受我的套接字并返回空列表

问题描述 投票:0回答:1

我希望为既充当服务器又充当客户端的东西编写一个脚本。对于服务器部分,我决定创建一个 SSL 套接字

accept()
循环,它将永远侦听新连接,并使其成为非阻塞,以便客户端部分可以工作,我决定使用
select

这是脚本:

from socket import (socket, 
                    AF_INET, 
                    SOCK_STREAM, 
                    create_connection, 
                    SOL_SOCKET, 
                    SO_REUSEADDR)
from ssl import (SSLContext, 
                 PROTOCOL_TLS_SERVER, 
                 PROTOCOL_TLS_CLIENT)


import select

import threading

import time
from tqdm.auto import tqdm


            

def handle_client(client, address):
    request_bytes = b"" + client.recv(1024)

    if not request_bytes:
        print("Connection closed")
        client.close()
    request_str = request_bytes.decode()
    print(f"we've received {request_str}")



ip = '127.0.0.1'
port = 8443
server_context = SSLContext(PROTOCOL_TLS_SERVER)
server_context.load_cert_chain('cert_ex1.pem', 'key_ex1.pem')


initial_counter = 1


server_socket = socket(AF_INET, SOCK_STREAM)
server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server_socket.bind((ip, port))
server_socket.listen(5)



print("forming lists for select...")
inputs = [server_socket]
print(f"we now have list inputs: {inputs}")
outputs = []

while True:
    print(f"checking: inputs still has a single socket in it: {type(inputs[0])}")
    readable, writable, exceptional = select.select(inputs, outputs, inputs, 1)
    print(f"readable is {readable}, \n writable is {writable}, \n exceptional is {exceptional}")
    for s in tqdm(readable):
        if s is server_socket:
            print("wrapping server socket in SSL...")
            with server_context.wrap_socket(server, server_side=True) as tls:
                connection, client_address = tls.accept()
                print("making the connection non-blocking...")
                connection.setblocking(0)
                inputs.append(connection)
                print("starting a thread that'd handle the messages...")
                threading.Thread(target=handle_client, args=(connection, client_address)).start()                
            
        else:
            print(f"dealing with socket {s}")

        
hostname='example.org'
client_context = SSLContext(PROTOCOL_TLS_CLIENT)
client_context.load_verify_locations('cert_ex1.pem')


with create_connection((ip, port)) as client:
    with client_context.wrap_socket(client, server_hostname=hostname) as tls:
        print(f'Using {tls.version()}\n')
        print("client is sending data...")
        tls.sendall(int.to_bytes(initial_counter, 4, 'little'))

        while True:
            data = tls.recv(1024*8)
            if not data:
                print("Client received no data")
                break
            
            new_data = int.from_bytes(data, 'little')
            print(f'Server says: {new_data}')
            new_data = int.to_bytes(new_data+1, 4, 'little')
            print("sleeping for 0.15...")
            time.sleep(0.15)
            tls.sendall(new_data)

运行此脚本的问题是,它正确创建了套接字,将仅包含此套接字的列表传递给

select.select()
,但随后
select()
返回 3 个空列表。

  1. 这有什么原因吗?
  2. 我的代码还有其他问题吗(总体思路是,尝试使用 SSL 来实现此功能,使用
    connection.setblocking(0)
    ,或者其他任何东西)?
python sockets ssl select
1个回答
0
投票

这是在服务器端使用非阻塞套接字的示例 - 客户端仍然生成到不同的线程中,但这是在“空闲回调”中完成的,所以现在您可以看到您可以接受客户端和客户端数据,并且仍然偶尔会这样做同一程序中的其他工作。

我跳过了

ssl
位,因为我手头没有您的证书,但请务必只将两个套接字都包装一次...

import random
import select
import socket
import threading
import time

hostname = "example.org"
ip = "127.0.0.1"
port = 8443
client_counter = 0


def server(idle_callback):
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((ip, port))
    server_socket.listen(5)
    # server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    # server_socket = server_context.wrap_socket(server_socket, server_side=True)

    clients = []

    while True:
        sockets = [server_socket, *clients]
        readable, writable, exceptional = select.select(sockets, [], sockets, 0.1)
        for s in readable:
            if s is server_socket:
                connection, client_address = server_socket.accept()
                connection.setblocking(False)
                clients.append(connection)
                print(f"new connection from {client_address}")
            else:  # must be a client socket
                try:
                    msg = s.recv(1024)
                    print(f"{s}: received {msg}")
                    if msg.startswith(b"The time is"):
                        s.sendall(b"The eagle flies at midnight...\n")
                    else:
                        s.sendall(f"Sorry, I don't understand {msg}\n".encode())
                except ConnectionError as exc:
                    print(f"{s}: {exc}")
                    s.close()
                    clients.remove(s)
                    continue
        for x in exceptional:
            print(f"exceptional condition on {x}")
            x.close()
            clients.remove(x)
        idle_callback()


def client():
    global client_counter
    client_counter += 1
    client_id = client_counter
    print(f"[{client_id}] Starting client...")

    with socket.create_connection((ip, port)) as client:
        # client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
        # with client_context.wrap_socket(client, server_hostname=hostname) as client:
        #     print(f'Using {client.version()}\n')

        while True:
            if random.random() < 0.1:
                print(f"[{client_id}] Time to go...")
                break

            if random.random() < 0.5:
                client.sendall(f"The time is {time.asctime()}\n".encode("utf-8"))
            else:
                client.sendall(b"Hello, server\n")

            data = client.recv(1024 * 8)
            if not data:
                print(f"[{client_id}] Client received no data")
                break
            print(f"[{client_id}] Server says: {data}")
            time.sleep(0.5)


def idle_callback():
    if random.random() < 0.1:
        threading.Thread(target=client).start()


def main():
    server(idle_callback)


if __name__ == "__main__":
    main()
© www.soinside.com 2019 - 2024. All rights reserved.