mpi tcp连接报错_Python——网络编程(TCPServer简单实现流程,TCP群聊服务端实现)...

七层模型实现过于复杂,所以是一种理想的模型

c68a5ae555bda8a3731467062021a6a6.png

TCP/IP 协议很重要,TCP协议(三次握手,四次断开)的优点:可靠的,有链接的(三次握手的链接代价偏高,但通道稳定),主动检测是否断开的方式:心跳

七层之间的数据转换:

1562c914cf2e9258566c08315ecfa6bc.png
应用层发消息,逐层向下转换成电信号,电信号通过脉冲的变化转换为0,1,组成帧,组成包,再向上变为数据

Socket

Socket库底级的库(要对协议,硬件等都要有所了解),若为了效率,倒不如C,C++,所以不常用,但必学。开发中应该用开发效率高的东西,接口较为简单,例如RPC(远程过程调用)库

d4efc5501631dbf067848104e429f5cd.png
来自 百度百科

Socket(双向通讯)是一个简单,标准的,基于一些协议的通讯接口,两端各有一个Socket(可视为插座),通过中间的传输管道进行数据传输,通信(逻辑上的理解)。两个插座的通讯要告诉对方IP地址(已知是TCP/IP协议),端口等信息,才能进行准确的通信。

域名解析为IP地址:浏览器通过DNS解析,拿到IP地址,TCP端口默认是80。

把一个端口(1000以内尽量不要自己用,为了解决进程接通讯的问题,将IP地址(操作系统软管理)利用端口可以分成多份)绑定(或分配)到一个应用进程上,应用程序需要端口信息,要与Socket绑定 --> 文件描述服务是归进程

161fbe093ec93982c12d218e7f848f8e.png
网络协议实际上跟操作系统没什么关系,操作系统是提供底层服务的

44376dadda060f63e316a5193b3f897e.png
都要占用一个文件描述符的

异步编程(网络效率较高,异步库)与同步编程(效率不高)

2e17fa030260dade76a361235f0e3b78.png

进程间通讯:进程间如何共享数据的问题。

990beda35d726e4e2d91c8b95b7786d5.png

Socket编程是有端到端的,设计Server端与服务端,经典的CS编程

TCP中数据包出错可以进行重发,其中协议的sever和client端是相对的,数据是可以双向传输的,习惯上定义“我在远端,我想你要数据,你返回给我了”你(绑定一个稳定的端口,向别人提供数据的端口)为server。

b84acfa7f4b08ca2bb7437ba3e1dbb19.png

b769de05726cf85646844a7c985748ac.png
accept是一个为了建立一对多关系的函数,会与客户端建立连接并立即重新分配一个新的Socket

53c7f5a0f565d636a976eb9d220b93a1.png

绑定了一个端口号,端口与你的应用程序建立连接(作为Server端)

1e8c13989fe3b84734c2fdbb9f7842d8.png

第一个请求过来,通过端口连接到了你,通过accept判断是否同意建立连接(进程间跨Socket或网络的链接),若同意则建立Socket通信要返回数据,Server会再建立一个新的Socket,让她去连接新的Socket(这个Socket与你的应用程序通信)。

Client端的Socket不需要绑定(无需处理),端口临时去分配(挑选一个闲置的端口与server通信),IP不能临时(但由于你的本机地址IPv4枯竭了,本机IP看到的是私网地址,然后通过net转化转为公网地址),要固定。多次关闭浏览器,每次建立连接的端口可能是变化的。

建立监听socket:

77f86fc8d655f0783a4d55d96c5d406b.png
IPv4家族(点分四段,127.0.0.0代表本机所有IP)的,TCP协议,IP,端口是空的

分配IP,端口,并绑定:

d3998c8a4ab8b3f770dd13ad5e196215.png
本地回环地址

81395ad19111b048491782aa98be6e49.png

bbac124f339badaecbce492f37045e4c.png

cedcfc5a114a5afa78de161ef63ba440.png

客户端的socket与server建立连接:

f7f7a3f7684b52998fed9baa318c060a.png
返回一个对端的socket对象和地址

dbcf7e233fb6ca4849558f831922905b.png

68df5a3f699e9b6ba64bae07db80c8e9.png
r remote 远端地址

一个Socket是一个文件,他会占用一个文件描述符(fd)

bdf4e9f9ef277e6cf7c802cd6b04492b.png

35e2e23f4d8df1f574669d248720ca72.png

一共三个Socket:

b5278fcc7a4494dccf3a805481e9f6a7.png
后三行,前后为建立的连接(双向通道),中间的为SSH

接收:

acfe8f8a35b802b3632f6daac8820762.png

发送:

c20bd35dda424239d197177bfc7da586.png

不发生阻塞的接收:

c899ccfd50dd12b68b053cd4aae9182f.png
管道在,有链接,先发送一个,已经存在,直接取走

断开了s2的链接:

c21b57c6853db5724eafe0a6a5d2c5da.png

关闭server:

9aa245f3aa5753645519251513faae9c.png
s1连接还在,但是并不好用

清理工作:(主要是归还文件描述符)

服务器端应该先将所有的与对端连接的socket逐个close,最后在将作监听的socket close。

先将监听断掉意味着,我再也不接收链接了,在逐个关闭与对端连接的socket

PyCharm

5f934bb656f33fddbc6621a83521e4e9.png
接收的data不是String类型的,要进行类型转换

循环跑起来:

accept方法返回值是一个二元组(新的Socket对象(fd文件描述符,laddr自己的地址,raddr对端地址),对端地址)

950bf76ff0331803707b8651c4954786.png
没有终止循环的条件

acd410a8950ea4c4be02173342a666ef.png

Client断开,Server端抛出一个异常:

5f3bc2fd4aa039f5f49c8b54eae3cfe5.png

问题:只有一个accpet(),也就是只能与一个客户端相连,并且进入循环无法主动跳出;新来客户端连接请求不予理睬 --> 多线程解决!

两次绑定同一个监听端口:

做服务端的IP地址应该是不变的,不能是浮动的,否则无法访问,常提供服务的端口应该是用默认或者固定(利于使用)。

1e73a9e3dbc15f2dfbea023624e209d4.png
*:22 所有的IP地址都为其所用,22为SSH

3302db85c2242bcbae1f86ed07e2c212.png

二次绑定:

4a47c0f5d529a8ed1314c47c962adf2a.png

原因:一个IP地址和端口只能对应一个应用程序,一个应用程序可以对应多个IP地址和端口

同步解决方案:

26fd81fa1eb436a0c34b69b992f215fb.png

send(传送信息,只不过当前管道已连接,是通的,所以看起来没有阻塞),accept(等对方来连接),recv(等对方传送的信息) 的默认工作模式都是阻塞的

非阻塞的异步代码较困难:

群聊:

949d0b5747b11dfeb0b01418c7ff1331.png

d3ec3b645243c69d8b2758a6ad7f4e9e.png

服务端应该对应一个类:(要注意循环,阻塞,线程的建立与消亡)

每一个server的初始化应该记录IP和Port,每个server应该监听在不同的端口上,所以每一个server是一个单独的socket(实例化一个socket) --> 与每个实例相关(可以给默认值,但每一个server的IP和Port应该是不同的)

5dd65513e077ddddb02d07ce554af0f3.png

上图所示,accept的特性是阻塞,写在start方法中启动后就会阻塞状态,其他的server也无法进行。应该更改为:

(注意TCP传输字节流,需要字符串转码为Bytes,要注意编码问题)

33375207eec1a41319260352ee980015.png
一个socket一个线程

存在的问题:accept方法(两行代码)执行完再继续向下执行时,start中的线程消亡了,无法再继续与客户端建立连接

7954ea8f0d7e7654aa95c28490f55031.png
一次 主线程,两个工作线程

因为每个循环中的第一个语句都是阻塞的,不新开辟一个线程是不可以的。

ce910f3e69f803f46c0b3023ea3256c5.png

f83b5e0e29f51b2fd3ca99081be14fc5.png
注意编码格式即可

fccc4120d662f473a8e089d79c7d6902.png

测试再创建一个连接:

b82f05be8c3ded37577ab0c8c0c564b9.png

77e84fb6a384c04d1966d27d3b8530a7.png

到此为止,实现了一个服务多个客户端的单独聊天,并且缺少socket回收(容器存储)。

d50eee8f1e07c3d63a65440b3a4ee7e5.png

解决一对多发送信息的问题:

fc022ed4e69c67517e280af1cd4eb869.png

099eed2d342975ed7c01d4dc8083e906.png
自己的地址与对端地址

2bbcf232d1d503d6f4524ef45271d03d.png

退出:

f323f5a151eafec638a44884c041b357.png
break执行后,线程就已经终止了,可以进行socket的close

3deebe209e06b533f67bd15b87a44bd8.png

主线程 输入quit,报错,但线程关闭:

68fa96e14f34b57dde18b826609ef51e.png

原因是主线程的accept在阻塞的状态,程序强行关闭了所以报错。

子线程(客户端)强制断开连接:

c25d1c1d4a68fd1445d3e4e22579274c.png

更改recv()方法:

b44080d334eca047c44264997ae8c4d1.png

若在Linux服务器上跑,还要在调试一遍,不同操作系统的实现,抛异常可能会不同

a8ad579a3cca992cca2312483e6a4b08.png
用while True不是一个好的方案,所以改为event,但event在处理同步县城的时候还是会先执行到阻塞语句后判断,所以还是会有异常,但对于异步处理是可以的

最常用为recv和send:

7a0971a73607c9213a0ed1c971238c1b.png

6727683e50d2d27a9f21ddbc61df8232.png
UDP连接不知道对端是谁

大致意思:sendfile尽量使用零拷贝机制(直接在内核空间读取一次,其他都打标记,最后一次性推出,只有一个副本,无复杂的操作);send方式(一个字节一个字节读出,再一个字节一个字节传送)IO读出,到内核空间到用户空间,从用户空间到内核空间,再由内核空间发出。

11bb01bfd8a214db42e6529662f6dd5d.png

afc25b9f96608cff0e7cc9eb7bca6351.png

将recv和send转换为read和write方法:

6d3ae46426c750d852bcecff6f2d7687.png
发送的时候要换行,才知道是一行

cf4d2d0bbfcf23f1c2f9ea88e315d488.png
在没有接受的时候才会用到设置是否阻塞,非阻塞模式如果没有接收到数据会立即抛异常,建议先使用block模式先练习

88e86b64f1c303a5dbcca8b7096bf17f.png

可以使用makefile是因为,socket对象有文件描述符,文件对象也有文件描述符,所以可以互相转换,一般由socket转为file,因为文件操作更简单(操作字符)

f226f2133bdcdec2f5a1047ade51195d.png
文件没有方法得知传送对端地址,所以需要传过来 read方法后面要跟着大小,所以选择readline,但该方法必须有换行符,没有换行符就等待换行符

主函数封装:

fc6565fa7c82d3554d593dc1227db251.png

f.fileno :实际上用的是当前socket的文件描述符,说以makefile传回的是一个类文件对象

c2b75aa9aa49a69fc14b0e3d27f0d89e.png

8c7f2922d1829c30ebc3e4e9b41e5038.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值