gpt4 book ai didi

python - Python套接字/端口转发

转载 作者:行者123 更新时间:2023-12-03 03:05:09 25 4
gpt4 key购买 nike

我已经用python编写了服务器和客户端程序

Server.py

 import socket

sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)

host = socket.gethostname()
port = 5555

sock.bind((host, port))

sock.listen(1)

conn, addr = sock.accept()

data = "Hello!"
data = bytes(data, 'utf-8')

conn.send(data)

sock.close()

Linux上的Client.py
import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

host = socket.gethostname()
port = 5555

sock.connect((host, port))

data = sock.recv(2048)

data = str(data, "utf-8")

print(data)

sock.close()

当我在本地计算机(Linux Mint)上运行服务器和客户端时,它可以正常工作。我收到“你好!”在bash中,一切都很好。但是,当我将客户端程序下载到另一台计算机(Windows 8)并运行它时(当然,以前我在Linux上运行服务器,并将客户端中的IP地址更改为我的静态Linux Mint的IP)
ConnectionRefusedError:[WinError 10061]无法建立连接,因为目标计算机主动拒绝了它
Windows上的client.py
  import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

host = "here is my static ip"
port = 5555

sock.connect((host, port))

data = sock.recv(2048)

data = str(data, "utf-8")

print(data)

sock.close()

我必须说,我已经在端口5555的路由器设置中完成了端口转发。之前我对端口80做过同样的事情,并且我自己的站点正常工作,但是现在在使用python套接字的5555中它不起作用!为什么?我听不懂!还有一件事:我试图将服务器和客户端文件中的端口更改为80,但是它也无法正常工作。请帮忙。

最佳答案

您必须将服务器脚本中的socket.gethostname()更改为空字符串(或直接调用socket.bind(('', port)))。

您的问题不是在python中,而是在套接字的使用方面。创建套接字时,您只需准备过程即可从另一个进程接收/发送一些数据。

服务器

创建套接字的第一步,必须指定在这些进程之间进行通信时将使用哪种协议(protocol)。在您的情况下,socket.AF_INET对于IP协议(protocol)的使用是恒定的,并且socket.SOCK_STREAM是指定可靠的面向流的服务。可靠的面向流的服务意味着您要确保每个发送的字节都将传递到另一端,并且在通信过程中不会丢失任何内容(底层OS会为此使用TCP协议(protocol))。从这一点来看,我们正在使用IPv4协议(protocol)(因为我们设置了socket.AF_INET)

第二步是bind它来解决。 bind进程分配您希望客户端加入的地址(使用套接字的设置,它是IP地址和TCP端口)。您的PC有多个IP地址(至少两个)。它始终具有127.0.0.1,称为回调,并且仅当您的应用程序在同一台PC上进行通信(即您的Linux-Linux场景)并且您具有用于与其他计算机进行通信的IP时才起作用(假设它是10.0.0.1)。

当您调用socket.bind(('127.0.0.1', 5555))时,您正在将套接字设置为仅监听来自同一台PC的通信。如果调用socket.bind(('10.0.0.1', 5555)),则套接字设置已准备就绪,可以接收针对10.0.0.1地址的数据。

但是,如果您有10个IP或更多,并且想要接收所有内容(具有正确的TCP端口),该怎么办。对于这些情况,您可以将bind()中的IP地址保留为空,并且它确实可以满足您的要求。

使用Python的bind()版本,您还可以输入“计算机名称”而不是具体的IP。 socket.gethostname()调用返回您的计算机名称。问题在于将“计算机名称”转换为Python支持的IP。转换有一些规则,但是通常您的“计算机名”可以转换为您在计算机上设置的任何IP地址。在您的情况下,您的计算机名称将转换为127.0.0.1,这就是为什么通信仅在同一计算机上的进程之间起作用的原因。

socket.bind()之后,您可以使用套接字了,但是它仍然是“不 Activity 的”。 socket.listen()激活套接字,并在有人要连接时等待。当套接字收到新的连接请求时,它将进入队列并等待处理。

那就是socket.accept()所做的。它从队列中拉出连接请求,接受连接请求,并在服务器和客户端之间建立流(在设置套接字时记住socket.SOCK_STREAM)。新的流实际上是新的套接字,但已准备好与另一端进行通信。

旧 socket 发生了什么?好了,它仍然存在,您可以再次调用socket.listen()以获取另一个流(连接)。

同一端口上如何有多个 socket

计算机网络内的每个连接均由以下五元组的流定义:

  • L4协议(protocol)(通常为TCP或UDP)
  • 源IP地址
  • 源L4端口
  • 目标IP地址
  • 目标L4端口

  • 当您从客户端创建新连接时,流程看起来像这个 (TCP, 192.168.0.1, 12345, 10.0.0.1, 55555)。只是为了澄清,服务器的响应流是 (TCP, 10.0.0.1, 55555, 192.168.0.1, 12345),但这对我们并不重要。如果您从客户端创建另一个连接,则它在源TCP端口上将有所不同(如果从另一台计算机进行连接,则它在源IP上也将有所不同)。只有通过此信息,您才能区分与计算机建立的每个连接。

    当您在代码中创建服务器套接字并调用 socket.listen()时,它将监听此模式 (TCP, *, *, *, 55555)的任何流(*表示匹配所有内容)。因此,当您与 (TCP, 192.168.0.1, 12345, 10.0.0.1, 55555)建立连接时,然后 socket.accept()创建另一个套接字,该套接字仅与此具体流程一起使用,而旧套接字仍接受未建立的新连接。

    当操作系统收到数据包时,它将查找该数据包并检查流。从这一点来看,它可能会发生以下几种情况:
  • 数据包的流与所有5个项目完全匹配(不使用*)。然后,数据包的内容将传递到与该套接字关联的队列(您在调用socket.recv()时正在读取队列)。
  • 数据包的流匹配套接字以及关联的流包含*,然后将其视为新连接,您可以调用scoket.accept()
  • 操作系统不包含与流程匹配的开放套接字。在这种情况下,操作系统会拒绝连接(或者只是忽略数据包,这取决于防火墙设置)。

  • 可能有一些例子可以阐明这些情况。操作系统具有类似于table之类的内容,它将流映射到套接字。当您调用 socket.bind()时,它将把流分配给套接字。调用后,该表如下所示:
    +=====================================+========+
    | Flow | Socket |
    +=====================================+========+
    | (TCP, *, *, *, 55555) | 1 |
    +-------------------------------------+--------+

    当它接收到带有流 (TCP, 1.1.1.1, 10, 10.0.0.1, 10)的数据包时,它将不匹配任何流(最后一个端口将不匹配)。因此,连接被拒绝。如果接收到带有流 (TCP, 1.1.1.1, 10, 10.0.0.1, 55555)的数据包,则该数据包将被传递到套接字 1(因为存在匹配项)。 socket.accept()调用创建一个新的套接字并记录在表中。
    +=====================================+========+
    | Flow | Socket |
    +=====================================+========+
    | (TCP, 1.1.1.1, 10, 10.0.0.1, 55555) | 2 |
    +-------------------------------------+--------+
    | (TCP, *, *, *, 55555) | 1 |
    +-------------------------------------+--------+

    现在,您有2个用于1个端口的 socket 。与套接字 2关联的流匹配的每个接收到的数据包也与套接字 1关联的流匹配(相反,它不适用)。这不是问题,因为套接字 2具有更精确的匹配(不使用 *),因此具有该流的所有数据都将传递到套接字 2

    如何处理多个连接

    当您想做一个“真正的”服务器时,您的应用程序应该能够处理多个连接(无需重新启动)。有两种基本方法:
  • 顺序处理
    try:
    l = prepare_socket()
    while True:
    l.listen()
    s, a = socket.accept()
    process_connection(s) # before return you should call s.close()
    except KeyboardInterrupt:
    l.close()

    在这种情况下,您只能处理一个客户端,而其他客户端则必须等待接受。如果process_connection()花费的时间太长,则其他客户端将超时。
  • 并行处理
    import threading
    threads = []

    try:
    l = prepare_socket()
    while True:
    l.listen()
    s, a = socket.accept()
    t = threading.Thread(target=process_connection, s)
    threads.append(t)
    t.start()
    except KeyboardInterrupt:
    for t in threads:
    t.join()
    l.close()

    现在,当您收到新的连接时,它将创建新的线程,因此每个连接都将并行处理。该解决方案的主要缺点是必须解决线程的常见问题(例如访问共享内存,死锁等)。

  • 当心那些仅仅是示例代码,它们并不完整!例如,它不包含用于在意外异常时正常退出的代码。

    Python中的服务器

    Python还包含名为 socketserver 的模块,该模块包含创建服务器的快捷方式。 Here您可以找到示例如何使用它。

    客户

    使用客户端比使用服务器要简单得多。您只需要创建具有一些设置的套接字(与服务器端相同),然后告诉它服务器在哪里(它的IP和TCP端口是什么)即可。这是通过 socket.connect()调用来完成的。作为奖励,它还会在客户端和服务器之间建立流,因此从这一点开始您就可以进行通信了。

    您可以在 Beej's Guide to Network Programming上找到有关 socks 的更多信息。它是为与C结合使用而编写的,但是概念是相同的。

    关于python - Python套接字/端口转发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45097727/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com