摘要
本文部分内容来源于网络,个人收集整理,请勿传播
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
基本上,Socket 是任何一种计算机网络通讯中最基础的内容。例如当你在浏览器地址栏中输入 http://www.cnblogs.com/ 时,你会打开一个套接字,然后连接到 http://www.cnblogs.com/ 并读取响应的页面然后然后显示出来。而其他一些聊天客户端如 gtalk 和 skype 也是类似。任何网络通讯都是通过 Socket 来完成的。
Python 官方关于 Socket 的函数请看 http://docs.python.org/library/socket.html
本篇文章来源网络,原文地址
socket
socket和file的区别:
- file模块是针对某个指定文件进行【打开】【读写】【关闭】
- socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】
socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递。 我们知道网络通信都是基于 ip+port 方能定位到目标的具体机器上的具体服务,操作系统有0-65535个端口,每个端口都可以独立对外提供服务,如果 把一个公司比做一台电脑 ,那公司的总机号码就相当于ip地址, 每个员工的分机号就相当于端口, 你想找公司某个人,必须 先打电话到总机,然后再转分机 。
建立一个socket必须至少有2端, 一个服务端,一个客户端, 服务端被动等待并接收请求,客户端主动发起请求, 连接建立之后,双方可以互发数据。
1 | # server |
socket常用方法
sk.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)
1 | 获取要连接的对端主机地址 |
sk.bind(address)
1 | s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。 |
sk.listen(backlog)
1 | 开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。 |
sk.setblocking(bool)
1 | 是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。 |
sk.accept()
1 | 接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。 |
sk.connect(address)
1 | 连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 |
sk.connect_ex(address)
1 | 同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061 |
sk.close()
1 | 关闭套接字 |
sk.recv(bufsize[,flag])
1 | 接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。 |
sk.recvfrom(bufsize[.flag])
1 | 与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
sk.send(string[,flag])
1 | 将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。 |
sk.sendall(string[,flag])
1 | 将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。内部通过递归调用send,将所有内容发送出去。 |
sk.sendto(string[,flag],address)
1 | 将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。 |
sk.settimeout(timeout)
1 | 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s ) |
sk.getpeername()
1 | 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 |
sk.getsockname()
1 | 返回套接字自己的地址。通常是一个元组(ipaddr,port) |
sk.fileno()
1 | 套接字的文件描述符 |
一开始的例子
1 | import socket |
粘包
相邻多次发送的数据粘到一起了
TCP的几个例子
实现大数据传输ssh
server
1 | #_*_coding:utf-8_*_ |
client
1 | #_*_coding:utf-8_*_ |
机器人聊天
1 | # 服务端 |
上传文件
1 | # 服务端 |
udp 传输
1 | import socket |
web服务
1 | #!/usr/bin/env python |
socketserver
- BaseServer
- TCPServer
- UDPServer
SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。
- 自己创建一个请求处理类,并且这个类继承BaseRequestHandler,并且重写handler方法
- 实例化TCPServer,并且传递server ip和上面创建的请求处理类给这个TCPServer
- server.handle_requese() 只处理一个请求
- server.serve_forover() 处理多次一个请求,永远执行
- server_close()
基本的sockerserver
1 | import socketserver |
上面的代码,依然不能同时处理多个连接,擦,那我搞这个干嘛?别急,不是不能处理多并发,如果你想,你还要启用多线程,多线程我们现在还没讲,但你大体知道,有了多线程,就能同时让cpu干多件事了就行先。
so 只需要把下面这句
1 | server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) |
换成下面这个,就可以多并发了,这样,客户端每连进一个来,服务器端就会分配一个新的线程来处理这个客户端的请求
1 | server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) |
其他介绍
- fileno() 文件描述符
- handle_request() 处理单个请求
- serve_forever(poll_interval=0.5) 没0.5秒检测停止信号
- service_actions() 自动执行,关闭的时候处理一些事情,可以重写
- shutdown() 停止
- server_close()
- allow_reuse_address
ThreadingTCPServer
ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。
ThreadingTCPServer基础
使用ThreadingTCPServer:
- 创建一个继承自 SocketServer.BaseRequestHandler 的类
- 类中必须定义一个名称为 handle 的方法
- 启动ThreadingTCPServer
1 | # 服务端 |
1 | # 客户端 |
ThreadingTCPServer源码剖析
ThreadingTCPServer的类图关系如下:
内部调用流程为:
- 启动服务端程序
- 执行 TCPServer.init 方法,创建服务端Socket对象并绑定 IP 和 端口
- 执行 BaseServer.init 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给 self.RequestHandlerClass
- 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 …
- 当客户端连接到达服务器
- 执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
- 执行 ThreadingMixIn.process_request_thread 方法
- 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass() 即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)
SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。
IO多路复用
I/O(input/output),即输入/输出端口。每个设备都会有一个专用的I/O地址,用来处理自己的输入输出信息首先什么是I/O:
I/O分为磁盘io和网络io,这里说的是网络io
IO多路复用:
I/O多路复用指:通过一种机制,可以监视多个描述符(socket),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
Linux
Linux中的 select,poll,epoll 都是IO多路复用的机制。
Linux下网络I/O使用socket套接字来通信,普通I/O模型只能监听一个socket,而I/O多路复用可同时监听多个socket.
I/O多路复用避免阻塞在io上,原本为多进程或多线程来接收多个连接的消息变为单进程或单线程保存多个socket的状态后轮询处理.
Python
Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。
1 | Windows Python: |
对于select模块操作的方法
1 | 句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间) |
利用select监听终端操作实例
1 | import socket |
利用select实现伪同时处理多个Socket客户端请求
1 | # 服务端: |
利用select实现伪同时处理多个Socket客户端请求读写分离
1 | # 服务端: |