7-24 1,990PVs
Socket网络编程,当不涉及底层的东西时十分简单。Python的Socket编程比C语言更加的简单,举例,C语言要建立TCP时需要知道IP地址和端口号,不能直接按照域名和端口号进行。但是Python却可以,Python自带DNS解析部分,可以直接利用URL和端口号建立连接。代码:
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/usr/bin/env python # Basic Connection Example - Chapter 2 - connect.py import socket print "Creating socket...", s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print "done." print "Connecting to remote host...", s.connect(("www.google.com", 80)) print "done." |
操作系统往往都有一个一个服务器端口号的列表,Unix系统中,在/etc/service目录下找到这个列表。Python的socket库中包含一个getservicebyname()的函数,可以自动查询协议的端口号。举例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/usr/bin/env python # Revised Connection Example - Chapter 2 - connect.py import socket print "Creating socket...", s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print "done." print "Looking up port number...", port = socket.getservbyname('http', 'tcp') print "done." print "Connecting to remote host on port %d..." % port, s.connect(("www.google.com", port)) print "done." |
一旦建立了一个Socket连接,您就可以从它那里得到有用的信息,代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#!/usr/bin/env python # Information Example - Chapter 2 import socket print "Creating socket...", s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print "done." print "Looking up port number...", port = socket.getservbyname('http', 'tcp') print "done." print "Connecting to remote host on port %d..." % port, s.connect(("www.google.com", port)) print "done." print "Connected from", s.getsockname() #可以打印出本机的IP和端口号 print "Connected to", s.getpeername() #可以打印出对端的IP和端口号 |
利用Socket能够做很多事情,比如建立简单的通信机制,进行发送和接受数据。Python提供的两种方法都很好用,一种是Socket对象一种是文件的类对象,Socket对象提供了操作系统的Send()、Sendto()、recv()、recvfrom()的调用接口。文件类对象提供了read()、write()、readline()这些典型的Python接口。当您有一些特殊要求的时候,Socket对象十分有用。例如:读写数据时,加入需要协议可以详细的控制,使用二进制传送固定大小的文件时,数据超时需要特殊处理时;再或者是任何不只需要的简单读写时。当您编写UDP程序的时候,Socket对象同样很好用。文件类对象一般用于面向线性的协议,因为它能通过提供readline()函数自动处理大量解析的工作,文件类对象一般对TCP连接工作的很好,对UDP连接反而不是很好。因为TCP连接可以保证数据完整,TCP的传输是像文件一样通过字节流进行的。而UDP是一种基于信息包的通信。文件类对象没有办法操作每个基本的信息包。
在网络通信中出现异常是不可避免的。不稳定的通信环境都会造成异常。例如:服务器关机、连接中断等。下面一个程序是简单的HTTP请求,包括了一些异常的处理。在这个程序中,异常处理只是简单地打印出一个友好的信息并终止运行。并捕获所有在这个例子中可能产生的和网络相关的异常。python的sockets模块实际上定义了4种可能出现的异常:
* 与一般I/O和通信问题有关的Socket.error;
* 与查询地址信息有关的Socket.gaierror;
* 与其他地址错误有关的Socket.herror(和C语言中的h_error相关)
* 与在一个Socket上调用settimeout()后,处理超时有关的Socket.timeout()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#!/usr/bin/env python # Error Handling Example - Chapter 2 - socketerrors.py import socket, sys host = sys.argv[1] textport = sys.argv[2] filename = sys.argv[3] try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error, e: print "Strange error creating socket: %s" % e sys.exit(1) # Try parsing it as a numeric port number. try: port = int(textport) except ValueError: # That didn't work, so it's probably a protocol name. # Look it up instead. try: port = socket.getservbyname(textport, 'tcp') except socket.error, e: print "Couldn't find your port: %s" % e sys.exit(1) try: s.connect((host, port)) except socket.gaierror, e: print "Address-related error connecting to server: %s" % e sys.exit(1) except socket.error, e: print "Connection error: %s" % e sys.exit(1) try: s.sendall("GET %s HTTP/1.0rnrn" % filename) except socket.error, e: print "Error sending data: %s" % e sys.exit(1) while 1: try: buf = s.recv(2048) except socket.error, e: print "Error receiving data: %s" % e sys.exit(1) if not len(buf): break sys.stdout.write(buf) |
以上的错误处理机制还不够,在客户端向服务器进行写操作之后,还应该关闭连接。使用shutdown()函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#!/usr/bin/env python # Error Handling Example With Shutdown - Chapter 2 import socket, sys, time host = sys.argv[1] textport = sys.argv[2] filename = sys.argv[3] try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error, e: print "Strange error creating socket: %s" % e sys.exit(1) # Try parsing it as a numeric port number. try: port = int(textport) except ValueError: # That didn't work. Look it up instread. try: port = socket.getservbyname(textport, 'tcp') except socket.error, e: print "Couldn't find your port: %s" % e sys.exit(1) try: s.connect((host, port)) except socket.gaierror, e: print "Address-related error connecting to server: %s" % e sys.exit(1) except socket.error, e: print "Connection error: %s" % e sys.exit(1) print "sleeping..." time.sleep(10) print "Continuing." try: s.sendall("GET %s HTTP/1.0rnrn" % filename) except socket.error, e: print "Error sending data: %s" % e sys.exit(1) try: s.shutdown(1) except socket.error, e: print "Error sending data (detected by shutdown): %s" % e sys.exit(1) while 1: try: buf = s.recv(2048) except socket.error, e: print "Error receiving data: %s" % e sys.exit(1) if not len(buf): break sys.stdout.write(buf) |
再举一个使用makefiel()和shutdown()处理异常的函数。文件类对象由于缓冲器引起的错误可能比较容易出错,因为无法控制具体是什么时候产生发送数据的尝试。避免在文件类对象中使用缓冲器。对flush()的调用,因为makefile()的调用没有制定缓冲(buffer)。这个调用不是必须的,但是当使用了缓冲器时,则需要使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
#!/usr/bin/env python # Error Handling Example With Shutdown and File-Like Objects - Chapter 2 import socket, sys, time host = sys.argv[1] textport = sys.argv[2] filename = sys.argv[3] try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error, e: print "Strange error creating socket: %s" % e sys.exit(1) # Try parsing it as a numeric port number. try: port = int(textport) except ValueError: # That didn't work. Look it up instread. try: port = socket.getservbyname(textport, 'tcp') except socket.error, e: print "Couldn't find your port: %s" % e sys.exit(1) try: s.connect((host, port)) except socket.gaierror, e: print "Address-related error connecting to server: %s" % e sys.exit(1) except socket.error, e: print "Connection error: %s" % e sys.exit(1) fd = s.makefile('rw', 0) print "sleeping..." time.sleep(10) print "Continuing." try: fd.write("GET %s HTTP/1.0rnrn" % filename) except socket.error, e: print "Error sending data: %s" % e sys.exit(1) try: fd.flush() except socket.error, e: print "Error sending data (detected by flush): %s" % e sys.exit(1) try: s.shutdown(1) except socket.error, e: print "Error sending data (detected by shutdown): %s" % e sys.exit(1) while 1: try: buf = fd.read(2048) except socket.error, e: print "Error receiving data: %s" % e sys.exit(1) if not len(buf): break sys.stdout.write(buf) |
使用UDP通信从来不使用文件类型对象,因为不能为数据提供一定稳定性和控制。先来一个基本的UDP客户端程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#!/usr/bin/env python # UDP Example - Chapter 2 import socket, sys, time host = sys.argv[1] textport = sys.argv[2] s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: port = int(textport) except ValueError: # That didn't work. Look it up instread. port = socket.getservbyname(textport, 'udp') s.connect((host, port)) print "Enter data to transmit: " data = sys.stdin.readline().strip() s.sendall(data) s.shutdown(1) print "Looking for replies; press Ctrl-C or Ctrl-Break to stop." while 1: buf = s.recv(2048) if not len(buf): break print "Received: %s" % buf |
值得一提的时候,UDP程序有时候根本不调用Connect函数。这个程序是RFC868上定义的简单时间协议的示范,程序向time.nist.gov的服务器发送了一个空字符,并未建立connect。程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#!/usr/bin/env python # UDP Connectionless Example - Chapter 2 - udptime.py import socket, sys, struct, time hostname = 'time.nist.gov' port = 37 host = socket.gethostbyname(hostname) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.sendto('', (host, port)) print "Looking for replies; press Ctrl-C to stop." buf = s.recvfrom(2048)[0] if len(buf) != 4: print "Wrong-sized reply %d: %s" % (len(buf), buf) sys.exit(1) secs = struct.unpack("!I", buf)[0] secs -= 2208988800 print time.ctime(int(secs)) |