网络编程
阅读目录
- 一.楔子
- 二.客户端/服务端架构
- 三.网络基础
- 四.套接字(socket)初使用
- 五.黏包
- 六.socket的更多方法介绍
- 七.验证客户端链接的合法性
- 八.socketserver模块
你现在已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运行,你就会发现,这两个python的文件分别运行的很好。但是如果这两个程序之间想要传递一个数据,你要怎么做呢?
这个问题以你现在的知识就可以解决了,我们可以创建一个文件,把a.py想要传递的内容写到文件中,然后b.py从这个文件中读取内容就可以了。

但是当你的a.py和b.py分别在不同电脑上的时候,你要怎么办呢?

类似的机制有计算机网盘,qq等等。我们可以在我们的电脑上和别人聊天,可以在自己的电脑上向网盘中上传、下载内容。这些都是两个程序在通信。
二.软件开发的架构
我们了解的涉及到两个程序之间通讯的应用大致可以分为两种:
第一种是应用类:qq、微信、网盘、优酷这一类是属于需要安装的桌面应用
第二种是web类:比如百度、知乎、博客园等使用浏览器访问就可以直接使用的应用
这些应用的本质其实都是两个程序之间的通讯。而这两个分类又对应了两个软件开发的架构~
1.C/S架构
C/S即:Client与Server ,中文意思:客户端与服务器端架构,这种架构也是从用户层面(也可以是物理层面)来划分的。
这里的客户端一般泛指客户端应用程序EXE,程序需要先安装后,才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大。
2.B/S架构
B/S即:Browser与Server,中文意思:浏览器端与服务器端架构,这种架构是从用户层面来划分的。
Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。

网络基础
计算机网络的发展及基础网络概念
问题:网络到底是什么?计算机之间是如何通信的?
早期 : 联机

以太网 : 局域网与交换机

广播
主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要),由于其不用路径选择,所以其网络成本可以很低廉。有线电视网就是典型的广播型网络,我们的电视机实际上是接受到所有频道的信号,但只将一个频道的信号还原成画面。在数据网络中也允许广播的存在,但其被限制在二层交换机的局域网范围内,禁止广播数据穿过路由器,防止广播数据影响大面积的主机。

ip地址与ip协议
- 规定网络地址的协议叫ip协议,它定义的地址称之为ip地址,广泛采用的v4版本即ipv4,它规定网络地址由32位2进制表示
- 范围0.0.0.0-255.255.255.255
- 一个ip地址通常写成四段十进制数,例:172.16.10.1
mac地址
head中包含的源和目标地址由来:ethernet规定接入internet的设备都必须具备网卡,发送端和接收端的地址便是指网卡的地址,即mac地址。
mac地址:每块网卡出厂时都被烧制上一个世界唯一的mac地址,长度为48位2进制,通常由12位16进制数表示(前六位是厂商编号,后六位是流水线号)
arp协议 ——查询IP地址和MAC地址的对应关系
广域网与路由器

路由器

局域网
局域网(Local Area Network,LAN)是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。
子网掩码
所谓”子网掩码”,就是表示子网络特征的一个参数。它在形式上等同于IP地址,也是一个32位二进制数字,它的网络部分全部为1,主机部分全部为0。比如,IP地址172.16.10.1,如果已知网络部分是前24位,主机部分是后8位,那么子网络掩码就是11111111.11111111.11111111.00000000,写成十进制就是255.255.255.0。
知道”子网掩码”,我们就能判断,任意两个IP地址是否处在同一个子网络。方法是将两个IP地址与子网掩码分别进行AND运算(两个数位都为1,运算结果为1,否则为0),然后比较结果是否相同,如果是的话,就表明它们在同一个子网络中,否则就不是。
#比如,已知IP地址172.16.10.1和172.16.10.2的子网掩码都是255.255.255.0,请问它们是否在同一个子网络?两者与子网掩码分别进行AND运算, #172.16.10.1:10101100.00010000.00001010.000000001 #255255.255.255.0:11111111.11111111.11111111.00000000 #AND运算得网络地址结果:10101100.00010000.00001010.000000001->172.16.10.0 #172.16.10.2:10101100.00010000.00001010.000000010 #AND运算得网络地址结果:10101100.00010000.00001010.000000001->172.16.10.0 #结果都是172.16.10.0,因此它们在同一个子网络。
总结一下,IP协议的作用主要有两个,一个是为每一台计算机分配IP地址,另一个是确定哪些地址在同一个子网络。
tcp协议和udp协议
用于应用程序之间的通信。如果说ip地址和mac地址帮我们确定唯一的一台机器,那么我们怎么找到一台机器上的一个软件呢?
端口
我们知道,一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等,这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区分不同的服务的。
TCP协议

当应用程序希望通过 TCP 与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址。在双方“握手”之后,TCP 将在两个应用程序之间建立一个全双工 (full-duplex) 的通信。
这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。

#TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK[1],并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接。[1] #TCP三次握手的过程如下: #客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。 #服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。 #客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。 #三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。
#建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的半关闭(half-close)造成的。 #(1) 某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕。 #(2) 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认。 #注意:FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。 #(3) 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。 #(4) 接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。[1] #既然每个方向都需要一个FIN和一个ACK,因此通常需要4个分节。 #注意: #(1) “通常”是指,某些情况下,步骤1的FIN随数据一起发送,另外,步骤2和步骤3发送的分节都出自执行被动关闭那一端,有可能被合并成一个分节。[2] #(2) 在步骤2与步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为“半关闭”(half-close)。 #(3) 当一个Unix进程无论自愿地(调用exit或从main函数返回)还是非自愿地(收到一个终止本进程的信号)终止时,所有打开的描述符都被关闭,这也导致仍然打开的任何TCP连接上也发出一个FIN。 #无论是客户还是服务器,任何一端都可以执行主动关闭。通常情况是,客户执行主动关闭,但是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。[2]
附赠一个超大的三次握手:

UDP协议
当应用程序希望通过UDP与一个应用程序通信时,传输数据之前源端和终端不建立连接。
当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。
tcp和udp的对比
TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
#现在Internet上流行的协议是TCP/IP协议,该协议中对低于1024的端口都有确切的定义,他们对应着Internet上一些常见的服务。这些常见的服务可以分为使用TCP端口(面向连接)和使用UDP端口(面向无连接)两种。 #说到TCP和UDP,首先要明白“连接”和“无连接”的含义,他们的关系可以用一个形象地比喻来说明,就是打电话和写信。两个人如果要通话,首先要建立连接——即打电话时的拨号,等待响应后——即接听电话后,才能相互传递信息,最后还要断开连接——即挂电话。写信就比较简单了,填写好收信人的地址后将信投入邮筒,收信人就可以收到了。从这个分析可以看出,建立连接可以在需要痛心地双方建立一个传递信息的通道,在发送方发送请求连接信息接收方响应后,由于是在接受方响应后才开始传递信息,而且是在一个通道中传送,因此接受方能比较完整地收到发送方发出的信息,即信息传递的可靠性比较高。但也正因为需要建立连接,使资源开销加大(在建立连接前必须等待接受方响应,传输信息过程中必须确认信息是否传到及断开连接时发出相应的信号等),独占一个通道,在断开连接钱不能建立另一个连接,即两人在通话过程中第三方不能打入电话。而无连接是一开始就发送信息(严格说来,这是没有开始、结束的),只是一次性的传递,是先不需要接受方的响应,因而在一定程度上也无法保证信息传递的可靠性了,就像写信一样,我们只是将信寄出去,却不能保证收信人一定可以收到。 #TCP是面向连接的,有比较高的可靠性, 一些要求比较高的服务一般使用这个协议,如#FTP、Telnet、SMTP、HTTP、POP3等。 #而UDP是面向无连接的,使用这个协议的常见服务有DNS、SNMP、QQ等。对于QQ必须另外说明一下,QQ2003以前是只使用UDP协议的,其服务器使用8000端口,侦听是否有信息传来,客户端使用4000端口,向外发送信息(这也就不难理解在一般的显IP的QQ版本中显示好友的IP地址信息中端口常为4000或其后续端口的原因了),即QQ程序既接受服务又提供服务,在以后的QQ版本中也支持使用TCP协议了。
互联网协议与osi模型
互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层

#每一层的作用都指向具体哪些事件
![]()
![]()
![]()
![]()
每层运行常见物理设备


每层运行常见的协议

1.一个程序如何在网络上找到另一个程序?
首先,程序必须要启动,其次,必须有这台机器的地址,我们都知道我们人的地址大概就是国家\省\市\区\街道\楼\门牌号这样字。那么每一台联网的机器在网络上也有自己的地址,它的地址是怎么表示的呢?
就是使用一串数字来表示的,例如:100.4.5.6
#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地址(100.4.5.6),实际上是32位二进制数(01100100.00000100.00000101.00000110)。
#"端口"是英文port的意译,可以认为是设备与外界通讯交流的出口。
因此ip地址精确到具体的一台电脑,而端口精确到具体的程序。
2.osi七层模型
引子
须知一个完整的计算机系统是由硬件、操作系统、应用软件三者组成,具备了这三个条件,一台计算机系统就可以自己跟自己玩了(打个单机游戏,玩个扫雷啥的)
如果你要跟别人一起玩,那你就需要上网了,什么是互联网?
互联网的核心就是由一堆协议组成,协议就是标准,比如全世界人通信的标准是英语,如果把计算机比作人,互联网协议就是计算机界的英语。所有的计算机都学会了互联网协议,那所有的计算机都就可以按照统一的标准去收发信息从而完成通信了。
osi七层模型
人们按照分工不同把互联网协议从逻辑上划分了层级:

3.socket概念
socket层

理解socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
#其实站在你的角度上看,socket就是一个模块。我们通过调用模块中已经实现的方法建立两个进程之间的连接和通信。 #也有人将socket说成ip+port,因为ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序。 #所以我们只要确立了ip和port就能找到一个应用程序,并且使用socket模块来与之通信。
3.套接字(socket)的发展史
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
4.tcp协议和udp协议
TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
我知道说这些你们也不懂,直接上图。

四.套接字(socket)初使用
基于TCP协议的socket
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
server端
import socket sk = socket.socket() 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() # 创建了一个socket对象 sk.bind(('127.0.0.1',8080)) # 绑定一台机器的(ip,端口) # 回环地址 - 指向自己这台机器 sk.listen() # 建立监听等待别人连接 conn,addr = sk.accept() # 阻塞:在这里等待直到接到一个连接 print(conn)#conn是链接 print(addr)#addr是对方的地址 conn.send(b'hello') # 和对方打招呼必须以byes类型发送 msg = conn.recv(1024) # 对方和我说的话 # 有发必有收 收发必相等 print(msg) conn.close() # 挂电话 sk.close() # 关机
client端
import socket sk = socket.socket() # 创建客户套接字 sk.connect(('127.0.0.1',8080)) # 尝试连接服务器 sk.send(b'hello!') ret = sk.recv(1024) # 对话(发送/接收) print(ret) sk.close() # 关闭客户套接字
#上课例题 import socket sk = socket.socket() # 买个手机 sk.connect(('127.0.0.1',8080)) # 拨号 ret = sk.recv(1024) #接收信息 print(ret)#打印接收的信息 sk.send(b'byebye!')#发送 sk.close()#关闭
问题:有的同学在重启服务端时可能会遇到

解决方法:
#加入一条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() #关闭服务器套接字(可选)
tcp 聊天小工具练习
服务器端
#serevr端 import socket#引入一个套接字模块 sk = socket.socket()#创建一个套接字对象 sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#使端口不会被冲突 sk.bind(('192.168.21.36',9000))#绑定一台机器的ip地址,和端口号 sk.listen()#监听 while True:#循环为真 conn,addr = sk.accept() #接收两个参数链接和地址# 接收连接 三次握手conn while True:#循环为真 inp = input('>>>')#请输入内容 if inp == 'q':#如果输入q conn.send(inp.encode('utf-8'))#向对端发送,输入utf-8模式的内容 break#打断 conn.send(inp.encode('utf-8'))#向对端发送,输入utf-8模式的内容 msg = conn.recv(1024)#接收对端信息 if msg == b'q':break#如果输入q则退出 print(msg.decode('utf-8'))#打印msg.decode('utf-8')的信息 conn.close() #断开连接 四次挥手 sk.close()#关闭服务器
对端
#client import socket#引入伯克利套接字模块 sk = socket.socket()#创建一个对象 sk.connect(('127.0.0.1',9000))#绑定ip地址和端口 while True:#循环为真 msg = sk.recv(1024)#接收对端信息 print(msg.decode('utf-8'))#打印接收的信息用utf-8方式解码 if msg == b'q':break#如果接收的信息是q,则退出 inp = input('>>>')#输入你的内容 if inp == 'q':#如果是q sk.send(inp.encode('utf-8'))#则发送utf-8的的内容 break#打断 sk.send(inp.encode('utf-8'))#发送utf-8的的内容 sk.close()#关闭这个对象
基于UDP协议的socket
udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接
简单使用
server端
import socket#引入伯克利套接字模块 udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个udp服务器的套接字 udp_sk.bind(('127.0.0.1',9000)) #绑定服务器套接字 msg,addr = udp_sk.recvfrom(1024)#接收2个参数消息和地址 print(msg)#打印这个消息 udp_sk.sendto(b'hi',addr) # 对话(接收与发送) udp_sk.close() # 关闭服务器套接字
client端
import socket#引入伯克利套接字模块 ip_port=('127.0.0.1',9000)#绑定ip和端口 udp_sk=socket.socket(type=socket.SOCK_DGRAM)#创建一个udp的套接字 udp_sk.sendto(b'hello',ip_port)#发送一个b'hello'到'127.0.0.1',9000的应用 back_msg,addr=udp_sk.recvfrom(1024)接收信息的地址,按1024字节大小 print(back_msg.decode('utf-8'),addr)#打印这个信息用utf—8解码
qq聊天
#_*_coding:utf-8_*_ #使用utf-8的编码方式 import socket#引入伯克利套接字 ip_port=('127.0.0.1',8081)#设置ip和端口号 udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)#设置网络套接字,udp模式 udp_server_sock.bind(ip_port)#绑定这个ip和端口 while True:#循环为真 qq_msg,addr=udp_server_sock.recvfrom(1024)#接收qq信息和地址 print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))#打印来自某ip地址的某端口的的信息 back_msg=input('回复消息: ').strip()#输入回复的消息并去除两边的空格 udp_server_sock.sendto(back_msg.encode('utf-8'),addr)#发送信息和地址以utf-8的模式
#_*_coding:utf-8_*_ #使用utf-8编码 import socket#引入BSD套接字 BUFSIZE=1024#设置大小为1024字节 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #设置udp的套接字
qq_name_dic={ '金老板':('127.0.0.1',8081), '哪吒':('127.0.0.1',8081), 'egg':('127.0.0.1',8081), 'yuan':('127.0.0.1',8081), } #得到一个qq名称的字典 while True:#设置循环为真 qq_name=input('请选择聊天对象: ').strip()#清水你要选择的聊天对象。并去除两边的空格 while True:#循环为真 msg=input('请输入消息,回车发送,输入q结束和他的聊天: ').strip()#请输入聊天内容 if msg == 'q':break#如果输入q则退出 if not msg or not qq_name or qq_name not in qq_name_dic:continue #如果没有输入信息,或者没有qq名字,或者qq名字不在qq字典里则跳过 udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name]) #udp对象发送信息以utf-8的模式,地址是qq名字对应的ip值 back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)得到来自谁的信息和地址 print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))#打印来自某IP地址和端口号的信息 udp_client_socket.close()#关闭这个udp对象
时间服务器
server端
# 需求 # 写一个时间同步的服务器 # 服务端接收请求 # 按照client端发送的时间格式,将服务器时间转换成对应格式 # 发送给客户端 import time#引入时间模块 import socket#引入伯克利套接字 sk = socket.socket(type=socket.SOCK_DGRAM)#创建一个udp套接字的对象 sk.bind(('127.0.0.1',9000))#定义ip地址和端口 while True:#循环为真 msg,addr = sk.recvfrom(1024)#接收信息和地址 # msg 客户端发送给server端的时间格式 "%Y-%m-%d %H:%M-%S" time_format = msg.decode('utf-8')#格式化时间信息 time_str = time.strftime(time_format)#转化成字符串时间 sk.sendto(time_str.encode('utf-8'),addr)#发送时间编码为utf-8的时间,和地址 sk.close()#关闭这个对象
client端
# client端每隔一段时间发送请求到服务端 # 发送时间的格式 import time#引入时间模块 import socket#引入套接字 sk = socket.socket(type = socket.SOCK_DGRAM)#实例化一个udp套接字对象 sk.sendto('%Y-%m-%d %H:%M:%S'.encode('utf-8'),('127.0.0.1',9000))#发送信息utf-8模式的'年月日时分秒',IP地址和端口号 msg,addr = sk.recvfrom(1024)#得到信息和地址 print(msg.decode('utf-8'))#打印信息以utf-8的模式 sk.close()#关闭套接字对象 # 方式一 # 操作系统的定时任务 + python代码的形式 # 方式二 # while True + time.sleep的形式
老师版本的时间管理器
服务器端
# _*_coding:utf-8_*_ from socket import * #引入伯克利套接字所有的模块 from time import strftime#引入时间模块的 strftime模块(结构化化时间转换成格式化时间) ip_port = ('127.0.0.1', 9000)#创建一个ip和端口 bufsize = 1024#设置大小伟1024字节 tcp_server = socket(AF_INET, SOCK_DGRAM)#实例化一个udp的套接字 tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) tcp_server.bind(ip_port)#把ip地址和端口绑定到套接字 while True:#循环为真 msg, addr = tcp_server.recvfrom(bufsize)#接收信息和地址 print('===>', msg)#打印字符串拼接('===>'+信息) if not msg:#如果没有信息 time_fmt = '%Y-%m-%d %X'#时间格式化为(年-月-日-时-分-秒) else:#否则 time_fmt = msg.decode('utf-8')#时间格式化,utf-8模式解码信息 back_msg = strftime(time_fmt)#显示结构化时间转换的格式化时间 tcp_server.sendto(back_msg.encode('utf-8'), addr)#发送一个信息以utf-8的模式,和地址 tcp_server.close()#关闭这个套接字
客户端
#_*_coding:utf-8_*_ #使用utf-8编码模式 from socket import * #引入套接字模块的所有属性 ip_port=('127.0.0.1',9000)#设置一个本地ip地址 bufsize=1024#设置大小为1024字节 tcp_client=socket(AF_INET,SOCK_DGRAM)#实例化一个套接字,网络套接字模式 while True:#循环为真 msg=input('请输入时间格式(例%Y %m %d)>>: ').strip()#请输入你的'时间格式' tcp_client.sendto(msg.encode('utf-8'),ip_port)#发送时间格式以utf-8的模式,(ip和接口) data=tcp_client.recv(bufsize)#接收这个数据
socket参数的详解
#socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)
创建socket对象的参数说明:
| family | 地址系列应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。 (AF_UNIX 域实际上是使用本地 socket 文件来通信) |
| type | 套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。 SOCK_STREAM 是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送。 SOCK_DGRAM 是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播信息。 |
| proto | 协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。 |
| fileno | 如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。 与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的。 这可能有助于使用socket.close()关闭一个独立的插座。 |
服务端套接字函数
#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() 创建一个与该套接字相关的文件





浙公网安备 33010602011771号