python基础——网络编程

news/2024/10/15 11:16:12/

前言

互联网时代,现在基本上所有的程序都是网络程序,很少有单机版的程序了。网络编程就是如何在程序中实现两台计算机的通信。
Python语言中,提供了大量的内置模块和第三方模块用于支持各种网络访问,而且Python语言在网络通信方面的优点特别突出,远远
领先其他语言。 

通过本章,可以学到:
1 了解TCP和UDP
2 掌握编写UDP Socket客户端应用
3 掌握编写UDP Socket服务器端应用
4 掌握编写TCP Socket客户端应用
5 掌握编写TCP Socket服务器端应用

一、大白话OSI七层协议

互联网的本质就是一系列的网络协议,这个协议就叫OSI协议(一系列协议),按照功能不同,分工不同,人为的分层七层。实际上这个七层是不存在的。没有这七层的概念,只是人为的划分而已。区分出来的目的只是让你明白哪一层是干什么用的。
每一层都运行不同的协议。协议是干什么的,协议就是标准。

实际上还有人把它划成五层、四层。

七层划分为:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。

五层划分为:应用层、传输层、网络层、数据链路层、物理层。

四层划分为:应用层、传输层、网络层、网络接口层。

二、TCP和UDP协议区别 

TCP 和 UDP 的优缺点无法简单地、绝对地去做比较:TCP 用于在传输层有必要实现可靠传输的情况;UDP 主要用于那些对高速传输
和实时性有较高要求的通信或广播通信。TCP 和 UDP 应该根据应用的目的按需使用。

  • TCP:适用于对传输可靠性和顺序性要求高的应用场景,保证数据不丢失、不乱序,并提供流量控制和拥塞控制。
  • UDP:适用于实时性要求高的应用,数据不必完全可靠传输,能容忍部分丢包,传输效率高。

2.1TCP 和 UDP 的主要区别

2.2 TCP协议特点

  • 面向连接:TCP 是面向连接的协议,通信双方在传输数据前必须建立一个连接,这个过程称为“三次握手”。
  • 可靠传输:TCP 提供可靠的数据传输服务,数据包的丢失、重复或顺序错误都会被检测并进行纠正。TCP使用确认机制,接收方必须确认收到的数据包。
  • 数据流控制:TCP 具有流量控制和拥塞控制机制,通过滑动窗口和慢启动等算法,确保网络资源不会被超载。
  • 有序传输:TCP 保证接收方按顺序接收到数据包,即使数据包乱序到达,TCP 也会重新排序。
  • 报文段:TCP 将数据划分为大小适当的报文段,每个报文段都包含一个序列号,用于顺序重组数据。
  • 应用场景:适用于对数据传输可靠性要求较高的场景,如文件传输(FTP)、电子邮件(SMTP)、网页浏览(HTTP/HTTPS)等。

2. 3 UDP协议特点

  • 无连接:UDP 是无连接的协议,发送数据前不需要建立连接,也不会维持连接状态。发送方可以直接将数据报发送给接收方。
  • 不可靠传输:UDP 不保证数据能可靠地送达接收方,不进行数据重传、丢包检测、重排序等操作。数据包可能会丢失、重复或乱序到达。
  • 没有流量控制:UDP 没有流量控制和拥塞控制机制,适用于对实时性要求高、对丢包敏感度较低的应用。
  • 数据报文:UDP 以数据报的形式发送,每个数据报是独立的,报文的大小不能超过 64KB。
  • 应用场景:适用于对实时性要求较高,传输效率重要、容忍一定丢包的应用场景,如视频会议、语音通信(VoIP)、实时游戏、DNS查询等。

 2.4 TCP建立连接的三次握手

 TCP是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接。 一个TCP连接必须要经过三次“对话”才能建立起
来,其中的过程非常复杂, 只简单的描述下这三次对话的简单过
程:
1)主机A向主机B发出连接请求:“我想给你发数据,可以吗?”,这是第一次对话;
2)主机B向主机A发送同意连接和要求同步 (同步就是两台主机一个在发送,一个在接收,协调工作)的数据包 :“可以,你什么时候发?”,这是第二次对话;
3)主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”, 这是第三次握手。三次“对话”的目的是使数据包的发送和接收同步, 经过三次“对话”
之后,主机A才向主机B正式发送数据。

三次“对话”的目的是使数据包的发送和接收同步, 经过三次“对话”之后,主机A才向主机B正式发送数据。

 第一步,客户端发送一个包含SYN即同步(Synchronize)标志的TCP报文,SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号。
第二步,服务器在收到客户端的SYN报文后,将返回一个SYN+ACK的报文,表示客户端的请求被接受,同时TCP序号被加一,ACK即确认(Acknowledgement)
第三步,客户端也返回一个确认报文ACK给服务器端,同样TCP序列号被加一,到此一个TCP连接完成。然后才开始通信的第二步:数据处理。

为什么TCP协议有三次握手,而UDP协议没有?

因为三次握手的目的是在client端和server端建立可靠的连接。保证双方发送的数据对方都能接受到,这也是TCP协议的被称为可靠的数据传输协议的原因。而UDP就不一样,UDP不提供可靠的传输模式,发送端并不需要得到接收端的状态,因此UDP协议就用不着使用三次握手。

2.5 TCP断开连接的四次挥手

 TCP建立连接要进行3次握手,而断开连接要进行4次:
第一次: 当主机A完成数据传输后,将控制位FIN置1,提出停止TCP连接的请求 ;
第二次: 主机B收到FIN后对其作出响应,确认这一方向上的TCP连接将关闭,将ACK置1;
第三次: 由B 端再提出反方向的关闭请求,将FIN置1 ;
第四次: 主机A对主机B的请求进行确认,将ACK置1,双方向的关闭结束.。
 由TCP的三次握手和四次断开可以看出,TCP使用面向连接的通信方式, 大大提高了数据通信的可靠性,使发送数据端和接收端在数据
正式传输前就有了交互, 为数据正式传输打下了可靠的基础。

三、基于TCP协议的socket套接字编程

3.1 什么是scoket?

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

所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

3.2 套接字发展史及分类

套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。

3.2.1 基于文件类型的套接字家族

套接字家族的名字:AF_UNIX

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

3.2.2 基于网络类型的套接字家族

套接字家族的名字:AF_INET

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

3.3 套接字工作流程

一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。 生活中的场景就解释了这工作原理。

先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束,使用以下Python代码实现: 

import socket# socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0
socket.socket(socket_family, socket_type, protocal=0)# 获取tcp/ip套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 获取udp/ip套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能大幅减短我们的代码
tcpSock = socket(AF_INET, SOCK_STREAM)

 3.3.1服务端套接字函数

3.3.2客户端套接字函数

3.3.3公共用途的套接字函数 

 3.3.4面向锁的套接字方法

 3.3.5面向文件的套接字的函数

3.4 基于TCP协议的套接字编程(循环)

3.4.1服务端

import socket
#1、买手机
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  #tcp称为流式协议,udp称为数据报协议SOCK_DGRAM
# print(phone)
#2、插入/绑定手机卡
# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1', 8080))
#3、开机
phone.listen(5)  # 半连接池,限制的是请求数
#4、等待电话连接
print('start....')
while True:  # 连接循环conn, client_addr = phone.accept()  #(三次握手建立的双向连接,(客户端的ip,端口))# print(conn)print('已经有一个连接建立成功', client_addr)#5、通信:收\发消息while True:  # 通信循环try:print('服务端正在收数据...')data = conn.recv(1024)  #最大接收的字节数,没有数据会在原地一直等待收,即发送者发送的数据量必须>0bytes# print('===>')if len(data) == 0: break  #在客户端单方面断开连接,服务端才会出现收空数据的情况print('来自客户端的数据', data)conn.send(data.upper())except ConnectionResetError:break#6、挂掉电话连接conn.close()
#7、关机
phone.close()

3.4.2客户端

import socket
#1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# print(phone)
#2、拨电话
phone.connect(('127.0.0.1', 8080))  # 指定服务端ip和端口
#3、通信:发\收消息
while True:  # 通信循环msg = input('>>: ').strip()  #msg=''if len(msg) == 0: continuephone.send(msg.encode('utf-8'))# print('has send----->')data = phone.recv(1024)# print('has recv----->')print(data)#4、关闭
phone.close()

3.5 模拟ssh远程执行命令

 3.5.1 服务端

from socket import *
import subprocess
server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5)
print('start...')
while True:conn, client_addr = server.accept()while True:print('from client:', client_addr)cmd = conn.recv(1024)if len(cmd) == 0: breakprint('cmd:', cmd)obj = subprocess.Popen(cmd.decode('utf8'),  # 输入的cmd命令shell=True,  # 通过shell运行stderr=subprocess.PIPE,  # 把错误输出放入管道,以便打印stdout=subprocess.PIPE)  # 把正确输出放入管道,以便打印stdout = obj.stdout.read()  # 打印正确输出stderr = obj.stderr.read()  # 打印错误输出conn.send(stdout)conn.send(stderr)conn.close()
server.close()
  • conn.recv(1024):从客户端接收最多 1024 字节的数据。recv() 方法会阻塞,直到接收到数据。接收的是客户端发来的命令。

    • 如果接收到的 cmd 长度为 0,则表示客户端关闭连接,跳出循环。
  • subprocess.Popen():创建一个子进程来执行客户端发来的命令。

    • cmd.decode('utf8'):将接收到的命令解码成字符串,recv() 接收到的是字节数据。
    • shell=True:表示通过 shell 执行命令。
    • stderr=subprocess.PIPEstdout=subprocess.PIPE:将标准输出和标准错误的输出重定向到管道,以便稍后读取。
  • stdout = obj.stdout.read():读取子进程标准输出的内容。

  • stderr = obj.stderr.read():读取子进程标准错误的内容。

  • conn.send(stdout)conn.send(stderr):将命令的执行结果或错误信息通过 conn 发送回客户端。

3.5.2客户端 

import socketclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)client.connect(('127.0.0.1', 8080))while True:data = input('please enter your data')client.send(data.encode('utf8'))data = client.recv(1024)print('from server:', data)client.close()

3.6 聊天室编程

3.6.1服务端

import socket
import threading
def handle_client(client_socket, client_address):print(f"{client_address} has connected.")while True:try:message = client_socket.recv(1024).decode('utf-8')if not message:breakprint(f"{client_address}: {message}")broadcast(message, client_socket)except ConnectionResetError:breakprint(f"{client_address} has disconnected.")client_socket.close()def broadcast(message, client_socket):for client in clients:if client != client_socket:client.send(message.encode('utf-8'))if __name__ == "__main__":server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server.bind(('127.0.0.1', 8080))server.listen(5)print("Server is running...")clients = []while True:client_socket, client_address = server.accept()clients.append(client_socket)client_handler = threading.Thread(target=handle_client, args=(client_socket, client_address))client_handler.start()

3.6.2客户端 

import socket
import threading
def receive_messages(client_socket):while True:try:message = client_socket.recv(1024).decode('utf-8')if not message:breakprint(message)except ConnectionResetError:break
def main():client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)client_socket.connect(('127.0.0.1', 8080))thread = threading.Thread(target=receive_messages, args=(client_socket,))thread.start()while True:message = input()if message.lower() == 'exit':breakclient_socket.send(message.encode('utf-8'))client_socket.close()if __name__ == "__main__":main()

四、UDP编程介绍 、

        UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。虽然用UDP传输数据不可靠,但它的优点是和TCP比,速度快,对于不要求可靠到达的数据,就可以使用UDP协议。创建Socket时, SOCK_DGRAM 指定了这个Socket的类型是UDP。绑定端口和TCP一样,但是不需要调用 listen() 方法,而是直接接收来自任何客户端的数据。 recvfrom() 方法返回数据和客户端的地址与端口,这样,服务器收到数据后,直接调用 sendto() 就可以把数据用UDP发给客户端。

4.1 DUP编程的实现

服务端

#coding=utf-8
from socket import *
#最简化的UDP服务端代码
s = socket(AF_INET,SOCK_DGRAM)  #创建UDP类型的套接字
s.bind(("127.0.0.1",8888))  #绑定端口,ip可以不写
print("等待接收数据!")
recv_data = s.recvfrom(1024)    #1024表示本次接收的最大字节数
print(f"收到远程信息:{recv_data[0].decode('gbk')},from {recv_data[1]}")
s.close()

客户端

#coding=utf-8
from socket import *
#最简化的UDP客户端发送消息代码
s = socket(AF_INET,SOCK_DGRAM)  #创建UDP类型的套接字
addr = ("127.0.0.1",8888)
data = input("请输入:")
s.sendto(data.encode("gbk"),addr)
s.close()

4.2 UDP持续接受信息 

服务端

#coding=utf-8
from socket import *
#最简化的UDP服务端代码
s = socket(AF_INET,SOCK_DGRAM)  #创建UDP类型的套接字
s.bind(("127.0.0.1",8888))  #绑定端口,ip可以不写
print("等待接收数据!")
while True:recv_data = s.recvfrom(1024)    #1024表示本次接收的最大字节数recv_content = recv_data[0].decode('gbk')print(f"收到远程信息:{recv_content},from {recv_data[1]}")if recv_content == "88":print("结束聊天!")breaks.close()

客户端

#coding=utf-8
from socket import *
#最简化的UDP客户端发送消息代码
s = socket(AF_INET,SOCK_DGRAM)  #创建UDP类型的套接字
addr = ("127.0.0.1",8888)while True:data = input("请输入:")s.sendto(data.encode("gbk"),addr)if data == "88":print("结束聊天!")breaks.close()

参考文献

大白话OSI七层协议 - B站-水论文的程序猿 - 博客园

电脑间数据通信-OSI协议简述版 - B站-水论文的程序猿 - 博客园

TCP协议的三次握手和四次挥手 - B站-水论文的程序猿 - 博客园

基于TCP协议的socket套接字编程 - B站-水论文的程序猿 - 博客园
Socket抽象层 - B站-水论文的程序猿 - 博客园

模拟ssh远程执行命令 - B站-水论文的程序猿 - 博客园

粘包问题 - B站-水论文的程序猿 - 博客园

解决粘包问题 - B站-水论文的程序猿 - 博客园

基于UDP协议的socket套接字编程 - B站-水论文的程序猿 - 博客园

基于socketserver实现并发的socket编程 - B站-水论文的程序猿 - 博客园


http://www.ppmy.cn/news/1539403.html

相关文章

Chromium html<iframe>对应c++接口定义

HTML <iframe> 标签 使用 <iframe> 标签 在当前 HTML 文档中嵌入另一个文档&#xff1a; <!DOCTYPE html> <html> <body><h1>iframe 元素</h1><iframe src"https://www.w3school.com.cn" title"W3School 在线教…

杨中科 .netcore Linq常用的扩展方法

一、 LINQ中提供了大量类似Where的扩展方法&#xff0c;简化数据处理。大部分都在System.Linq命名空间中 基本是对于lEnumerable 接口的扩展方法。 数组、List、Dictionary、Set… 都是实现了 lEnumerable 接口。因此都是可以使用这些扩展方法。 准备初始数据 class Employ…

k8s集群版本升级

Kubernetes 集群版本升级是为了获得最新的功能、增强的安全性和性能改进。然而&#xff0c;升级过程需要谨慎进行&#xff0c;特别是在生产环境中。通常&#xff0c;Kubernetes 集群的版本升级应遵循逐步升级的策略&#xff0c;不建议直接跳过多个版本。 Kubernetes 版本升级的…

【Python库安装】Python环境安装气象地理常用库salem

【Python库安装】Python环境安装salem库 salem库概述Anaconda创建环境下载基础安装包 1 安装geopandas库1.1 安装geopandas依赖库1.2 安装geopandas库1.3 安装matplotlib库 2 安装salem库2.1 安装salem依赖库2.2 安装salem库 3 安装cartopy库参考 salem库概述 salem是一个用于…

基于SpringBoot的个性化健康建议平台

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于智能推荐的卫生健康系统的相关信息成为…

uniapp打包安卓apk步骤

然后安装在手机上就可以啦

鼠标市场洞察:数据分析揭示消费趋势!

鼠标整体数据分析 一. 概述 本报告基于从淘宝商品搜索接口和淘宝精确月销量接口中提取的数据&#xff0c;分析了前百个品牌在销售额上的占比情况。分析涵盖了销售额和占比的数据&#xff0c;为决策提供了依据。(以上两个接口有需求的可以找我要链接&#xff09; 1. 大盘整体…

文本语义检索系统的搭建过程,涵盖了召回、排序以及Milvus召回系统、短视频推荐等相关内容

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下本文详细介绍了文本语义检索系统的搭建过程&#xff0c;涵盖了召回、排序以及Milvus召回系统的相关内容。通过使用PyTorch框架&#xff0c;我们提供了样例代码&#xff0c;以帮助读者更好地理解和实践。该系统具有广…