Python 网络编程(二)
Python 网络编程
上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端、服务器程序,本篇博客主要对socket编程进行更深入的讲解
一、简化版ssh实现
这是一个极其简单的仿ssh的socket程序,实现的功能为客户端发送命令,服务端接收到客户端的命令,然后在服务器上通过subrocess模块执行命令,如果命令执行有误,输出内容为空,则返回"command error"的语句给客户端,否则将命令执行的结果返回给客户端
服务端
|
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
|
import socketimport subprocessip = '0.0.0.0'port = 8005sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.bind((ip, port))sock.listen(5)while True: conn, addr = sock.accept() while True: try: cmd = str(conn.recv(1024), encoding="utf-8") if cmd == "exit": conn.close() break p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) send_data = p.stdout.read() if len(send_data) == 0: send_data = "command error" else: send_data = str(send_data, encoding="gbk") send_data = bytes(send_data, encoding="utf-8") conn.sendall(send_data) except Exception as e: print (e) break |
客户端
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import socketip = '127.0.0.1'port = 8005sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((ip, port))while True: cmd = input("client>").strip() if cmd == "": continue else: sock.sendall(bytes(cmd, encoding="utf-8")) if cmd == "exit": break recv_data = str(sock.recv(1024), encoding="utf-8") print (recv_data)sock.close() |
上面的程序有个问题,如果命令执行的结果比较长,那么客户端发送下一个命令过去之后仍然返回上一个命令没接收完的结果,这样的现象我们称作粘包的现象。我们知道TCP传输的是数据流,发送数据时会等缓冲区满了然后再发送,或者等待要发送的时间超时了再发送,几个包组合在一起发送可以提高发送效率,此时也造成了粘包现象的产生,目标机器一次性接收几个包的数据,可能导致本次请求接收多余的数据。粘包还有一种情况,就是本次程序里面出现的情况,因为服务端要发送的数据大于1024,导致客户端无法一次性接收完数据,虽然我们可以修改接收的大小,但是治标不治本。解决方法有我们在发送具体数据前,先将数据的大小发送给客户端,客户端做好接收准备并告诉给服务端,服务端一次性发送数据之后,客户端根据服务器端发送数据的大小进行循环接收,直到数据接收完毕。
改进版
服务端
|
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
|
import socketimport subprocessip = '0.0.0.0'port = 8005sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.bind((ip, port))sock.listen(5)while True: conn, addr = sock.accept() while True: try: cmd = str(conn.recv(1024), encoding="utf-8") if cmd == "exit": conn.close() break p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) send_data = p.stdout.read() if len(send_data) == 0: send_data = "command error" else: send_data = str(send_data, encoding="gbk") send_data = bytes(send_data, encoding="utf-8") data_len = len(send_data) ready_tag = "Ready|%d" %data_len conn.sendall(bytes(ready_tag, encoding="utf-8")) start_data = str(conn.recv(1024), encoding="utf-8") if start_data.startswith("Start"): conn.sendall(send_data) except Exception as e: print (e) break |
客户端
|
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
|
import socketip = '127.0.0.1'port = 8005sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((ip, port))while True: cmd = input("client>").strip() if cmd == "": continue else: sock.sendall(bytes(cmd, encoding="utf-8")) if cmd == "exit": break ready_data = str(sock.recv(1024), encoding="utf-8") if ready_data.startswith("Ready"): msg_size = int(ready_data.split("|")[-1]) start_tag = "Start" sock.sendall(bytes(start_tag, encoding="utf-8")) msg = "" recv_size = 0 while recv_size < msg_size: recv_data = sock.recv(1024) recv_size += len(recv_data) msg += str(recv_data, encoding="utf-8") print (msg)sock.close() |
二、IO多路复用
IO多路复用指通过一种机制,可以监视多个描述符,一旦某个描述符就绪,就能够通过程序进行相应的读写操作
Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用
对于select:
|
1
2
3
4
5
6
7
8
9
10
11
|
句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间) 参数: 可接受四个参数(前三个必须)返回值:三个列表 select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化 当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。 |
服务端
|
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
|
import selectip = "0.0.0.0"port = 8003sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.bind((ip, port))sock.listen(5)inputs = [sock]outputs = []message_queues = {}while True: read_list, write_list, error_list = select.select(inputs, outputs, inputs, 1) print ("inputs: %d outputs: %d read_list: %d write_list: %d" %(len(inputs), len(outputs), len(read_list), len(write_list))) for r in read_list: if r is sock: conn, addr = r.accept() conn.sendall(bytes("welcome to here", encoding="utf-8")) inputs.append(conn) message_queues[conn] = [] else: data = str(r.recv(1024), encoding="utf-8") if data == "exit": if r in outputs: outputs.remove(r) inputs.remove(r) r.close() del message_queues[r] else: message_queues[r].append(data) if r not in outputs: outputs.append(r) for w in write_list: data = message_queues[w].pop() w.sendall(bytes(data, encoding="utf-8")) if len(message_queues[w]) == 0: outputs.remove(w) for e in error_list: inputs.remove(e) if e in outputs: outputs.remove(e) e.close() del message_queues[e] |
客户端
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import socketip = "127.0.0.1"port = 8003sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((ip, port))while True: data = str(sock.recv(1024), encoding="utf-8") print ("server>%s" %data) send_data = input("client>").strip() if not send_data: send_data = "empty" sock.sendall(bytes(send_data, encoding="utf-8")) if send_data == "exit": exit()sock.close() |
三、SocketServer
SocketServer内部使用 IO多路复用 以及多线程和多进程,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求

SocketServer有以下几种类型:
socketserver.TCPServer
socketserver.UDPServer
socketserver.UnixStreamServer
socketserver.UnixDatagramServer
每种类型都可以通过多线程或者多进程的方式处理多个客户的请求,这里介绍ThreadingTCPServer的使用方式:
创建一个继承自 SocketServer.BaseRequestHandler 的类
类中必须定义一个名称为 handle 的方法
启动ThreadingTCPServer
服务端
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import socketserverclass Myserver(socketserver.BaseRequestHandler): def handle(self): request = self.request Flag = True while Flag: data = str(request.recv(1024), encoding="utf-8") if data == "exit": request.close() Flag = False else: request.sendall(bytes(data, encoding="utf-8"))if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('0.0.0.0', 8888), Myserver) server.serve_forever() |
客户端
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import socketip = '127.0.0.1'port = 8888sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sk.connect((ip, port))while True: send_data = input("client>") sk.sendall(bytes(send_data, encoding="utf-8")) if send_data == "exit": sk.close() break recv_data = str(sk.recv(1024), encoding="utf-8") print("server>%s" % recv_data) |
Python 网络编程(二)的更多相关文章
- Python学习笔记【第十四篇】:Python网络编程二黏包问题、socketserver、验证合法性
TCP/IP网络通讯粘包问题 案例:模拟执行shell命令,服务器返回相应的类容.发送指令的客户端容错率暂无考虑,按照正确的指令发送即可. 服务端代码 # -*- coding: utf- -*- # ...
- python 网络编程:socket(二)
上节地址:Python网络编程:socket 一.send和sendall区别 send,sendall ret = send('safagsgdsegsdgew') #send 发送 ...
- Python 网络编程(一)
Python 网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. ...
- 28、Python网络编程
一.基于TCP协议的socket套接字编程 1.套接字工作流程 先从服务器端说起.服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客 ...
- python 网络编程:socket
在学习socket之前,我们先复习下相关的网络知识. OSI七层模型:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层.OSI七层模型是由国际标准化组织ISO定义的网络的基本结构,不仅包括一 ...
- 图解Python网络编程
返回目录 本篇索引 (1)基本原理 (2)socket模块 (3)select模块 (4)asyncore模块 (5)asynchat模块 (6)socketserver模块 (1)基本原理 本篇指的 ...
- Python学习(22)python网络编程
Python 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的 ...
- Day07 - Python 网络编程 Socket
1. Python 网络编程 Python 提供了两个级别访问网络服务: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...
- python网络编程-01
python网络编程 1.socket模块介绍 ①在网络编程中的一个基本组件就是套接字(socket),socket是两个程序之间的“信息通道”. ②套接字包括两个部分:服务器套接字.客户机套接字 ③ ...
随机推荐
- 硬件初始化,nand flash固化操作,系统启动简单流程
2015.3.27星期五 晴 链接脚本定义代码的排放顺序 硬件系统初始化:一:arm核初始化:(里面有指令)初始化ARM核的时候需要看arm核的手册指令:1.异常向量(最起码有个复位异常,初始化模式- ...
- hdu1000,hdu1001,hdu1002,hdu1003
hdu1000 仅仅是为了纪念 #include <cstdio> int main() { int a,b; while (scanf("%d%d",&a,& ...
- 软件测试第三次作业——7.使用下面方法printPrimes()完成后面的问题(a)~(f)
(a)控制流图如下: (b)令MAXPRIMES=4,会出现越界错误. (c)令n=1,不会经过while循环体. (d)节点覆盖:{1,2,3,4,5,6,7,8,9,10,11,12,13,14, ...
- thinkphp 介绍
一.ThinkPHP的介绍 MVC M - Model 模型 工作:负责数据的操作 V - View 视图(模板) 工作:负责 ...
- Javascript 右移0位的作用
Javascript 中右移0位可以用来快速去掉小数,关于位移运算的定义: 右移运算是将一个二进制位的操作数按指定移动的位数向右移动,移出位被丢弃,左边移出的空位或者一律补0,或者补符号位. 实际看下 ...
- tomcat端口号被占用或者修改端口号的解决方法
一)修改端口号: 在tomcat文件中找到conf里面的server.xml 在tomcat解压后的文件中按照下图操作
- FTP弱口令猜解【python脚本】
ftp弱口令猜解 python脚本: #! /usr/bin/env python # _*_ coding:utf-8 _*_ import ftplib,time username_list=[' ...
- 模拟ajax的同异步
今天突然想到那只在app中,如果请求数据时用的是app提供的接口,如果该接口没有同异步的话,怎么办. 所以就捣腾了下. 模拟ajax同异步. var VshopDataApi = { queryArr ...
- 从DataTable获取Json数据
public string GetJson(DataTable dt){ JavaScriptSerializer jss=new JavaScriptSerializer(); jss.MaxJso ...
- LINQ 客户端生成自增列
var testQuery = (from item in TestInfo.GroupBy(t=>t.TestName) select new { TestCode = item.Min(g= ...