python——socket网络编程
一、OSI七层模型
网络通信要素:
- 源、目的IP地址
- 应用程序端口号
- 通信协议
二、Socket
socket通常也称作”套接字“。网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
Socket通信流程:
1、服务器根据IP地址类型、socket类型、协议来创建socket对象。
2、为服务器socket对象绑定IP地址和端口号。
3、服务器socket监听端口号请求,随时准备接收来自客户端的请求,此时,socket并没有打开。
4、客户端创建socket对象。
5、客户端打开socket,根据IP地址和端口号试图连接服务器。
6、服务器监听到客户端的连接请求,被动打开socket。
7、服务器socket通过Accept进入阻塞状态,等待客户端返回连接信息。
8、客户端连接成功,返回连接信息给服务器。
9、服务器连接成功。
10、服务器与客户端之间,根据“一收一发”原则发送和接收数据。
11、客户端socket关闭。
12、服务器socket关闭。
三、方法
import socket
1. socket()
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
(1)family = AF_INET
表示服务器之间的通信。
(2)fmily = AF_UNIX
表示Unix不同进程间的通信。
(3)type = SOCK_STREAM
表示TCP连接。
(4)type = SOCK_DGRAM
表示UDP连接。
2. bind()
sk.bind(address)
将地址与socket绑定。address(host IP,端口号),address必须是个元组。
1 sk = socket.socket() 2 print(sk) 3 4 addr = ('127.0.0.1',8080) 5 sk.bind(addr) 6 print(sk)
输出结果:
<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080)>
3. listen()
listen(backlog)
监听客户端的连接。backlog为可选参数,表示最大等待连接数量。
4.accept()
接受连接并返回(conn,address)。其中conn表示客户端的sk对象,
1 sk = socket.socket() 2 address = ('127.0.0.1',9080) 3 sk.bind(address) 4 sk.listen(5)
5 conn,addr = sk.accept() 6 print(sk) 7 print(conn) 8 print(addr)
输出结果:
<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9080)>
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9080), raddr=('127.0.0.1', 35066)>
('127.0.0.1', 35066)
5.recv()
sk.recv(bufsize)
接收数据。其中bufsize表示最大可接收的数据大小。
6.connect()
sk.connect(address)
连接指定地址的socket。address用元组表示。
7.send()
sk.send(data)
将data发送给连接的socket。
8.sendall()
与send类似,内部通过递归调用send()方法,尝试将所有数据发送出去。
9.sendto()
sk.sendto(data,addr)
可指定远端地址。10.settimeout(timeout)
设置超时。
11.getpeername()
返回远端socket的地址(address,port)。
12.getsockname
返回自己的socket地址(address,port)。
13.fileno()
套接字的文件描述符。
四、实例
1.Client连接Server,并发送数据给Server。
server:
1 sk = socket.socket() 2 address = ('127.0.0.1',9080) 3 sk.bind(address) 4 sk.listen(5) 5 conn,addr = sk.accept() 6 data = conn.recv(1024) 7 print(str(data,'utf8'))
client:
1 sk = socket.socket() 2 address = ('127.0.0.1',9080) 3 sk.connect(address) 4 inp = input('>>') 5 sk.send(bytes(inp,'utf8'))
2.
server:
1 sk = socket.socket() 2 address = ('127.0.0.1',9090) 3 sk.bind(address) 4 sk.listen(3) 5 6 while 1: 7 conn,addr = sk.accept() 8 print(addr) 9 while 1: 10 try: 11 data = conn.recv(1024) #当conn挂掉后,进行异常处理 12 except Exception: 13 break 14 print(str(data, 'utf8')) 15 if not data: 16 break 17 inp = input('>>') 18 conn.send(bytes(inp,'utf8')) 19 sk.close()
client:
1 sk = socket.socket() 2 address = ('127.0.0.1',9090) 3 sk.connect(address) 4 while 1: 5 inp = input('>>') 6 if inp == 'exit': 7 break 8 sk.send(bytes(inp,'utf8')) 9 data = sk.recv(1024) 10 print(str(data,'utf8')) 11 sk.close()
3.在客户端输入一条命令,服务器端执行命令,并返回结果给客户端
注意:需要解决粘包现象
server:
1 import subprocess 2 3 sk = socket.socket() 4 address = ('127.0.0.1',9090) 5 sk.bind(address) 6 sk.listen(3) 7 8 while 1: 9 conn,addr = sk.accept() 10 print(addr) 11 while 1: 12 try: 13 data = conn.recv(1024) 14 except Exception: 15 break 16 if not data: break 17 print(str(data, 'utf8')) 18 19 obj = subprocess.Popen(str(data, 'utf8'),shell=True,stdout=subprocess.PIPE) 20 cmd_result = obj.stdout.read() 21 result_len = bytes(str(len(cmd_result)),'utf8') 22 print(result_len) 23 conn.sendall(result_len) 24 import time 25 time.sleep(1) #解决粘包现象:在两次send之间,停留一段时间使之隔断。 26 # conn.recv(1024) #或者接收一个数据,将两次send隔断。 27 conn.sendall(cmd_result) 28 29 sk.close()
client:
1 sk = socket.socket() 2 address = ('127.0.0.1',9090) 3 sk.connect(address) 4 while 1: 5 inp = input('>>') 6 if inp == 'exit': 7 break 8 sk.send(bytes(inp,'utf8')) 9 10 result_len = int(str(sk.recv(1024),'utf8')) 11 # sk.send(bytes('ok','utf8')) 12 data = bytes() 13 while len(data) != result_len: 14 ret = sk.recv(1024) 15 data += ret 16 17 print(str(data,'gbk')) 18 sk.close()
4.实现简单的文件传输
server:
1 time.asctime() 2 3 sk = socket.socket() 4 address = ('127.0.0.1',8081) 5 sk.bind(address) 6 sk.listen(3) 7 8 while 1: 9 conn,addr = sk.accept() 10 while 1: 11 data = conn.recv(1024) #bytes 12 # data = str(data,'utf8') #将bytes转成string 13 # print(data) # post|123.mp4|86566153 14 cmd,filename,filesize = str(data,'utf8').split(' ') 15 filesize = int(filesize) 16 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 17 path = os.path.join(BASE_DIR,'FTP目录',filename) 18 has_sent = 0 19 with open(path,'ab') as f: 20 while has_sent != filesize: 21 data = conn.recv(1024) 22 f.write(data) 23 has_sent += len(data) 24 print("文件%s上传成功,文件大小:%s"%(filename,filesize))
client:
1 sk = socket.socket() 2 address = ('127.0.0.1',8081) 3 sk.connect(address) 4 5 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 6 print(BASE_DIR) 7 8 while 1: 9 inp = input(">>").strip() #post 123.mp4 10 cmd,filename = inp.split(' ') 11 path = os.path.join(BASE_DIR,filename) 12 13 if os.path.exists(path): 14 filename = os.path.basename(path) #获取文件名 15 print(filename) 16 filesize = os.stat(path).st_size #获取文件大小 17 print(filesize) 18 file_info = 'post %s %s'%(filename,filesize) #string 19 20 sk.sendall(bytes(file_info,'utf8')) #需要将string转成bytes才能进行传输 21 22 with open(path,'rb') as f: 23 has_post = 0 24 while has_post != filesize: 25 data = f.read(1024) 26 sk.sendall(data) 27 has_post += len(data) 28 print("上传成功!")
五、socketserver
socketserver可以简化了网络服务器的编写,减少开发人员编写网络服务器程序的工作量。
- TCPServer
- UDPserver
- UnixStreamServer
- UnixDatagramServer
这4各类会同步处理每一个request,也就是说只有当前的request处理完才会处理下一个request,这种方式显然很不合理,如果当前的request处理过慢导致堵塞。正确的处理方式应该是创建新的进程或线程去处理不同的request。
另外通过ForkingMixIn和ThreadingMixIng类来支持异步。
Server类
包含五种server类:
- BaseServer(不直接对外服务)
- TCPServer
- UnixStreamServer
- UDPServer
- UnixDatagramserver
五种类的继承关系如下:
官方参考文档:
https://docs.python.org/3/library/socketserver.html
创建socketserver的步骤:
(1)穿件处理request的类,创建方法为:继承BaseRequestHandler类,并重载handle()方法。该方法将被回调用作处理当前接收到的request。
注意:一般的做法是直接继承StreamRequestHandler或者DatagramRequestHandler。比如:
1 class MyTCPHandler(SocketServer.StreamRequestHandler):
(2)实例化一个server基类的对象,冰法服务器地址和处理request的类作为参数传入。
(3)使用server基类对象调用handle_request()或serve_forever()方法,即可处理一个或多个request。
(4)如果需要创建多进程或多线程的服务器程序,则可以通过混合继承ForkingMixIn或ThreadingMixIn类来实现,比如:
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass //创建一个多线程的TCP服务器。
注意:ThreadingMixIn必须要放在TCPServer前面。
简单并发实例:
server:
1 import socketserver 2 3 class MyServer(socketserver.BaseRequestHandler): #定义request handler类,从 4 BaseRequestHandler类继承 5 def handle(self): #重写handle()方法,处理当前的request 6 print("服务端启动..") 7 while True: 8 conn = self.request 9 print(self.client_address) 10 while True: 11 client_data = conn.recv(1024) #self.request是和客户端连接的套接字 12 print(str(client_data,'utf8')) 13 server_response = input('>>').strip() 14 conn.send(bytes(server_response,'utf8')) 15 conn.close() 16 17 if __name__ == '__main__': 18 server = socketserver.ThreadingTCPServer(('127.0.0.1',8081),MyServer)
#传入监听地址 端口号和request handler类 19 server.serve_forever() #启动监听处理request
client:
1 import socket 2 3 sk = socket.socket() 4 address = ('127.0.0.1',8081) 5 sk.connect(address) 6 print("客户端启动..") 7 8 while True: 9 inp = input(">>") 10 sk.sendall(bytes(inp,'utf8')) 11 if inp == 'exit': 12 break 13 server_response = sk.recv(1024) 14 print(str(server_response,'utf8')) 15 16 sk.close()
posted on 2016-09-27 14:43 oliver.lee 阅读(303) 评论(0) 编辑 收藏 举报