×
新网 > 域名资讯 > 正文

Python网络编程

IP地址是指互联网协议地址(英语:Internet Protocol Address,又译为网际协议地址),是IP Address的缩写 IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异 IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”(也就是4个字节)

Python网络编程 一.网络初识 IP地址 IP地址是指互联网协议地址(英语:Internet Protocol Address,又译为网际协议地址),是IP Address的缩写 IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异 IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”(也就是4个字节) IP地址通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之间的十进制整数

例:点分十进IP地址(192.168.5.6),实际上是32位二进制数(01100100.00000100.00000101.00000110)

端口(port) ”端口”是英文port的意译,可以认为是设备与外界通讯交流的出口 osi模型

osi模型图

套接字(socket)

Socket是应用层与TCP/IP协议簇通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。 socket模型图

套接字家族 基于文件类型 套接字家族的名字:AF_UNIX

unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

基于网络类型 套接字家族的名字:AF_INET

(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

tcp协议、udp协议

TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。

TCP协议模型图

UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)

套接字(socket)初始使用 基于TCP协议的socket server端

import socket sk = socket.socket() sk.bind((\'127.0.0.1\', 8088)) sk.listen() conn, addr = sk.accept() while True: res = conn.recv(1024).decode(\'utf-8\') if res == \'bye\': break else: print(res) info = input(\'请输入:n>>>\').encode(\'utf-8\') conn.send(info) conn.close() sk.close() client端

import socket sk= socket.socket() sk.connect((\'127.0.0.1\', 8088)) while True: info = input(\'请输入:n>>>\').encode(\'utf-8\') sk.send(info) res = sk.recv(1024).decode(\'utf-8\') if res == \'bye\': break else: print(res) sk.close() 对于Address already in use的情况,可以加入socket配置重用ip和端口

import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket.socket() sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 sk.bind((\'127.0.0.1\',8898)) #把地址绑定到套接字 sk.listen() #监听链接 conn,addr = sk.accept() #接受客户端链接 ret = conn.recv(1024) #接收客户端信息 print(ret) #打印客户端信息 conn.send(b\'hi\') #向客户端发送信息 conn.close() #关闭客户端套接字 sk.close() #关闭服务器套接字(可选) 一个服务端与多个客户端交互示例

服务端

import socket sk = socket.socket() sk.bind((\'127.0.0.1\', 8088)) # listen([backlog])中的[backlog]参数代表服务端允许多少客户端连接到服务端,即阻塞队列长度,所以一共能与服务器连接的客户端共有backlog+1个 sk.listen(3) while True: conn, addr = sk.accept() # sk.accept()放在循环内部是为了每次与同一个客户端交互结束后会重新建立conn连接,以便下一个客户端连入 res = conn.recv(1024).decode(\'utf-8\') if res == \'bye\': break else: print(\'收到\',res) info = input(\'请输入>>>\').encode(\'utf-8\') conn.send(info) conn.close() # 同上面sk.accept()的作用 sk.close()

客户端(可以多个客户端逐个与服务端进行通讯)

import socket sk = socket.socket() sk.connect((\'127.0.0.1\', 8088)) while True: info = input(\'请输入>>>\').encode(\'utf-8\') if info == \'bye\': break else: sk.send(info) res = sk.recv(1024).decode(\'utf-8\') print(res) sk.close() 基于UDP协议的socket:不可靠 无连接,效率高 server端

import socket ip_port=(\'127.0.0.1\',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udp_server_client.bind(ip_port) while True: msg,addr=udp_server_client.recvfrom(BUFSIZE) print(msg,addr) udp_server_client.sendto(msg.upper(),addr) client端

import socket ip_port=(\'127.0.0.1\',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while True: msg=input(\'>>: \').strip() if not msg:continue udp_server_client.sendto(msg.encode(\'utf-8\'),ip_port) back_msg,addr=udp_server_client.recvfrom(BUFSIZE) print(back_msg.decode(\'utf-8\'),addr) 黏包

产生原因:粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

特点:只有TCP有粘包现象,UDP永远不会粘包

产生黏包的两种情况 发送数据时间间隔很短,数据了很小,会合到一起,产生粘包

服务端

from socket import * ip_port=(\'127.0.0.1\',8080) tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) conn,addr=tcp_socket_server.accept() data1=conn.recv(10) data2=conn.recv(10) print(\'----->\',data1.decode(\'utf-8\')) print(\'----->\',data2.decode(\'utf-8\')) conn.close()

客户端

import socket BUFSIZE=1024 ip_port=(\'127.0.0.1\',8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(ip_port) s.send(\'hello\'.encode(\'utf-8\')) s.send(\'feng\'.encode(\'utf-8\')) 接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

服务端

from socket import * ip_port=(\'127.0.0.1\',8080) tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) conn,addr=tcp_socket_server.accept() data1=conn.recv(2) #一次没有收完整 data2=conn.recv(10)#下次收的时候,会先取旧的数据,然后取新的 print(\'----->\',data1.decode(\'utf-8\')) print(\'----->\',data2.decode(\'utf-8\'))

客户端

import socket BUFSIZE=1024 ip_port=(\'127.0.0.1\',8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(ip_port) s.send(\'hello feng\'.encode(\'utf-8\')) 黏包的解决方案 原理:问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。

直接告知客户端发送数据的长度(Server端)

import socket, subprocess sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sk.bind((\'localhost\', 8070)) sk.listen() while True: conn, addr = sk.accept() print(\'客户端:\', addr) while True: msg = conn.recv(1024) if not msg: break res_temp = subprocess.Popen(msg.decode(\'utf-8\'), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) error = res_temp.stderr.read() if error: res = error else: res = res_temp.stdout.read() date_len = len(res) conn.send(str(date_len).encode(\'utf-8\')) data = conn.recv(1024).decode(\'utf-8\') if data == \'recv_ready\': conn.sendall() conn.close()

直接告知客户端发送数据的长度(Client端)

import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) res = s.connect_ex((\'127.0.0.1\', 8080)) while True: msg = input(\'>>: \').strip() if len(msg) == 0: continue if msg == \'quit\': break s.send(msg.encode(\'utf-8\')) length = int(s.recv(1024).decode(\'utf-8\')) s.send(\'recv_ready\'.encode(\'utf-8\')) send_size = 0 recv_size = 0 data = b\'\' while recv_size < length: data += s.recv(1024) recv_size += len(data) print(data.decode(\'utf-8\'))

借助struct模块打包定制报头(Server端)

import socket, struct, json import subprocess sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加 sk.bind((\'127.0.0.1\', 8080)) sk.listen(5) while True: conn, addr = sk.accept() while True: cmd = conn.recv(1024) if not cmd: break print(\'cmd: %s\' % cmd) res = subprocess.Popen(cmd.decode(\'utf-8\'), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err = res.stderr.read() print(err) if err: back_msg = err else: back_msg = res.stdout.read() conn.send(struct.pack(\'i\', len(back_msg))) # 先发back_msg的长度 conn.sendall(back_msg) # 在发真实的内容 conn.close()

借助struct模块打包定制报头(Server端)

import socket, time, struct s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) res = s.connect_ex((\'127.0.0.1\', 8080)) while True: msg = input(\'>>: \').strip() if len(msg) == 0: continue if msg == \'quit\': break s.send(msg.encode(\'utf-8\')) l = s.recv(4) x = struct.unpack(\'i\', l)[0] print(type(x), x) r_s = 0 data = b\'\' while r_s < x: r_d = s.recv(1024) data += r_d r_s += len(r_d) print(data.decode(\'gbk\')) # windows默认gbk编码 网络传输大文件的解决方案 读取固定字节内容,避免全部读进内存使得内存占用过大

Server端

import json import socket import struct sk = socket.socket() sk.bind((\'127.0.0.1\',8090)) sk.listen() buffer = 1024 conn,addr = sk.accept() # 接收 head_len = conn.recv(4) head_len = struct.unpack(\'i\',head_len)[0] json_head = conn.recv(head_len).decode(\'utf-8\') head = json.loads(json_head) filesize = head[\'filesize\'] with open(head[\'filename\'],\'wb\') as f: while filesize: print(filesize) if filesize >= buffer: content = conn.recv(buffer) f.write(content) filesize -= len(content) else: content = conn.recv(filesize) f.write(content) break conn.close() sk.close()

Client端

import os import json import struct import socket sk = socket.socket() sk.connect((\'127.0.0.1\',8090)) buffer = 2048 # 发送文件 head = {\'filepath\':r\'C:UsersygDesktop\', \'filename\':r\'02 python fullstack s9day32 基于udp的socket服务.mp4\', \'filesize\':None} file_path = os.path.join(head[\'filepath\'],head[\'filename\']) filesize = os.path.getsize(file_path) head[\'filesize\'] = filesize json_head = json.dumps(head) # 字典转成了字符串 bytes_head = json_head.encode(\'utf-8\') # 字符串转bytes # 计算head的长度 head_len = len(bytes_head) # 报头的长度 pack_len = struct.pack(\'i\',head_len) sk.send(pack_len) # 先发报头的长度 sk.send(bytes_head) # 再发送bytes类型的报头 with open(file_path,\'rb\') as f: while filesize: print(filesize) if filesize >= buffer: content = f.read(buffer) # 每次读出来的内容 sk.send(content) filesize -= len(content) else: content = f.read(filesize) sk.send(content) break sk.close() socket方法

服务端套接字函数

s.bind() 绑定(主机,端口号)到套接字 s.listen() 开始TCP监听 s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数

s.connect() 主动初始化TCP服务器连接 s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数

s.recv() 接收TCP数据 s.send() 发送TCP数据 s.sendall() 发送TCP数据 s.recvfrom() 接收UDP数据 s.sendto() 发送UDP数据 s.getpeername() 连接到当前套接字的远端的地址 s.getsockname() 当前套接字的地址 s.getsockopt() 返回指定套接字的参数 s.setsockopt() 设置指定套接字的参数 s.close() 关闭套接字

面向锁的套接字方法

s.setblocking() 设置套接字的阻塞与非阻塞模式 s.settimeout() 设置阻塞套接字操作的超时时间 s.gettimeout() 得到阻塞套接字操作的超时时间

面向文件的套接字的函数

s.fileno() 套接字的文件描述符 s.makefile() 创建一个与该套接字相关的文件 hmac模块—客户端链接合法性验证 hmac模块—客户端链接合法性验证

链接合法性验证—举例

服务端

# Server端 import os, socket, hmac secret_key = b\'secretkey\' sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) sk.bind((\'localhost\', 8889)) sk.listen() def check_client(conn): send_msg = os.urandom(32) conn.send(send_msg) h = hmac.new(secret_key, send_msg) digest = h.digest() client_digest = conn.recv(1024) return hmac.compare_digest(digest, client_digest) conn, addr = sk.accept() is_legal = check_client(conn) if is_legal: print(\'合法客户端!\') conn.send(b\'legal\') while True: recv_msg = conn.recv(1024).decode(\'utf-8\') if recv_msg.lower() == \'q\': conn.send(b\'q\') conn.close() break else: print(recv_msg) info = input(\'服务端>>>\').strip() if info: conn.send(info.encode(\'utf-8\')) else: print(\'非法客户端!\') conn.close() sk.close()

客户端

#Client端 import socket, hmac secret_key = b\'secretkey\' sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) sk.connect((\'localhost\', 8889)) msg = sk.recv(1024) h = hmac.new(secret_key, msg) digest = h.digest() sk.send(digest) is_legal = sk.recv(1024) if is_legal == b\'legal\': while True: info = input(\'客户端>>>\').strip() if info: sk.send(info.encode(\'utf-8\')) recv_msg = sk.recv(1024).decode(\'utf-8\') if recv_msg == \'q\': break else: print(recv_msg) sk.close() 利用socketserver模块处理服务端与多客户端交互问题

服务端

#Server端 import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): while True: recv_msg = self.request.recv(1024).decode(\'utf-8\') if recv_msg == \'q\': self.request.send(b\'q\') break else: print(recv_msg) info = input(\'服务端>>>\').strip() if info: self.request.send(info.encode(\'utf-8\')) if __name__ == \'__main__\': server = socketserver.ThreadingTCPServer((\'localhost\', 8889), MyServer) server.allow_reuse_address = True server.serve_forever()

客户端

#Client端 import socket sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) sk.connect((\'localhost\', 8889)) while True: info = input(\'客户端>>>\').strip() if info == \'\': continue elif info == \'q\': sk.send(info.encode(\'utf-8\')) break else: sk.send(info.encode(\'utf-8\')) recv_msg = sk.recv(1024).decode(\'utf-8\') print(recv_msg) sk.close()

  • 相关专题

免责声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,也不承认相关法律责任。如果您发现本社区中有涉嫌抄袭的内容,请发送邮件至:operations@xinnet.com进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。

免费咨询获取折扣

Loading