标签:
这一部分主要介绍python中socket模块的相关内容,socket即套接字。
socket是使用TCP/IP协议的应用程序通常采用的应用编程接口,它位于运输层和应用层之间,起源于UNIX,由于遵从UNIX“一切皆文件的”思想故socket可看作一种特殊的文件,对其的操作基本可以视为读写I/O、打开、关闭。关于套接字的基本概念@吴秦的Linux Socket编程(不限Linux)写的很详细,大家可以参考。
在下面列出的各个部分中我将先贴出代码,然后对其进行解释。
1 >>> import socket 2 >>> host_name = socket.gethostname() 3 >>> print "Host name: %s" %host_name 4 Host name: debian6 5 >>> print "IP address: %s" %socket.gethostbyname(host_name) 6 IP address: 127.0.1.1
上面代码中gethostname方法返回字符串形式的当前主机名,gethostbyname方法返回对应主机的IPv4地址,注意,这里的host可以是类似‘www.baidu.com’等因特网上主机名。
gethostname() -> host
gethostbyname(host) -> address
代码很简单明了,不详细说了。
将前面的gethostbyname(host)方法的参数host设为‘www.baidu.com‘格式的远端主机名就能获得其IP地址。
1 import socket 2 def get_remote_machine_info(): 3 remote_host = ‘www.baidu.com‘ 4 try: 5 print("IP address: %s" % socket.gethostbyname(remote_host)) 6 except socket.error as err_msg: 7 print("%s: %s" %(remote_host, err_msg)) 8 if __name__ == ‘__main__‘: 9 get_remote_machine_info()
处理到底层网络时,通常用到的是32位二进制形式的而不是字符串形式的IP地址,socket库提供了相互转换的方法:inet_aton()和inet_ntoa()
1 socket.inet_aton(string) #将字符串形式的IP地址转换为用于底层网络的32位形式地址
2 socket.inet_ntoa(packed_ip) -> ip_address_string #将32位形式ip地址转化为字符串形式
>>> socket.inet_aton(‘127.0.0.1‘)
b‘\x7f\x00\x00\x01‘
>>> socket.inet_ntoa(b‘\x7f\x00\x00\x01‘)
‘127.0.0.1‘
getservbyport(port[, protocolname]) -> string
getservbyport方法可以方便的通过查看指定端口对应的服务名称,其中port为端口号,可选参数protocolname为使用协议,以下例子获取80 25 53端口对应的服务:
import socket def find_service_name(): protocolname = ‘tcp‘ for port in [80, 25]: print("Port: %s => service name: %s" % (port, socket.getservbyport(port, protocolname))) print("Port: %s => service name: %s" % (53, socket.getservbyport(53,‘udp‘))) if __name__ == ‘__main__‘: find_service_name()
[output]
Port: 80 => service name: http
Port: 25 => service name: smtp
Port: 53 => service name: domain
def convert_integer(): """将数据字节在本地顺序和网络顺序间转换""" data = 1234 #将data转换为32位长格式 print("Original: %s => Long host byte order: %s, Network byte order: %s" % (data, socket.ntohl(data),socket.htonl(data))) #将data转换为16位短格式 print("Original: %s => Short host byte order: %s, Network byte order: %s" % (data, socket.ntohs(data), socket.htons(data)))
Original: 1234 => Long host byte order: 3523477504, Network byte order: 3523477504
Original: 1234 => Short host byte order: 53764, Network byte order: 53764
def test_socket_timeout(): """通过gettimeout和settimeout方法获取和设置timeout时间.""" # first args of s is socket family, second args is socket type. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print("Default socket timeout: %s" % s.gettimeout()) s.settimeout(100) print("Current socket timeout: %s" % s.gettimeout())
Default socket timeout: None
Current socket timeout: 100.0
getsockopt(level, option[, buffersize]) -> value
setsockopt(level, option, value)
socket.socket.getsocket方法可以获得当前缓存大小信息,socket.socket.setsocket方法可以重新设置缓存大小:
import socket SEND_BUF_SIZE = 4096 RECV_BUF_SIZE = 4096 def modify_buff_size(): """修改发送和接收缓存的大小""" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 获得发送缓存大小 bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF) print("Buffer size [Before]:%d" % bufsize) sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) sock.setsockopt( socket.SOL_SOCKET, socket.SO_SNDBUF, SEND_BUF_SIZE) sock.setsockopt( socket.SOL_SOCKET, socket.SO_RCVBUF, RECV_BUF_SIZE) bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF) print("Buffer size [After]:%d" % bufsize) if __name__ == ‘__main__‘: modify_buff_size()
[output]
Buffer size [Before]:131072
Buffer size [After]:4096
socket默认为阻塞模式,即调用connect等API时程序会被阻塞直到操作完成,然而很多时候这不是我们所希望的,幸运的是可以通过socket.socket.setblocking来设置:
setblocking(flag) Set the socket to blocking (flag is true) or non-blocking (false). setblocking(True) is equivalent to settimeout(None); setblocking(False) is equivalent to settimeout(0.0).
下面的例子将socket设为了非阻塞模式:
import socket def test_socket_modes(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setblocking(0) s.settimeout(0.5) s.bind(("127.0.0.1", 0)) socket_address = s.getsockname() print("Trivial Server launched on socket: %s" % str(socket_address)) while True: s.listen(1) if __name__ == ‘__main__‘: test_socket_modes()
当我们关闭某个特定端口上的python服务端后如果试图重新在这个端口上打开它系统将会提示“Address already in use”错误,然而很多情况下客户端都需要通过某个特定的端口连接服务程序,这可以通过开启socket地址和端口重用实现。
import socket def reuse_socket_addr(): """ 使端口在关闭或者发生异常而退出时能重新使用""" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #获得SO_REUSEADDR状态 old_state = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) print("Old sock state: %s" % old_state) #设置端口能够被重用 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) new_state = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) print(" New sock state: %s" % new_state) local_port = 8282 srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv.bind((‘‘, local_port)) srv.listen(1) print(" Listening on port: %s" % local_port) while True: try: connection, addr = srv.accept() print("Connected by %s: %s" % (addr[0], addr[1])) except KeyboardInterrupt: break except socket.error as e: print(‘%s‘ % (e,)) if __name__ == ‘__main__‘: reuse_socket_addr()
很多程序的正常运行依赖于精确的时间,这就需要使本地时间与网络时间进行同步以保持其精确性。NTP(Network Time Protocol, 网络时间协议)就是用来使网络中各个计算机时间同步的一种协议,下面就是一个简单的同步时间程序,在运行前请先安装ntplib库:
pip3 install ntplib
import ntplib from time import ctime def print_time(): """First: pip3 install ntplib, NTP(Network Time Protocol)""" ntp_client = ntplib.NTPClient() response = ntp_client.request(‘pool.ntp.org‘) print(ctime(response.tx_time)) if __name__ == ‘__main__‘: print_time()
在介绍完前面的一些基本的socket API后,我们来动手写一个简单的“回声”程序来进行巩固、复习。
在这个应用中,服务端和客户端都通过argparse模块得到port参数,在服务端,首先建立一个基于TCP的套接字,并设置其可以被重用使服务端程序能打开任意次,然后将它绑定在本机上命令行指定的端口上;接着,在监听阶段,我们设置backlog参数使得服务端能同时监听多个客户端连接,最后等待客户端连接进来并发送一些数据,在接收到这些数据之后,再将其原封不动地返回回去。
注意: 由于python 2.x和python3.x编码类型地不同,python2.x中直接s.send(data)就行,然而,socket中还不支持python3的编码,因此,python3中需要先对str型的data进行encode, 在从socket接受下来的数据打印前也需要先decode处理!否则会报错。
[服务端程序]
import socket import argparse HOST = ‘localhost‘ DATA_PAYLOAD = 2048 #The max number of clients. BACKLOG = 5 def echo_server(port): """A simple echo server""" #Create a TCP socket serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #Enable reuse address/port serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #Bind the socket to the port server_address = (HOST, port) print("Starting up echo server on %s port %s" % server_address) serv.bind(server_address) #Listen to clients, backlog argumen specifies the max no. of queued connections serv.listen(BACKLOG) while True: print("waiting to receive message from client") client, address = serv.accept() data = client.recv(DATA_PAYLOAD) if data: print("Data: %s" % data.decode()) client.send(data) print("sent %s bytes back to %s" % (data.decode(), address)) #end connection client.close() if __name__ == ‘__main__‘: parser = argparse.ArgumentParser(description=‘Socket Server Example‘) parser.add_argument("--port", action = ‘store‘, dest=‘port‘, type = int, required=True) given_args = parser.parse_args() port = given_args.port echo_server(port)
在client端,我们使用命令行参数获得的server端口号创建socket并且与server连接,成功后向服务器发送信息,之后马上按一次16字节接收server回传的信息。
import socket import argparse HOST = ‘localhost‘ def echo_client(port): """A simple echo client""" # Create a TCP/IP socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Connect the socket to the server server_address = (HOST, port) print("Connecting to %s port %s" % server_address) client.connect(server_address) try: #Send data message = " Test message. This will be echoed" print("Sending %s" % message) client.sendall(message.encode()) # Receive from server. amount_received = 0 amount_excepted = len(message) while amount_received < amount_excepted: data = client.recv(16) amount_received += len(data) print("Received: %s" % data.decode()) except socket.error as e: print("Socket error %s" % str(e)) except Exception as e: print("Other exception: %s" % str(e)) finally: client.close() if __name__ == ‘__main__‘: parser = argparse.ArgumentParser(description=‘Socket Client Example‘) parser.add_argument(‘--port‘,action=‘store‘, dest=‘port‘, type=int,required=True) given_args = parser.parse_args() port = given_args.port echo_client(port)
[output]
$ python3 1_13a_echo_server.py --port=9900 Starting up echo server on localhost port 9900 Waiting to receive message from client Now, run the client from another terminal as follows: $ python3 1_13b_echo_client.py --port=9900 Connecting to localhost port 9900 Sending Test message. This will be echoed Received: Test message. Th Received: is will be echoe Received: d Closing connection to the server Upon connecting to the localhost, the client server will also print the following message: Data: Test message. This will be echoed sent Test message. This will be echoed bytes back to (‘127.0.0.1‘, 42961) Waiting to receive message from client
《Python Network Programming Cookbook》读书笔记1---套接字, IPv4, 简单的Client/Server程序
标签:
原文地址:http://www.cnblogs.com/weixinbupt/p/4666063.html