python入门自学进阶——8-网络编程(代码片段)

kaoa000 kaoa000     2023-01-27     668

关键词:

网络编程,就是在两台或多台计算机之间通信,网络通信的三个要素:IP地址、端口号、协议。

socket所在层次示意图:

我们写的程序运行起来就是用户进程,我们的程序进行在运行时,如果要进行网络通信,只需要与socket进行交互就可以,socket封装了底层的协议与逻辑,使我们不必关心底层的实现,简化网络通信编程。

 SOCKET编程:

涉及两方:服务器端和客户端。

 

 服务器与客户端都需要创建socket。

python中创建socket,需要引入socket模块,然后使用socket方法创建:

import socket
sk = socket.socket(family,type,proto)

socket(family,type[,proto]) 使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。其中,family参数可选值如下:

名称目的
AF_INETIPv4网络通信
AF_INET6IPv6网络通信
AF_PACKET链路层通信
AF_UNIX, AF_LOCAL本地通信

type参数

protocol参数:

  0  (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议

IPPROTO_TCPIPPTOTO_UDPIPPROTO_SCTPIPPROTO_TIPCTCP
TCP传输协议UDP传输协议STCP传输协议TIPC传输协议

 也就是说,默认不输入参数时,socket()创建的是IPv4网络协议簇,流式socket,即TCP协议数据的一个进程。同socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM,proto=0)   语句等价。

创建好socket后,下一步要进行分叉了,就是要确定到低是作为服务器还是客户端,作为服务器,我们知道,服务器要对外提供服务,需要有确定的地址和端口,这个socket就要与作为服务器的地址和端口进行绑定,相当于变身为具体的服务器了,这个服务器的地址就是绑定的IP和端口号。客户端就可以通过这个地址进行链接。

服务端套接字函数:
s.bind()    绑定(主机,端口号)到套接字
s.listen()  开始TCP监听,半连接池可以指定等待数量
s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来

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

公共用途的套接字函数:
s.recv()        接收TCP数据
s.send()       发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall()    发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
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()        创建一个与该套接字相关的文件

服务器端:

 运行后,在accept()处阻塞,即停止在此处,等待客户端连接的到来。运行如下客户端:

服务器端则接着执行,打印clisock:

 打印的clisock如下:

(<socket.socket fd=300, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.1.117', 8888), raddr=('192.168.1.117', 63077)>, ('192.168.1.117', 63077))

server_tcpsock如下:

<socket.socket fd=296, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> 没有绑定地址前
<socket.socket fd=296, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.1.117', 8888)>,绑定本地地址后

 可以看到这是同一个sock,fd都是296,绑定地址与没绑定之前的区别就是多了laddr,即本地地址。

而接收客户端连接后,生成了新的sock,新的sock的fd是300,多了raddr,即客户端的地址,返回的clisock分成两部分,一部分是新的sock,一部分是客户端地址(IP+端口号),新的sock是以服务器的原sock为模板,通过增加raddr来生成的。这样服务器就有了客户端地址,就可以向客户端发送信息。

客户端是主动连接服务器,客户端是知道服务器的地址的,服务器是通过客户端的连接获得了客户端的地址。

通过程序,可以看到,只要客户端连接服务器,就在服务器端生成新的socket,不必发送信息。服务器与这个客户端的通信就使用这个socket,如果有其他客户端连接,会生成其他socket,相互的连接也是使用各自的socket,不会发生混乱。

clisock并不是socket,它是一个元组,包含了socket,所以,一般使用:

conn,addr = server_tcpsock.accept()

来分别获得socket和raddr。

看一下结果:

 连接后,服务器和客户端都可以主动发送信息,但要做到一发一收。

客户端发送数据:

使用socket,应用TCP进行通信,数据必须是bytes类型的,所以需要进行转码:

服务器端接收:

 服务器端的运行结果:

看到接收后数据也是bytes型,显示时需要再次转换为字符串:

 运行结果:

 服务器端发送数据:

客户端接收:

客户端结果:

 注意服务器的发送数据和接收数据,使用的socket都是conn,而不是server_tcpsock。

程序运行完毕后,连接是要关闭的,这里是Python自动为我们关闭的连接,一般我们需要在程序中主动关闭连接。对于服务器来说,关闭连接不是关闭所有的socket,而是关闭连接进来的socket,这里就是conn,因为服务器启动后是要不间断为所有客户端服务的,哪个客户端不使用了,就关闭哪个客户端的连接,也就是关系哪个客户端的socket。客户端只有一个socket,直接关闭这个socket就行了。

使用conn.close()关闭socket。

 对于发送信息,有send()和sendall(),区别是,send()发送数据是有大小限制的,最大为发送缓冲区大小的数据,多出来的不发送,sendall()则反复循环使用send(),将所有数据全部发送。

recv()接收数据,参数用于指定接收缓冲区的大小,即一次最大接收多少数据。

 recv()也有阻塞的作用,即一方启动后,可以不等到对方发送数据,先启动接收,这时因为对方还没发送数据,所以这时接收方就阻塞,一直在接收等待状态。

持续连接:

 

 

 

 上述程序的BUG:

1、当客户端断开连接后,服务器端就应该跳出第二层while循环,否则服务器停在input处,输入信息发送,会使用已经断开的socket,发生错误。

2、客户端一开始就输入quit,直接终止时,服务器端也进行了接收,运行到input处,然后就跟第1条错误类似。

3、客户端连接以后,如果直接又异常终止了,服务器端会直接错误,接收语句使用了异常的socket。

那么,程序修改就针对上面的逻辑进行修改:当客户端主动结束时,服务器端接收的是空,可以此为判定条件,跳出内层循环;如果异常终止,在接收时就会出错,可以使用try语句捕获错误。

修改一:在这种交互中,不能发空,也就是不能直接回车,直接回车后,发送方因为是空,没有发送,又无法回到输入,卡死,接收端则一直处于接收阻塞,卡死

 双方都增加一个判断输入为空的判断。

修改二:双方有一方正常结束时,另一方收到的是空,判断后结束:

 

 修改三:客户端异常结束,服务器端要能判断并正常结束,同时服务器端在一个客户端终止后,循环连接其他客户端:

 对于listen()方法,参数是连接池的大小,这里是2,就是可以有2个连接排队,即一个有3个连接:

 关于多次运行同一程序的问题:在pycharm高版本中,需要如下操作:

右键点击程序,选择edit 'client'...,出现如下界面:

 

 将Allow parallel run勾选上,就可以同时运行多个,即允许并行运行。

 

python入门自学进阶-web框架——18formmodelform(代码片段)

Form:强大的数据验证,对前端的请求进行数据验证基本的Form使用:fromdjango.shortcutsimportrender,HttpResponsefromdjangoimportformsfromdjango.formsimportfields#一般引入forms就可以,但是字段类型是保存在forms下的fields中的 查看详情

python入门自学进阶-web框架——18formmodelform(代码片段)

Form:强大的数据验证,对前端的请求进行数据验证基本的Form使用:fromdjango.shortcutsimportrender,HttpResponsefromdjangoimportformsfromdjango.formsimportfields#一般引入forms就可以,但是字段类型是保存在forms下的fields中的 查看详情

python入门自学进阶-web框架——3django的url配置(代码片段)

了解一下Django的配置文件settings.py:"""DjangosettingsforMyPySiteproject.Generatedby'django-adminstartproject'usingDjango3.2.11.Formoreinformationonthisfile,seehttps://docs.djangoproj 查看详情

python入门自学进阶-web框架——21djangoadmin项目应用(代码片段)

客户关系管理。以admin项目为基础,扩展自己的项目。一、创建项目二、配置数据库,使用mysql数据库:需要安全mysqlclient模块:pipinstallmysqlclientDATABASES='default':'ENGINE':'django.db.backends.mysql 查看详情

python入门自学进阶-web框架——2django初识(代码片段)

Django是使用Python编写的一个WEB服务器应用框架,使用MTV模型。Django实现的流程:1、安装Django:pipinstalldjango安装完毕后,在python目录下的scripts子目录下有2、创建项目project在D盘根目录下运行:django-adminstartprojec... 查看详情

python入门自学进阶——11-协程(代码片段)

协程,又叫做微线程,纤程。Coroutine。协程是一种用户态的轻量级线程。协程拥有自己的寄存器上下文和栈,调度切换时,将寄存器上下文和栈保存,在切换回来时,恢复寄存器山下文和栈。协程能保留上... 查看详情

python入门自学进阶-web框架——9django进阶-认识cookie和session(代码片段)

Django的应用简单后台管理:1、登录注册2、班级、教师、学生管理3、增删改查CRUD操作首先,做一个简单的登录认证,COOKIES认识与使用:登录页面login.html:<!DOCTYPEhtml><htmllang="en"><head><me... 查看详情

python入门自学进阶-web框架——15django的form验证2(代码片段)

对Form进行更深入的研究。使用Form,需要1、创建一个验证用户请求的模板,就是一个类:fromdjangoimportformsclassMyForm(forms.Form)   user=forms.CharField(...)    #默认生成inputtype=‘text’  email= fo 查看详情

python入门自学进阶-web框架——20django其他相关知识2(代码片段)

...xff08;本地内存)3、文件4、数据库5、Memcache缓存(python-memcached模块)  (分布式内存)6、Memca 查看详情

python入门自学进阶-web框架——20django其他相关知识2(代码片段)

...xff08;本地内存)3、文件4、数据库5、Memcache缓存(python-memcached模块)  (分布式内存)6、Memca 查看详情

python入门自学进阶-web框架——13django实践小项目3(代码片段)

实现老师管理中心班级选择的左右框列表方式实现:Django中的数据库查询,有filter(),还有一个exclude()models.Tb.objects.filter(name='root')   查找name为root的记录models.Tb.objects.exclude 查看详情

python入门自学进阶-web框架——11django实践小项目(代码片段)

以学生、老师、班级管理实现一个小的管理项目。基本的界面 前端页面的总的框架,因为页头、左侧菜单栏基本是始终保持一致,只是右边内容随不同的菜单项改变,所以,使用一个lindex_base.html作为框架模板。... 查看详情

python入门自学进阶-web框架——19django其他相关知识(代码片段)

与Django相关的其他一些知识点。中间件、CSRF、缓存、信号、BootStrap(模板)——响应式+模板一、中间件在所有的web框架中都有。 Django中中间件,在settings.py中: 定义了一个MIDDLEWARE列表,其中定义了使用... 查看详情

python入门自学进阶-web框架——12django实践小项目2(代码片段)

前面的例子是单表操作,这里进行多表操作的实验。一对多的操作,即一张表中有外键,先创建测试表classProvince(models.Model):name=models.CharField(max_length=32)#默认Django会生成id列作为主键,也可以自己定义一个唯... 查看详情

python入门自学进阶-web框架——24djangoadmin项目应用-定制页面2(代码片段)

这里要实现点击对应的表名称,显示具体表的详细内容,大致的流程是:前端显示各个表名,如下:<tbody>%fortable_name,admininapp_tables.items%<trclass="border-bottom"><tdstyle="paddi 查看详情

python入门自学进阶-web框架——23djangoadmin项目应用-定制页面(代码片段)

一、单个菜单页面实现:类似DjangoAdmin中点击一个管理表打开的样子。以客户首页为例:这里cust_index就是路由表中的name字段的值,即别名,对应的就是路由项中的cust.html,即对应视图函数cust_index。 视图函数&... 查看详情

python入门自学进阶-web框架——16django登录/注册(代码片段)

以抽屉为原型,实现用户的注册和登录。基本的界面: 第一个知识点:自动发送验证码到邮箱,也就是实现自动发送邮件的功能:  要自动给别人发送邮件,首先要有自己的邮箱,msg["From"]保存... 查看详情

python入门自学进阶-web框架——16django登录/注册(代码片段)

以抽屉为原型,实现用户的注册和登录。基本的界面: 第一个知识点:自动发送验证码到邮箱,也就是实现自动发送邮件的功能:  要自动给别人发送邮件,首先要有自己的邮箱,msg["From"]保存... 查看详情