Python网络编程——编写一个简单的回显客户端/服务器应用
今天将python中socket模块的基本API学习完后,照着书上的实例编写一个套接字服务器和客户端。采用python3.5版本,在注释中会标明python2和python3的不同之处。
1.代码
(1)服务器端及对应代码解释
1 # ! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # 编写回显服务器 4 5 import socket 6 import sys 7 import argparse 8 9 # 定义常量 10 host = 'localhost' 11 data_payload = 2048 12 backlog = 5 13 14 15 def echo_server(port): 16 17 # 创建一个TCP socket 18 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 19 20 # 设置TCP套接字关联选项——重用地址 21 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 22 23 # 建立套接字端口 24 server_address = (host, port) 25 print("Starting up echo server on %s port %s" % server_address) 26 27 # 将socket绑定到server_address地址 28 sock.bind(server_address) 29 30 # 监听客户端 31 # backlog指定最多允许多少个客户连接到服务器。它的值至少为1。收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求。 32 # 这里设定为5 33 sock.listen(backlog) 34 # 在调用 Listen 之前,必须首先调用 Bind 方法,否则 Listen 将引发 SocketException。 35 while True: 36 print("Waiting to receive message from client") 37 38 # 调用accept方法时,socket会时入“waiting”状态。客户请求连接时,方法建立连接并返回服务器。 39 # accept方法返回一个含有两个元素的 元组(connection,address)。第一个元素connection 40 # 是新的socket对象,服务器必须通过它与客户通信;第二个元素 address是客户的Internet地址。 41 client, address = sock.accept() 42 43 # 指定data最大长度为2048字节 44 data = client.recv(data_payload) 45 if data: 46 print("Data: %s" % data) 47 client.send(data) 48 print("sent %s bytes back to %s" % (data, address)) 49 # 关闭连接 50 client.close() 51 52 if __name__ == '__main__': 53 # 创建一个解析对象,其中描述为"Socket Error Examples" 54 parser = argparse.ArgumentParser(description='Socket Server Example') 55 # 采用add_argument方法 56 # name or flags —— 必须的参数,该参数接收选项参数或者是位置参数 57 # action: 58 # (1)store —— 默认action模式,储存值到指定变量 59 # (2)store_const —— 储存值在参数的const部分指定,多用于实现非布尔的命令行flag 60 # (3)store_true/store_false —— 布尔开关。可以2个参数对应一个变量 61 # (4)append —— 储存值到列表,储存值在参数的const部分指定 62 # (5)append_const —— 储存值到列表,储存值在参数的const部分指定 63 # (6)version —— 输出版本信息然后退出 64 # type —— 把从命名行输入的结果转成设置的类型,通常用来检查值的范围,以及合法性。默认string 65 # required —— 指定某个选项在命名中出现, 默认False, 若为 True, 表示必须输入该参数 66 # dest —— 把位置或者选项关联到一个特定的名字 67 parser.add_argument('--port', action="store", dest="port", type=int, required=True) 68 # 调用parse_args()方法进行解析 69 given_args = parser.parse_args() 70 port = given_args.port 71 echo_server(port) 72 73 def bind(self, address): # real signature unknown; restored from __doc__ 74 """ 75 bind(address) 76 77 Bind the socket to a local address. For IP sockets, the address is a 78 pair (host, port); the host must refer to the local host. For raw packet 79 sockets the address is a tuple (ifname, proto [,pkttype [,hatype]]) 80 """ 81 pass 82 def listen(self, backlog=None): # real signature unknown; restored from __doc__ 83 """ 84 listen([backlog]) 85 86 Enable a server to accept connections. If backlog is specified, it must be 87 at least 0 (if it is lower, it is set to 0); it specifies the number of 88 unaccepted connections that the system will allow before refusing new 89 connections. If not specified, a default reasonable value is chosen. 90 """ 91 pass
(2)客户端及对应代码解释
1 # ! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # 编写回显客户端 4 5 import socket 6 import sys 7 import argparse 8 9 host = 'localhost' 10 11 12 def echo_client(port): 13 # 创建TCP socket连接 14 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 15 # 获取服务器端host和端口号 16 server_address = (host, port) 17 print("Connecting to %s port %s" % server_address) 18 sock.connect(server_address) 19 20 try: 21 # 将message内容发送到服务器端 22 message = "Test message, This will be echoed" 23 print("Sending %s" % message) 24 # python2和python3此处不同 25 # python2—— sock.sendall(message) 26 # sendall()发送完整的TCP数据,成功返回None,失败抛出异常 27 sock.sendall(message.encode()) 28 29 # 服务器端将发送的数据回传给客户端并打印 30 amount_received = 0 31 amount_expected = len(message) 32 while amount_received < amount_expected: 33 data = sock.recv(1024) 34 amount_received += len(data) 35 print("Received: %s" % data) 36 # 处理相对应错误 37 except socket.error as e: 38 print("socket error: %s" % str(e)) 39 except Exception as e: 40 print("Other exception: %s" % str(e)) 41 finally: 42 print("Closing connection to the server") 43 sock.close() 44 45 if __name__ == '__main__': 46 parser = argparse.ArgumentParser(description='Socket Server Example') 47 parser.add_argument('--port', action="store", dest="port", type=int, required=True) 48 given_args = parser.parse_args() 49 port = given_args.port 50 echo_client(port)
(3)运行结果
1 1. 服务器端 2 Abel$ python3 1_13a_echo_server.py --port=9900 3 Starting up echo server on localhost port 9900 4 Waiting to receive message from client 5 6 2.客户端发送数据时,服务器端 7 Data: b'Test message, This will be echoed' 8 sent b'Test message, This will be echoed' bytes back to ('127.0.0.1', 62389) 9 Waiting to receive message from client 10 11 3.客户端 12 Abel$ python3 1_13b_echo_client.py --port=9900 13 Connecting to localhost port 9900 14 Sending Test message, This will be echoed 15 Received: b'Test message, This will be echoed' 16 Closing connection to the server