这篇文章主要为大家详细介绍了 python 之 Socket 网络编程, 具有一定的参考价值,感兴趣的小伙伴们可以参考一下
Python 是一种面向对象、解释型计算机程序设计语言,由 Guido van Rossum 于 1989 年底发明,第一个公开发行版发行于 1991 年。Python 语法简洁而清晰,具有丰富和强大的类库。它常被昵称为胶水语言,它能够把用其他语言制作的各种模块(尤其是 C/C++)很轻松地联结在一起。
什么是网络?
网络是由节点和连线构成,表示诸多对象及其相互联系。在数学上,网络是一种图,一般认为专指加权图。网络除了数学定义外,还有具体的物理含义,即网络是从某种相同类型的实际问题中抽象出来的模型。在计算机领域中,网络是信息传输、接收、共享的虚拟平台,通过它把各个点、面、体的信息联系到一起,从而实现这些资源的共享。网络是人类发展史来最重要的发明,提高了科技和人类社会的发展。
网络通信的三要素
IP 地址
用来表示一台独立的主机
特殊的 IP 地址 127.0.0.1 或称 localhost(表示本地回环地址,保留地址等),可用于本机测试
端口号
要将数据发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。为了方便称呼这些数字,则将这些数字称为端口
传输协议
传输控制协议
面向连接:传输之前需要建立连接
在连接过程中进行大量数据传输
通过三次握手方式连接,属于安全可靠连接
传输速率慢,效率低
用户传输协议
面向无连接:传输过程不需要建立连接即可传输
每个数据传输的大小都限制在 64K 以内
传输过程不可靠
传输速率快,效率高
SOCKET 网络编程
如简单的实现一个 web 小程序
- import socket
- def handle_request(client):
- buf = client.recv(1024)
- client.send(bytes("HTTP/1.1 200 OK\r\n\r\n",'utf8'))
- client.send(bytes("Hello, World",'utf8'))
- def main():
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.bind(('localhost', 8080))
- sock.listen(5)
- while True:
- connection, address = sock.accept()
- handle_request(connection)
- connection.close()
- if __name__ == '__main__':
- main()
Python 提供了两个级别访问的网络服务:
低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统 Socket 接口的全部方法。
高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
什么是 socket
Socket 又称 "套接字",应用程序通常通过 "套接字" 向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。
Socket() 函数:
socket.socket([family[, type[, proto]]])
参数
family: 套接字家族可以使 AF_UNIX 或者 AF_INET
type: 套接字类型可以根据是面向连接的还是非连接分为 SOCK_STREAM 或 SOCK_DGRAM
protocol: 一般不填默认为 0。
通信流程
- #######server端##########
- import socket
- sk = socket.socket()
- address = ('127.0.0.1', 8000)
- sk.bind(address)
- sk.listen(3)
- while True:
- conn, addr = sk.accept()
- while True:
- try:
- data = conn.recv(1024)
- print(str(data, 'utf8'))
- if not data:
- break
- inp = input(">>>")
- conn.send(bytes(inp, 'utf8'))
- except Exception:
- break
- conn.close()
- ##########Client端###########
- import socket
- sk = socket.socket()
- address = ('127.0.0.1', 8000)
- sk.connect(address)
- while True:
- inp = input(">>>")
- if inp == "exit":
- break
- sk.send(bytes(inp, 'utf8'))
- data = sk.recv(1024)
- print(str(data, 'utf8'))
- sk.close()
Socket 内建方法
s.bind() 绑定地址(host,port)到套接字, 在 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() 创建一个与该套接字相关连的文件
实例
- #########Server端##########
- import socket
- import subprocess
- sk = socket.socket()
- address = ('127.0.0.1', 8000)
- sk.bind(address)
- sk.listen(3)
- while True:
- conn, addr = sk.accept()
- while True:
- try:
- data = conn.recv(1024)
- except Exception:
- break
- if not data:
- break
- # print(str(data, 'utf8'))
- # data = str(data, 'utf8')#解码同decode
- obj = subprocess.Popen(data.decode('utf8'), shell=True, stdout=subprocess.PIPE)
- ssh_result = obj.stdout.read()
- result_len = bytes(str(len(ssh_result)),'utf8')
- conn.send(result_len)
- conn.send(ssh_result)
- conn.close()
- #########Client#########
- import socket
- sk = socket.socket()
- address = ('127.0.0.1', 8000)
- sk.connect(address)
- while True:
- inp = input(">>>")
- if inp == "exit":
- break
- sk.send(bytes(inp, 'utf8'))
- result_len = int(str(sk.recv(1024), 'utf8'))
- print(result_len)
- data = bytes()
- while len(data) != result_len:
- recv = sk.recv(1024)
- data += recv
- print(str(data, 'gbk'))
- sk.close()
文件上传
Server
- import socket
- import os
- sk = socket.socket()
- address = ('127.0.0.1', 8000)
- sk.bind(address)
- sk.listen(3)
- BASE_DIR = os.path.dirname(os.path.abspath(__file__))
- while True:
- conn, addr = sk.accept()
- while True:
- data = conn.recv(1024)
- cmd, file_name, file_size = str(data, 'utf8').split('|')
- path = os.path.join(BASE_DIR, 'model', file_name)
- file_size = int(file_size)
- f = open(path, 'ab')
- has_recv = 0
- while has_recv != file_size:
- data = conn.recv(1024)
- f.write(data)
- has_recv += len(data)
- f.close()
Client
- import socket
- import os
- sk = socket.socket()
- address = ('127.0.0.1', 8000)
- sk.connect(address)
- BASE_DIR = os.path.dirname(os.path.abspath(__file__))
- while True:
- inp = input(">>>>").strip()
- path = os.path.join(BASE_DIR, inp)
- file_name = os.path.basename(path)
- file_size = os.stat(path).st_size
- file_info = 'post|%s|%s' % (file_name, file_size)
- sk.sendall(bytes(file_info, 'utf8'))
- f = open(path, 'rb')
- has_sent = 0
- while has_sent != file_size:
- data = f.read(1024)
- sk.sendall(data)
- has_sent += len(data)
- f.close()
- print("上传成功")
socketserver
socketserver 模块简化了网络编程服务程序的任务,同时 SocketServer 模块也是 Python 标准库中很多服务器框架的基础。
学习它的最好办法是自己浏览一遍它的源码。
首先先看一下如何去运用
- import socketserver
- class MyServer(socketserver.BaseRequestHandler):
- def handle(self):
- print("服务端启动")
- while True:
- conn = self.request
- while True:
- data = conn.recv(1024)
- print(str(data, 'utf8'))
- inp = input(">>>>>")
- conn.sendall(bytes(inp, 'utf8'))
- conn.close()
- if __name__ == '__main__':
- server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyServer)
- server.serve_forever()
- server
- import socket
- sk = socket.socket()
- address = ('127.0.0.1', 8080)
- sk.connect(address)
- print("客户端启动")
- while True:
- inp = input(">>>>>")
- sk.sendall(bytes(inp, 'utf8'))
- if inp == "q":
- break
- data = sk.recv(1024)
- print(str(data, 'utf8'))
- sk.close()
此代码简单的实现了 server 端能同时和多个 client 聊天的功能。
我们在看源码前,首先要明确的是它分了几个类及每个类的功能作用等。
There are five classes in an inheritance diagram, four of which represent
synchronous servers of four types:
下面的就不一一详细说了,想要了解的更透彻,还是看一遍源码吧。
来源: http://www.phperz.com/article/17/0319/301487.html