【问题标题】:Python Server-Client Lottery IssuePython 服务器-客户端彩票问题
【发布时间】:2019-09-26 08:07:48
【问题描述】:

我对我正在做的一项 python 作业有疑问,想向社区寻求一些指导。我将仅将 socket 模块argparse 模块用于此任务。我创建了一个 socketserver.py 文件和一个 socketclient.py 文件。服务器和客户端之间的连接很好。现在分配的目的是客户端使用 argparse 发送彩票游戏类型、票数和每票的行数。例如。 socketclient.py 的语法是 python3 -t Lotto_Max -n 2 -l 2。票务游戏的输出、票务类型和每张票的行数正确显示在服务器上。但是,它们有时无法在客户端上正确显示,并且目前确实卡住了。这是我的以下代码....

服务器代码

```socketserver.py code```

import socket
from random import sample

def main():
    host = input("Please specify an IP address for this server's socket\t")
    port = int(input('Please speciy a port number above 1024 for this socket.\t'))

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

    try:
        kulmiye.bind((host, port))
    except socket.error as egeh:
        print('Socket bind failed. Error Code : ' + str(egeh[0]) + ' Message ' + egeh[1])

    print('Socket bind accomplished...\n')

    print("Listening for an incoming connection...")

    kulmiye.listen()
    conn, addr = kulmiye.accept()

    print('Connected with ' + addr[0] + ':' + str(addr[1]))

    while True:
        server_data = conn.recv(1024).decode("utf-8")
        game_data = server_data.split(",")
        if not server_data:
            break
        if game_data[0] == "Lotto_Max":
            nval = int(game_data[1])
            lval = int(game_data[2])
            for nval in range(nval):
                for i in range(lval):
                    numbers = sample(range(1, 50), 7)
                    numbers.sort()
                    sortedd = str(numbers)
                    print(sortedd)
                print("--------------------")
                conn.sendall(sortedd.encode("utf-8"))
                #conn.sendall(bytes(str(numbers),'utf-8'))
                liners = "-----------------------"
                conn.sendall(liners.encode("utf-8"))
            print("From Client: " + str(game_data))
            conn.sendall(b'goodbye')
    #        server_data = input('#\t')
            break
        else:
            conn.close()

if __name__ == '__main__':
    main()

客户代码

```socketclient.py code```
import socket
import argparse

def client():
    host = input("Please specify the server's IP you want to connect to\t")
    port = int(input("Please specify the server's port you want to connect to\t"))

    kulmiye = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    kulmiye.connect((host, port))

#    client_data = input("#\t")
#    while True:
#        client_data = input()
#        if client_data == 'quit':
#            kulmiye.close()
#            sys.exit()
#        if len(str.encode(client_data)) > 0:
#            kulmiye.sendall(str.encode(client_data))
#            server_response = str(kulmiye.recv(1024), "utf-8")

#            print(server_response, end = " ")

    kulmiye.sendall(bytes(tvar.encode("utf-8")))
    kulmiye.sendall(bytes(','.encode("utf-8")))
    kulmiye.sendall(bytes(str(nval).encode("utf-8")))
    kulmiye.sendall(bytes(','.encode("utf-8")))
    kulmiye.sendall(bytes(str(lval).encode("utf-8")))

    server_data = kulmiye.recv(1024).decode("utf-8")


    while server_data != 'goodbye':
        server_data = kulmiye.recv(1024).decode("utf-8")
        print('Server: \n' + server_data)
        #        client_data = input("#\t")
        if not server_data:
            break
    kulmiye.close()

# this code block serves to give the user the ability to play lotto max
# with the amount of tickets and lines per ticket they would like

# Using the argparse module to allow the user to input command-line interfaces
parser = argparse.ArgumentParser(description='Welcome to OLG Gaming.')
parser.add_argument(
    '-t',
    type=str,
    help="Pick the lottery you want to play",
    required=True)
parser.add_argument(
    '-n',
    type=int,
    help="Pick the amount of lottery tickets you want to play",
    required=True)
parser.add_argument(
    '-l',
    type=int,
    help="Pick the amount of lines you would like to play",
    required=True)
# parser.add_argument('-o', type = str, help = "This is optional", required = False)

# parse_args will convert the argument strings into objects and will get
# stored in the cmdargs variable
cmdargs = parser.parse_args()

tvar = cmdargs.t  # the t string argument that gets parsed into an object will get stored into a variable called tvar
# the n integer argument that gets parsed into an object will get stored

# into a variable called nval
nval = int(cmdargs.n)

# the l integer argument that gets parsed into an object will get stored
# into a variable called lval
lval = int(cmdargs.l)

if __name__ == "__main__":
    client()
```code```

服务器

python3 socketserver.py

  • 将 localhost 指定为 IP
  • 指定一个端口,例如第4444章

客户

python3 socketclient.py -t Lotto_Max -n 1 -l 1

  • 指定一个 IP 地址连接到服务器(例如 localhost 或 127.0.0.1)
  • 指定要连接的端口,例如第4444章

当客户端和服务器之间建立连接时,服务器接收客户端输入并在其末端打印游戏类型(Lotto_Max)、票数和每票行

服务器将输出结果例如

但是,客户不会无限期地收到它。通常它会得到大约 25% 的时间,我不知道为什么

【问题讨论】:

  • 请将您的代码减少到演示问题所需的最低限度。包含输入以及期望和观察到的输出也将有所帮助。

标签: python python-3.x sockets


【解决方案1】:

服务器有一个问题:

server_data = conn.recv(1024).decode("utf-8")

conn.recv(1024) 可以接收从 0(关闭的连接)到 1024 的任意数量的字节。上面的行假设每次都接收到整个消息,但是当它失败时,它只会收到部分消息。下面我修改了您的代码,以便服务器一次可以处理多个客户端连接,并且客户端将一遍又一遍地连接/断开连接以加速失败:

服务器:

import socket
from random import sample

def main():
    host = ''
    port = 4444
    kulmiye = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    kulmiye.bind((host, port))
    print('Socket bind accomplished...\n')
    print("Listening for an incoming connection...")
    kulmiye.listen()

    while True:
        conn, addr = kulmiye.accept()
        print('Connected with ' + addr[0] + ':' + str(addr[1]))
        with conn:
            server_data = conn.recv(1024).decode("utf-8")
            print(f'server_data={server_data}')
            game_data = server_data.split(",")
            print("From Client: " + str(game_data))
            if server_data:
                if game_data[0] == "Lotto_Max":
                    nval = int(game_data[1])
                    lval = int(game_data[2])
                    for nval in range(nval):
                        for i in range(lval):
                            numbers = sample(range(1, 50), 7)
                            numbers.sort()
                            sortedd = str(numbers)
                            print(sortedd)
                        print("--------------------")
                        conn.sendall(sortedd.encode("utf-8"))
                        liners = "-----------------------"
                        conn.sendall(liners.encode("utf-8"))
                    conn.sendall(b'goodbye')

if __name__ == '__main__':
    main()

客户:

import socket
import argparse

def client():
    host = 'localhost'
    port = 4444

    kulmiye = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    kulmiye.connect((host, port))

    kulmiye.sendall(bytes(tvar.encode("utf-8")))
    kulmiye.sendall(bytes(','.encode("utf-8")))
    kulmiye.sendall(bytes(str(nval).encode("utf-8")))
    kulmiye.sendall(bytes(','.encode("utf-8")))
    kulmiye.sendall(bytes(str(lval).encode("utf-8")))

    server_data = kulmiye.recv(1024).decode("utf-8")

    while server_data != 'goodbye':
        server_data = kulmiye.recv(1024).decode("utf-8")
        print('Server: \n' + server_data)
        #        client_data = input("#\t")
        if not server_data:
            break
    kulmiye.close()

tvar = 'Lotto_Max'
nval = 1
lval = 1

if __name__ == "__main__":
    while True:
        client()

这是 100 次成功连接后的结果。注意server_data连接成功与不成功之间:

Connected with 127.0.0.1:12175
server_data=Lotto_Max,1,1
From Client: ['Lotto_Max', '1', '1']
[4, 7, 9, 12, 24, 31, 48]
--------------------
Connected with 127.0.0.1:12176
server_data=Lotto_Max,1,
From Client: ['Lotto_Max', '1', '']
Traceback (most recent call last):
  File "C:\server.py", line 38, in <module>
    main()
  File "C:\server.py", line 24, in main
    lval = int(game_data[2])
ValueError: invalid literal for int() with base 10: ''

conn.recv(1024) 没有收到完整的消息(没有收到最终的1)。 TCP 是一种字节流协议,没有消息边界的概念。您的代码负责调用recv() 并缓冲数据,直到您收到完整的消息。您可以发送固定大小的消息并调用 recv 直到您有那么多字节,使用诸如换行符(\n)之类的标记字节终止消息并读取直到收到标记,或者首先发送消息的大小通过消息字节。

我不会为你解决它,因为它是一个赋值,但作为一个提示 socket.makefile 可以将套接字包装在一个类似文件的对象中,你可以调用 .read(n) 从一个socket,或.readline() 读取\n-终止的行。

这是我的一个答案的链接,它证明了这一点:https://stackoverflow.com/a/55840026/235698

另一个 TCP 流性质的例子是在客户端。有时,它会打印:

-----------------------
Server:
goodbye
Server:
-----------------------goodbye
Server:

Server:
-----------------------
Server:
goodbye

服务器中的单独行:

conn.sendall(liners.encode("utf-8"))
conn.sendall(b'goodbye')

有时会被客户端中的单个recv 接收:

server_data = kulmiye.recv(1024).decode("utf-8")

同样,这是由于 TCP 的流式传输特性和没有消息边界。如果您发送的每一行都以换行符结尾,您可以多次调用 recv() 直到检测到换行符,然后只处理该行。

另一个说明这种技术的答案在这里:https://stackoverflow.com/a/55186805/235698

【讨论】:

    【解决方案2】:

    感谢 Mark Tolonen 的指导。我还需要复习如何处理 TCP 数据流。尽管如此,我还是能够使用众多 conn.sendall 在我的嵌套 for 循环中。快乐的露营者!

    【讨论】:

      猜你喜欢
      • 2011-06-06
      • 1970-01-01
      • 1970-01-01
      • 2011-05-26
      • 1970-01-01
      • 2016-03-30
      • 2011-11-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多