Python之socket
Socket通长也称作"套接字",用于描述IP地址和端口,是一个通信的句柄.
vim day8-16.py
#!/usr/bin/env python #coding:utf-8 import socket def handle_request(client): buf = client.recv(1024) print buf client.send("HTTP/1.1 200 OK\r\n\r\n") client.send("Hello, World") def main(): #创建socket对象 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #bind方法监听某个端口 sock.bind(('127.0.0.1',8080)) #开始监听, sock.listen(5) while True: #阻塞,等... #直到有请求连接 connection, address = sock.accept() #connection代表客户端sockert对象 #address客户端IP地址 handle_request(connection) connection.close() if __name__ == '__main__': main()
运行,然后在开启一个客户端访问
本客户端返回
使用python建立一个socket的服务端和客户端
vim socket_server.py
#!/usr/bin/env python #coding:utf-8 import socket obj_server = socket.socket() obj_server.bind(('localhost',8341)) obj_server.listen(5) #开始监听最大连接数5 while True: print 'waiting...' conn,addr = obj_server.accept() client_data = conn.recv(1024) #最多一次性接收1024size print client_data conn.send('好的') conn.close()
vim socket_client.py
#!/usr/bin/env python #coding:utf-8 import socket obj = socket.socket() obj.connect(('localhost',8341)) obj.send('我爱北京天安门') server_data = obj.recv(1024) print server_data obj.close() ~
运行服务端,然后新开一个窗口运行客户端
智能机器人实例
vim day8-18.py
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ('127.0.0.1',8888) sk = socket.socket() sk.bind(ip_port) sk.listen(5) while True: conn,address = sk.accept() conn.sendall('欢迎致电10086,请输入1xxx,0转人工服务') Flag = True while Flag: data = conn.recv(1024) #阻塞等待客户端发生数据 print data #打印从客户端接收到的数据 if data == 'exit': Flag = False elif data == '0': conn.sendall('您的通话可能被录音') else: conn.sendall('请重新输入') conn.close()
vim day8-19.py
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ('127.0.0.1',8888) sk = socket.socket() sk.connect(ip_port) #客户端连接服务端 sk.settimeout(5) while True: data = sk.recv(1024) #接收服务端数据欢迎致电... print 'recevie:',data inp = raw_input('please input:') sk.sendall(inp) if inp == 'exit': break sk.close()
执行过程
服务端
客户端
PS:一个socket同时只能处理一个请求,如果一个请求在连接中,其他请求在过来将排队等待,等待时间为设置的超时时间为5s,python提供了一个模块用于多线程模块socketserver
SoketServer
多线程条件
1,必须写一个类
2,必须继承SocketServer.BaseRequestHandler
3,必须写一个方法而且方法名必须为handle
4,必须调用ThreadingTCPServer方法来实现多线程
day8-22.py
#!/usr/bin/env python # -*- coding:utf-8 -*- import SocketServer import os class MyServer(SocketServer.BaseRequestHandler): def handle(self): print "--got connection from", self.client_address while True: data = self.request.recv(1024) print "Recv from cmd:%s" %(data) cmd_res = os.popen(data).read() print 'cmd_res:',len(cmd_res) #打印服务器发送数据长度 self.request.sendall(cmd_res) if __name__ == '__main__': server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer) server.serve_forever() #永久运行
客户端vim day8-23.py
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ('127.0.0.1',8009) sk = socket.socket() sk.connect(ip_port) sk.settimeout(5) while True: # data = sk.recv(1024) # print 'receive:',data inp = raw_input('please input:') sk.sendall(inp) while True: data = sk.recv(1024) print "---data---" # if len(data) == 0: if len(data) < 1024: print '---not data---' break print data if inp == 'exit': break sk.close()
客户端输入命令服务端把命令结果返回给客户端打印
修改代码让服务器在发送数据之前就把要发送的大小发送给客户端,客户端根据包的大小进行判断打印
服务端
#!/usr/bin/env python # -*- coding:utf-8 -*- import SocketServer import os class MyServer(SocketServer.BaseRequestHandler): def handle(self): print "--got connection from", self.client_address while True: data = self.request.recv(1024) print "Recv from cmd:%s" %(data) cmd_res = os.popen(data).read() print 'cmd_res:',len(cmd_res) #打印服务器发送数据长度 self.request.send( str(len(cmd_res)) ) #服务端要给客户端发多长数据 self.request.sendall(cmd_res) if __name__ == '__main__': server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer) server.serve_forever() #永久运行
客户端
#!/usr/bin/env python # -*- coding:utf-8 -*- import socket ip_port = ('127.0.0.1',8009) sk = socket.socket() sk.connect(ip_port) sk.settimeout(5) while True: # data = sk.recv(1024) # print 'receive:',data inp = raw_input('please input:') sk.sendall(inp) res_size = sk.recv(1024) print "goint to recv data size:",res_size,type(res_size)#打印将收到多少数据和数据类型 total_size = int(res_size) #共要收取的 received_size = 0 while True: data = sk.recv(1024) received_size += len(data) print "---data---" # if len(data) == 0: # if len(data) < 1024: if total_size == received_size: #如果收到的大小等于收到的大小代表收完了 print data print '---not data---' break print data if inp == 'exit': break sk.close()
PS:这样也会出现问题,这两个包可能在网络层集合成一个包发送给客户端导致客户端接收ValueError,这种现象叫连包,可以通过修改服务器端代码(比如两个包之间sleep1秒再发送下一个包),但是效率太低
可以通过在两条消息直接加代码解决
服务端
客户端回一条可以是一个空格
作业:客户端往服务端发文件,客户端可以从服务端下载文件,实现文件MD5验证和用户验证