Python-08-Socket
1. Python 网络编程
Python 提供了两个级别访问的网络服务:
- 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
- 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
什么是 Socket?
Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。
2. socket()函数
Python 中,我们用 socket()函数来创建套接字,语法格式如下:
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
参数
family(地址簇):
- AF_INET(IPv4)
- AF_INET6(IPv6)
- AF_UNIX(unix本机进程间通信)
type(套接字类型):
- SOCK_STREAM(面向连接的TCP协议)
- SOCK_DGRAM(非连接的UDP协议)
- SOCK_RAW (原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。)
- SOCK_RDM (是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。)
- SOCK_SEQPACKET(可靠的连续数据包服务,不过目前已废弃)
protocol: 一般不填默认为0
Socket 对象(内建)方法
| 函数 | 描述 |
|---|---|
| 服务器端套接字 | |
| s.bind() | 绑定地址(host,port)到套接字,address地址的格式取决于地址族。 在AF_INET下,以元组(host,port)的形式表示地址。 |
| s.listen() | 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 |
| s.accept() | 被动接受TCP客户端连接,(阻塞式)等待连接的到来 |
| 客户端套接字 | |
| s.connect() | 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 |
| s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 |
| 公共用途的套接字函数 | |
| s.recv() | 接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。 |
| s.send() | 发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。 |
| s.sendall() | 完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 |
| s.recvform() | 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
| s.sendto() | 发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。 |
| s.close() | 关闭套接字 |
| s.getpeername() | 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 |
| s.getsockname() | 返回套接字自己的地址。通常是一个元组(ipaddr,port) |
| s.setsockopt(level,optname,value) | 设置给定套接字选项的值。 |
| s.getsockopt(level,optname[.buflen]) | 返回套接字选项的值。 |
| s.settimeout(timeout) | 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect()) |
| s.gettimeout() | 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。 |
| s.fileno() | 返回套接字的文件描述符。 |
| s.setblocking(flag) | 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。 |
| s.makefile() | 创建一个与该套接字相关连的文件。 |
3. 基本Socket实例
服务端
我们使用 socket 模块的 socket 函数来创建一个 socket 对象。socket 对象可以通过调用其他函数来设置一个 socket 服务。
现在我们可以通过调用 bind(hostname, port) 函数来指定服务的 port(端口)。
完整代码如下:
# SocketClient.py
import socket # 导入 socket 模块
server = socket.socket() # 创建 socket 对象
host = socket.gethostname() # 获取本地主机名
port = 9999 # 设置端口
server.bind((host,port)) # 绑定ip port
server.listen(5) # 开始监听,等待客户端连接
while True: # 第1层loop,实现Socket多连接
conn,addr = server.accept() # 接受并建立与客户端的连接,程序在此处开始阻塞,直到有客户端连接进来...
while True: # 第2层loop
data = conn.recv(1024) # 收消息,每次1k
if not data: # 判断收到消息是否为空,为空就断开回到第一层loop,否则将会进入死循环。
print("客户端已断开!")
break
print("收到消息:",data)
conn.send(data.upper()) # 将消息转为大写后发回
server.close()
客户端
接下来我们写一个简单的客户端实例连接到以上创建的服务。端口号为 9999。
socket.connect(hosname, port ) 方法打开一个 TCP 连接到主机为 hostname 端口为 port 的服务商。连接后我们就可以从服务端后期数据,记住,操作完成后需要关闭连接。
完整代码如下:
# SocketClient.py
import socket # 导入 socket 模块
client = socket.socket() # 创建 socket 对象
host = socket.gethostname() # 获取本地主机名
port = 9999 # 设置端口号
client.connect((host,port)) # 连接到 Server端
while True: # 实现多次交互
msg = input(">>:").strip()
if len(msg) == 0:continue # 判断数据为空的时候继续loop
client.send(msg.encode("utf-8")) # 发送数据
data = client.recv(1024) # 接收数据
print("来自服务器:",data)
client.close() # 关闭连接
数据多次交互实现图示

4. 通过socket实现简单的ssh
光只是简单的发消息、收消息没意思,干点正事,可以做一个简单版的ssh,就是客户端连接上服务器后,让服务器执行命令,并返回结果给客户端。
服务端
# Server.py
import socket
import os
server = socket.socket() # 获得socket实例
#server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
host = socket.gethostname() # 获取本地主机名
port = 9998 # 设置端口号
server.bind((host,port)) # 绑定ip port
server.listen() # 开始监听
while True: #第一层loop
print("等待客户端的连接...")
conn,addr = server.accept() #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
print("新连接:",addr )
while True:
data = conn.recv(1024)
if not data:
print("客户端断开了...")
break #这里断开就会再次回到第一次外层的loop
print("收到命令:",data)
#res = os.popen(data.decode()).read() #py3 里socket发送的只有bytes,os.popen又只能接受str,所以要decode一下
res = subprocess.Popen(data,shell=True,stdout=subprocess.PIPE).stdout.read() #跟上面那条命令的效果是一样的
if len(res) == 0:
res = "cmd exec success,has not output!".encode("utf-8")
conn.send(str(len(res.encode())).encode("utf-8")) #发送数据之前,先告诉客户端要发多少数据给它
print("等待客户ack应答...")
client_final_ack = conn.recv(1024) #等待客户端响应,防止粘包
print("客户应答:",client_final_ack.decode())
print(type(res))
conn.sendall(res) #发送端也有最大数据量限制,所以这里用sendall,相当于重复循环调用conn.send,直至数据发送完毕
server.close()
客户端
# Client.py
import socket
client = socket.socket()
host = socket.gethostname() # 获取本地主机名
port = 9998 # 设置端口号
client.connect((host,port)) # 连接到 Server端
while True:
msg = input(">>:").strip()
if len(msg) == 0:continue
client.send( msg.encode("utf-8") )
res_return_size = client.recv(1024) #接收这条命令执行结果的大小
print("getting cmd result , ", res_return_size)
total_rece_size = int(res_return_size)
print("total size:",res_return_size)
client.send("准备好接收了,发吧loser".encode("utf-8")) # 防止粘包,确认一下。
received_size = 0 #已接收到的数据
cmd_res = b''
f = open("test_copy.html","wb")#把接收到的结果存下来,一会看看收到的数据 对不对
while received_size != total_rece_size: #代表还没收完
data = client.recv(1024)
received_size += len(data) #为什么不是直接1024,还判断len干嘛,注意,实际收到的data有可能比1024少
cmd_res += data
else:
print("数据收完了",received_size)
#print(cmd_res.decode())
f.write(cmd_res) #把接收到的结果存下来,一会看看收到的数据 对不对
#print(data.decode()) #命令执行结果
client.close()
这里引入了一个重要的概念:粘包, 即服务器端你调用时send 2次,但你send调用时,数据其实并没有立刻被发送给客户端,而是放到了系统的socket发送缓冲区里,等缓冲区满了、或者数据等待超时了,数据才会被send到客户端,这样就把好几次的小数据拼成一个大数据,统一发送到客户端了,这么做的目地是为了提高io利用效率,一次性发送总比连发好几次效率高嘛。
但也带来一个问题,就是“粘包”,即2次或多次的数据粘在了一起统一发送了。
那么如何解决粘包的问题呢?
- time.sleep(0.5),经多次测试,让服务器程序sleep 至少0.5就会造成缓冲区超时。这种方法比较low,数据实时性差,生产环境肯定不能这么玩!
- 通过上面的代码你应该知道了,不用sleep,服务器端每发送一个数据给客户端,就立刻等待客户端进行回应,即调用 conn.recv(1024), 由于recv在接收不到数据时是阻塞的,这样就会造成,服务器端接收不到客户端的响应,就不会执行后面的conn.sendall(命令结果)的指令,收到客户端响应后,再发送命令结果时,缓冲区就已经被清空了,因为上一次的数据已经被强制发到客户端了。
5. SocketServer模块
SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。

ThreadingTCPServer
ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 线程,该线程用来和客户端进行交互。
1. ThreadingTCPServer基础
使用ThreadingTCPServer:
- 创建一个继承自 SocketServer.BaseRequestHandler 的类
- 类中必须定义一个名称为 handle 的方法
- 启动ThreadingTCPServer
SocketServer实现服务器
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import SocketServer
class MyServer(SocketServer.BaseRequestHandler):
def handle(self):
# print self.request,self.client_address,self.server
conn = self.request
conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.'.encode("utf-8"))
Flag = True
while Flag:
data = conn.recv(1024)
if data == 'exit':
Flag = False
elif data == '0':
conn.sendall('通过可能会被录音.balabala一大推'.encode("utf-8"))
else:
conn.sendall('请重新输入.'.encode("utf-8"))
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.encode("utf-8"))
if inp == 'exit':
break
sk.close()
2.ThreadingTCPServer源码剖析
参考以下博客:
Alex、
Mr.Seven、
RUNOOB、
廖雪峰
谢谢!
Python-08-Socket的更多相关文章
- 转:Python 的 Socket 编程教程
这是用来快速学习 Python Socket 套接字编程的指南和教程.Python 的 Socket 编程跟 C 语言很像. Python 官方关于 Socket 的函数请看 http://docs. ...
- 进击的Python【第十章】:Python的socket高级应用(多进程,协程与异步)
Python的socket高级应用(多进程,协程与异步)
- Python底层socket库
Python底层socket库将Unix关于网络通信的系统调用对象化处理,是底层函数的高级封装,socket()函数返回一个套接字,它的方法实现了各种套接字系统调用.read与write与Python ...
- Python Udp Socket
socket(套接字),传输层通信的端点,由IP和端口号组成(IP,Port),可以通过socket精确地找到服务器上的进程并与之通信 python2.6实现,基于AF_INET(网络套接字) 类型S ...
- Python Tcp Socket
socket(套接字),传输层通信的端点,由IP和端口号组成(IP,Port),可以通过socket精确地找到服务器上的进程并与之通信 python2.6实现,基于AF_INET(网络套接字) 类型S ...
- Python 之socket的应用
本节主要讲解socket编程的有关知识点,顺便也会讲解一些其它的关联性知识: 一.概述(socket.socketserver): python对于socket编程,提供了两个模块,分别是socket ...
- python tcp socket 多线程
不多说,直接上代码 client.py #!/usr/bin/python import socket,sys,string host="localhost" port=8000 ...
- 老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具
老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具 poptest是业内唯一的测试开发工程师培训机构,测试开发工程师主要是为测试服务开发测试工具,在工作中要求你做网络级别的安全 ...
- 操作系统底层原理与Python中socket解读
目录 操作系统底层原理 网络通信原理 网络基础架构 局域网与交换机/网络常见术语 OSI七层协议 TCP/IP五层模型讲解 Python中Socket模块解读 TCP协议和UDP协议 操作系统底层原理 ...
- python中socket模块详解
socket模块简介 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.socket通常被叫做"套接字",用于描述IP地址和端口,是一个通信 ...
随机推荐
- php实现设计模式之 中介者模式
<?php /* * 中介者模式:用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用从而使其耦合松散,而且可以独立地改变它们之间的交互 */ /* * 以一个同学qq群为例说明, ...
- jquery插件图片延时加载实例详解
效果预览:http://keleyi.com/keleyi/phtml/image/index.htm 使用方法:1.导入JS插件 <script src="http://keleyi ...
- 谷歌浏览器下载地址 chrome最新版本 百度云地址
每次下载更新谷歌浏览器是一件很蛋疼的事情.百度搜索"谷歌浏览器下载地址",居然有很多骗子网站,相信有很多不知所以的人中招了.收集了一些chrome的安装包,放在了百度云里面(打不开 ...
- iOS UITabBarController的使用
UITabBarController 和 UINavigationController 几乎是iOS APP的标配. UITabBarController分栏(标签栏)控制器, 和UINavigati ...
- SharePoint 2013 搭建app本地开发环境
使用SharePoint App,如果要通过应用程序目录分发 SharePoint 相关应用程序,如具有完全控制权限的 SharePoint 相关应用程序(无法部署到 Office 365 网站),则 ...
- SharePoint Permission Extension
SharePoint Permission Extension 项目很久没维护了,也没有迁移到sp2013上(貌似只要把2013的Form的RenderMode设置为Server后也是可以用的). 在 ...
- Mac ping localhost 地址变化
title: Mac ping localhost 地址变化date: 2016-1-15 16:21:55categories: IOS tags: mac 小小程序猿我的博客:http://day ...
- Android入门开发时注意的两个问题
android开发中的问题: . 开发应用时要访问网络往往会忘记添加网络权限 <uses-permission android:name="android.permission.INT ...
- Android Studio 运行出现 Error:Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'.
转载请标明出处: http://www.cnblogs.com/why168888/p/5978381.html 本文出自:[Edwin博客园] 我引用compile 'com.squareup.re ...
- iOS程序逆向Mac下常用工具——Reveal、HopperDisassemble、IDA
原文在此 一.Reveal 1 一般使用 Reveal是ITTY BITTY发布的UI分析工具,可以很直观的查看App的UI布局.如下图所示: Reveal是需要付费的,需要89美元, ...