Python网络编程-一文厘清socket、TCP和UDP那点事

Python 专栏收录该内容
12 篇文章 1 订阅

在这里插入图片描述

网络基础

网络协议


网络协议是计算机网络数据进行彼此交换而建立起的规则或标准。就像我们说的普通话一样,网络协议是计算机设备间的“普通话”,是一种彼此交流的方式。更多计算机网络总结可参考这篇博客,此处不便赘述

不得不提网络协议三要素:语义、语法、同步

  • 语义:即需要发出何种控制信息,完成何种动作以及做出何种相应,“讲什么”。
  • 语法:即数据与控制信息的结构或格式,“怎么讲”。
  • 同步:即事件实现顺序的详细说明。

在著名的OSI/RM模型中,将网络协议划分为7层,如下图所示:
在这里插入图片描述
网络协议中最为重要的是TCP/IP协议,它是互联网的基础协议。TCP/IP协议并不是TCP和IP协议的合称,是因特网整个网络TCP/IP协议簇。协议体系结构如图中四个层次,包括网络接口层、网络层、传输层、应用层。

IP地址与端口


IP(Internet Protocol)是计算机网络相互连接进行通信而设计的协议,位于TCP/IP协议簇体系网络层中。它规定了计算机在因特网上进行通信时应当遵守的规则,是所有计算机网络实现通信的一套规则。换句话说,任何计算机系统只要遵守IP协议就可以与因特网互联互通。

每一台主机都有一个唯一的IP地址,IP协议正是利用IP地址在主机间传递信息。IP地址由网络标识号码与主机标识号码两部分组成,可以分为ABCDE五类,分别适用大型网络、中型网络、小型网络、多目地址、备用。可以在cmd输入ipconfig查看信息。

IP地址不便于记忆,通常会使用主机名来代替IP地址,即使用DNS域名解析协议。比如输入“baidu.com”就能访问到百度了,不必输百度的IP地址。

端口是计算机与外界进行通信交流的出口,我们通过IP或域名访问到一台具体的计算机后,可以通过端口号来访问这台计算机上对应的软件或服务。常用的保留TCP端口号有HTTP80、FTP20/21、Telnet23、SMTP25、DNS53等。
更多细节可参考这篇博客,此处不便赘述

socket套接字

概念


socket是网络通信端口的一种现象,也称套接字。用于描述IP地址和端口,是一个通信链的句柄,以实现不同计算机间的通信,可以比喻成一个多孔插座,不同型号的插座得到不同的服务。具体来说就是两个程序通过一个双向通信连接实现数据的交换,而这个连接的一段就是一个socket。

socket是应用层与TCP/IP协议簇通信的中间软件抽象层,是应用层与运输层间的桥梁,如下图所示:
在这里插入图片描述

Python中socket模块


一、socket模块中的socket类
Python中,可以通过socket模块实现网络通信,该模块提供了一个scoket类,定义如下:

class socket(_socket.socket):
	def __init__(self,family=AF_INET,type=SOCK_STREAM,proto=0)

从上述定义看出,socket类是_socket.socket子类,根据给定的地址簇、套接字类型和协议号创建一个新的socket。套接字是通过地址簇套接字类型两个主要属性来控制如何发送数据。如下:

  • family套接字地址簇
    可取值有AF_INET(默认,用于IPv4寻址)、AF_INIET6(用于IPv6寻址)、AF_UNIX(UNIX域套接字的地址簇,仅支持UDS系统)等等。一般默认值是最高效的。
  • type套接字类型
    默认SOCK_STREAM,还可取SOCK_DGRAMSOCK_RAW等。SOCK_STREAM对应传输控制协议TCP。 TCP确保每条信息按顺序正确发送,而UDP传送没有顺序,可能多次传送或不传送,适合广播。
  • proto协议编号
    通常为0,可以忽略

由socket类创建的socket对象有一系列方法及属性,篇幅限制(偷懒 )不再一一演示,梳理如下(建议收藏):

名称描述
服务器套接字方法
sock.bind()将地址绑定到套接字上
sock.listen()设置并启动TCP监听器
sock.accept()被动接收TCP客户端连接,一直阻塞直到连接到达
客户端套接字方法
sock.connect()发起TCP客户端连接
sock.connect_ex()connect()扩展版本,会以错误码形式显示异常
普通的套接字方法
sock.recv()接收TCP消息
sock.recv_into()接收TCP消息到指定缓冲区
sock.send()发送TCP消息
sock.sendall()完整发送TCP消息
sock.recvfrom()接收UDP消息
sock.recvfrom_into()接收UDP消息到指定的缓冲区
sock.sendto()发送UDP消息
sock.getpeername()连接到套接字的远程地址
sock.getsockname()获取当前套接字地址
sock.getsockopt()获取给定套接字选项的值
sock.shutdown()关闭连接
sock.share()复制套接字并准备与目标进程共享
sock.close()关闭套接字
sock.detach()在未关闭文件描述符的情况下关闭套接字并返回文件描述符
sock.ioctl()控制套接字的模式
面向阻塞的套接字方法
sock.setblocking()设置套接字的阻塞或非阻塞模式
sock.gettimeout()获取阻塞套接字操作的超时时间
面向文件的套接字方法
sock.fileno()套接字的文件描述符
sock.makefile()创建与套接字关联的文件对象
数据属性
sock.family()套接字家族
sock.type()套接字类型
sock.proto()套接字协议

二、socket模块中其他功能函数

小结如下(建议码住):

名称描述
属性
AF_UNIX、AF_INET、AF_INET6、AF_NETLINK、AF_TIPCPython中支持的套接字地址家族
SO_STREAM、SO_DGRAM套接字类型
has_ipv6是否支持IPv6
异常
error套接字相关错误
herror主机和地址相关错误
gaierror地址相关错误
timeout超时时间
方法
socket()以给定的地址家族、套接字类型和协议类型创业一个套接字对象
socketpair()以给定的地址家族、套接字类型和协议类型创业一对套接字对象
create_connection()接收一个地址,返回套接字对象
fromfd()以一个打开的文件描述符创建一个套接字对象
ssl()通过套接字启动一个安全套接字连接,不执行证书验证
getaddrinfo()获取一个五元组序列形式的地址信息
getnameinfo()以给定的套接字地址,返回二元组(主机名,端口号)
getfqdn()返回完整的域名
gethostname()返回当前主机名
gethostbyname()将一个主机名映射到它的IP地址
gethostbyname_ex()gethostbyname()扩展版本,返回主机名、别名主机集合和IP地址列表
gethostbyaddr()将一个IP地址映射到DNS信息,返回与gethostbyname_ex()相同的三元组
getprotobyname()将协议名映射到一个数字
getservbyname()将服务名映射到一个协议名
getservbyport()将服务名映射到一个端口号
ntohl()/ntohs()将来自网络的整数转换为主机字节顺序
htonl()/htons()将来自网络的整数转换为网络字节顺序
inet_aton()/inet_ntoa()将IP地址八进制字符串转换为32位的包格式,或者反过来
getdefaulttimeout()返回默认套接字超时时间
setdefaulttimeout()设置默认套接字超时时间

几个常用举例:

import socket
print('-----将主机端口转换为五元组(family,type,proto,canonname,sockaddr):')
print(socket.getaddrinfo('baidu.com',port=80))
print(socket.getaddrinfo('example.org',80,proto=socket.IPPROTO_TCP))
print('-----当前主机名')
print(socket.gethostname())
print('-----返回限制域名:')
print(socket.getfqdn())  #不带参默认本机
print(socket.getfqdn('baidu.com'))  #可能网络影响有点慢
print(socket.getfqdn('123.110.50.216'))
print('-----主机名转换IP地址')
print(socket.gethostbyname('baidu.com'))
print(socket.gethostbyname('123.125.105.110'))
print('-----主机名转换IP地址并返回三元组(hostname,aliaslist,ipaddrlist)')
print(socket.gethostbyname_ex('baidu.com'))

在这里插入图片描述

插播反爬信息 )博主CSDN地址:https://wzlodq.blog.csdn.net/

TCP下的服务器与客户端


TCP(Transmission Control Protocol)传输控制协议是一种面向连接的、可靠的和基于字节流的传输层通信协议,用于在不可靠的互联网络上提供可靠的、端对端字节流传输服务。

位于TCP/IP体系结构的传输层是处在IP层之上、应用层之下的中间层,所以数据传输必须经过IP层。当应用层想TCP层发送用于网间传输、用八位字节表示的数据流时,TCP把数据流分割成适当长度的报文段,然后把离散的报文组装为比特流。为了保障数据的可靠传输,会对从应用层传送到TCP实体的数据进行监管,并提供了重发机制和流控制。

在这里插入图片描述

TCP工作原理


TCP是如何保障数据可靠不丢失且有序呢?答案是对传输数据按字节进行了编号,编号的目的是保证传送到接收端的数据能够按顺序接收。接收端会对已经接收的数据发回一个确认,若发送端在规定时间内未收到有编号的数据,则将重新传送前面的数据。

如何编号?TCP不是使用顺序的整数作为数据包的编号,而是通过一个计数器记录发送的字节数,且TCP初始序列号是随机选择的,这样可以避免TCP序号易于猜测而伪造数据进行欺骗或攻击。比如包大小是2048字节,初始序号为3000,那么下一个数据包的序号是5048。

此外,TCP可以一次性发送多个数据包,无须按数据包依次发送。同时可以通过发送方传输的数据量大小来进行减缓或暂停(流量控制),若发送数据包丢弃,就会减少每秒发送的数据量。

结合前面讲的socket模块,要如何进行TCP通信呢?先从服务器开始:初始化Socket、然后绑定(bind)端口、监听(listen)端口、调用accept阻塞、最后等待客户端连接;某个客户端初始化一个Socket,然后连接(connect)服务器。若连接成功,那么客户端与服务器的连接就建立了,客户端发送数据请求,服务器接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互就结束了。上图解:
在这里插入图片描述

TCP服务器的实现


创建TcpServer.py,使用socket模块实现TCP服务器,启动服务器,等待客户端连接,详见注释:

import socket

HOST = 'localhost'  # 主机
PORT = 6666   # 端口
BUF_SIZE = 1024  # 最大字节数
ADDRESS = (HOST, PORT)  # 地址(IP,端口)

if __name__ == '__main__':
    # 新建socket连接
    server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 将套接字与指定IP端口连接
    server_socket.bind(ADDRESS)
    # 启动监听(并设最大连接数为5)
    server_socket.listen(5)
    print('正在监听:%s:%d' % (HOST, PORT))
    while True:
        print(u'服务器已就绪,等待连接中...')
        # 当有连接时,把收到的套接字存到client_sock,远程连接细节存到address中
        client_sock, address = server_socket.accept()
        print(u'连接客户端地址:',address)
        while True:
            # 接收数据
            data = client_sock.recv(BUF_SIZE)
            if not data or data == 0:
                break
            print('来自客户端信息:%s' % data.decode('utf-8'))
            # 发送数据
            client_sock.send('好的'.encode('utf-8'))
        client_sock.close()  # 关闭客户端
    server_socket.close()  # 关闭socket

运行服务器:
在这里插入图片描述

TCP客户端的实现


新建TcpClient.py:

import socket

HOST = 'localhost'
PORT = 6666  # 注意端口一致
BUF_SIZE = 1024
ADDRESS = (HOST, PORT)

if __name__ == '__main__':
    # 创建socket
    sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 连接服务器
    sock.connect(ADDRESS)
    print('成功连接目标主机:%s,目标主机端口:%s' % (HOST,PORT))
    # 发送数据
    sock.send('记得一键三连~'.encode('utf-8'))
    # 接收数据
    msg=sock.recv(BUF_SIZE)
    print('来自服务器信息:%s' % msg.decode('utf-8'))
    #关闭连接
    sock.close()

运行客户端:
在这里插入图片描述

服务器结果:
在这里插入图片描述

注意发送接收数据时以bytes进行而不是string,要不然会报错“TypeError: a bytes-like object is required, not ‘str’”,所以使用encode()decode()进行编码和解码。

UDP下的服务器与客户端


UDP(User Datagram Protocol)用户数据报协议是OSI模型中一种无连接的传输层协议,提供了面向事务的简单不可靠消息传送服务。

UDP同TCP一样也是用于处理数据包,不过它只负责将应用层的数据发送出去,不具备差错控制和流量控制。因此在传送过程中如果数据出错就要由高层协议处理,但也因为没有差错控制和流量控制的开销,所以使得传输效率高、延时小,适用于对可靠性要求不高的应用,可以快速大量的发送数据但不负责可靠性,同文章开头表情包,快不快就完事了
在这里插入图片描述

UDP工作原理


UDP提供不可靠的无连接数据包传输服务,使用底层互联网协议传送报文,IP报文协议号是17,其报文是封装在IP数据报中进行传输的。UDP报文由UDP源端口自动、UDP目标端口字段、UDP报文长度字段、UDP校验和字段以及数据区组成。首先通过端口机制进行复用和分解,每个UDP应用程序在发送数据报文之前,必须与操作系统协商获取相应的协议端口及端口号,然后根据目的端口号进行分解,接收端使用UDP的校验进行确认,查看UDP报文是否正确到达了目标主机的相应端口。
在这里插入图片描述

UDP服务器的实现


新建UdpServer.py:

import socket
BUF_SIZE = 1024

if __name__ == '__main__':
    # 新建socket连接(用SOCK_DGRAM即UDP=数据报)
    sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    # 绑定主机和端口
    sock.bind(('localhost',8888))
    while True:
        print(u'服务器已就绪,等待连接中...')
        # 当也有连接时,将接收数据存到data,远程连接细节存到address
        data, address = sock.recvfrom(BUF_SIZE)
        print('连接客户端地址:', address)
        print('来自客户端信息:%s' % data.decode('utf-8'))
        # 发送数据
        sock.sendto('收到'.encode('utf-8'),address)

运行服务器:
在这里插入图片描述

UDP客户端的实现


新建UdpClient.py:

import socket
BUF_SIZE = 1024

if __name__ == '__main__':
    # 新建socket连接
    sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    # 发送数据
    sock.sendto('在线蹲个一键三连~'.encode(), ('localhost', 8888))
    # 接收数据
    data, address = sock.recvfrom(BUF_SIZE)
    print('连接服务器地址:', address)
    print('来自服务器信息:%s' % data.decode())

运行客户端:
在这里插入图片描述
服务器结果:
在这里插入图片描述

注意UDP与TCP连接不同的是socket.socket()的第二个参数。TCP使用SOCK_STREAM,UDP使用SOCK_DGRAM

Python系列博客持续更新中

原创不易,请勿转载本不富裕的访问量雪上加霜
博主首页:https://wzlodq.blog.csdn.net/
微信公众号:吾仄lo咚锵
如果文章对你有帮助,记得一键三连❤

评论 22 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:护眼 设计师:闪电赇 返回首页

打赏作者

吾仄lo咚锵

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值