1.https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2.http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3.http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4.http的连接很简单,是无状态的。而HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
URI包括了URL和URN,URN和URL有交集。
1.统一资源标志符URI:就是在某一规则下能把一个资源独一无二地标识出来。(标识出这是一个人)
URI一般由三部组成:
①访问资源的命名机制
②存放资源的主机名
③资源自身的名称,由路径表示,着重强调于资源。
2.统一资源定位符URL:描述资源的详情地址。(标识出张三家的地址,)
URL一般由三部组成:
①协议(或称为服务方式)
②存有该资源的主机IP地址(有时也包括端口号)
③主机资源的具体地址。如目录和文件名等
3.统一资源名URN:描述资源的名称。(标识出这个人叫张三,168cm 60kg 18cm 等。)
数据完整性和隐私性由TLS Record Protocol保证,身份认证由TLS Handshaking Protocols实现。
—>1. 客户端发起HTTPS请求
—>2.服务端将证书(公钥)发给客服端
—>3.客服端解析证书(1.验证,2.生成随机对称密钥(即加密,解密;客服端和服务端一人一份),3.用验证的证书对生成的对称密钥加密)
—>4.发送证书加密的对称密钥给服务端
—>5.服务端通过证书(私钥)解密,获得一份对称密钥
—>6.现在客服端和服务端就人手一份对称密钥,最后就通过这一份对称密钥加密通信信息进行通信。
##一次完整的HTTP请求所经历的8个步骤
—>1.应用层把http数据交给传输层,—>2.传输层收到数据后将数据分割打上标记序号和端口号(增加TCP首部)后将数据交给网络层
—>3.网络层(增加IP首部)后发给链路层—>4.客服端链路层将其(增加以太网首部)发送出去
—>5.服务端链路层(解析以太网首部)后发给网络层—>6.网络层(解析ip首部)后发给传输层
—>7.传输层(解析TCP首部)后发送给应用层—>8.应用层接收http数据
状态码 | 类别 | 原因短语 |
---|---|---|
1XX | 信息状态码(informational) | 接收到的请求正在处理 |
2XX | 成功状态码(Success) | 请求正常处理完毕 |
3XX | 重定向状态码(Redirection) | 需要进行附加的操作完成请求 |
4XX | 客户端错误状态码(Client Error) | 服务器无法处理请求 |
5XX | 服务器错误状态码(Server Error) | 服务器处理请求出错 |
404 请求失败,请求所希望得到的资源未被在服务器上发现。404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。
503 由于临时的服务器维护或者过载,服务器当前无法处理请求。503状态码的存在并不意味着服务器在过载的时候必须使用它。某些服务器只不过是希望拒绝客户端的连接。
##TCP协议和UDP协议的区别是什么
TCP/IP 中有两个具有代表性的传输层协议,分别是 TCP 和 UDP。
1.TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构,当应用程序采用 TCP 发送消息时,虽然可以保证发送的顺序,但还是犹如没有任何间隔的数据流发送给接收端。.TCP 为提供可靠性传输,实行“顺序控制”或“重发控制”机制。此外还具备“流控制(流量控制)”、“拥塞控制”、提高网络利用率等众多功能。
2.而UDP 是不具有可靠性的数据报协议。细微的处理它会交给上层的应用去完成。在 UDP 的情况下,虽然可以确保发送消息的大小,却不能保证消息一定会到达。因此,应用有时会根据自己的需要进行重发处理。
3.总体上TCP 和 UDP 的优缺点无法简单地、绝对地去做比较:TCP 用于在传输层有必要实现可靠传输的情况;而在一方面,UDP 主要用于那些对高速传输和实时性有较高要求的通信或广播通信。TCP 和 UDP 应该根据应用的目的按需使用。
##TCP建立连接的过程采用三次握手,已知第三次握手报文的发送序列号为555,确认序列号为6666,请问第二次握手报文的发送序列号和确认序列号分别为?
确认序列号为555,发送的序列号为6665。
1.应用层:传输文件
2.传输层:对接端口
3.网络层:选择路由
4.链路层:线路光纤
##简述 osi七层模型
1.应用层 :文件传输,电子邮件
2.表示层 :数据格式化
3.会话层 :接触或建立与别的借口
4.传输层 :提供端口对接
5.网络层 :为数据选择路由
6.数据链路层 :传输有地址的帧 以及错误检测功能
7.物理层 :以二进制数据形式在物理媒体上传数据
DNS是计算机域名(Domain Name System)的缩写,它是由解析器和域名服务器组成的。域名服务器是指保存有该网络中所有主机的域名和对应IP地址,并具有将域名转换为IP地址功能的服务器。其中域名必须对应一个IP地址,一个IP地址可以同时对应多个域名,但IP地址不一定有域名。
DNS是应用层协议 ,TCP/IP协议族的一员
##arp是哪一层协议
ARP和RARP都是网络层的协议,但是它所工作的内容是链路层的。
ARP具体说来就是将网络层(IP层,也就是相当于OSI的第三层)地址解析为数据连接层(MAC层,也就是相当于OSI的第二层)的MAC地址。
wifi 数据链路层
简述cdn作用,即内容分发网络,在现有的Internet中增加一层新的网络架构,将内容发布到最接近用户的网络"边缘",使用户可以就近取得所需的内容,解决Internet网络拥挤的状况,提高用户访问网站的响应速度。
发生在第二次挥手和第三次挥手之间
SYN攻击利用的是TCP的三次握手机制,攻击端利用伪造的IP地址向被攻击端发出请求,而被攻击端发出的响应报文将永远发送不到目的地,那么被攻击端在等待关闭这个连接的过程中消耗了资源,如果有成千上万的这种连接,主机资源将被耗尽,从而达到攻击的目的。
对于SYN泛洪攻击的防范,优化主机系统设置是常用的手段。如降低SYN timeout时间,使得主机尽快释放半连接的占用;又比如采用SYN cookie设置,如果短时间内连续收到某个IP的重复SYN请求,则认为受到了该IP的攻击,丢弃来自该IP的后续请求报文。此外合理地采用防火墙等外部网络安全设施也可缓解SYN泛洪攻击。
156.123.32.13 是B类地址
0.0.0.0~127.0.0.0 是 A 类的网络地址。
后面则为127+1 .0.0.0 —— 127+1+63 .0.0.0 B类
127+1+63+1 .0.0.0 —— 127+1+63+1+63 .0.0.0 C类
127+1+63+1+63+1 .0.0.0 —— 127+1+63+1+63+1+63 .0.0.0 D类
网络地址 193.32.5.0
广播地址 193.32.5.31
网络地址:193.32.5.0
广播地址为:193.32.5.31
icmp是网络层协议:它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
GET :从指定的资源请求数据。
POST : 向指定的资源提交要被处理的数据
DoS:是Denial of Service的简称,即拒绝服务
最常见的DoS攻击有计算机网络带宽攻击和连通性攻击。
DDOS:分布式拒绝服务
借助于客户/服务器技术,将多个计算机联合起来作为攻击平台,对一个或多个目标发动DDoS攻击,从而成倍地提高拒绝服务攻击的威力。
事实上DOS的攻击方式有很多种,比如下面的常见的:
1、SYN FLOOD
利用服务器的连接缓冲区(Backlog Queue),利用特殊的程序,设置TCP的Header,向服务器端不断地成倍发送只有SYN标志的TCP连接请求。当服务器接收的时候,都认为是没有建立起来的连接请求,于是为这些请求建立会话,排到缓冲区队列中。
如果你的SYN请求超过了服务器能容纳的限度,缓冲区队列满,那么服务器就不再接收新的请求了。其他合法用户的连接都被拒绝掉。可以持续你的SYN请求发送,直到缓冲区中都是你的只有SYN标记的请求。
2、IP欺骗DOS攻击
这种攻击利用RST位来实现。假设现在有一个合法用户(1.1.1.1)已经同服务器建立了正常的连接,攻击者构造攻击的TCP数据,伪装自己的IP为1.1.1.1,并向服务器发送一个带有RST位的TCP数据段。服务器接收到这样的数据后,认为从1.1.1.1发送的连接有错误,就会清空缓冲区中建立好的连接。这时,如果合法用户1.1.1.1再发送合法数据,服务器就已经没有这样的连接了,该用户就必须从新开始建立连接。
攻击时,伪造大量的IP地址,向目标发送RST数据,使服务器不对合法用户服务。
3、带宽DOS攻击
如果你的连接带宽足够大而服务器又不是很大,你可以发送请求,来消耗服务器的缓冲区消耗服务器的带宽。这种攻击就是人多力量大了,配合上SYN一起实施DOS,威力巨大。不过是初级DOS攻击。
4、自身消耗的DOS攻击
这是一种老式的攻击手法。说老式,是因为老式的系统有这样的自身BUG。比如Win95 (winsock v1), Cisco IOS v.10.x, 和其他过时的系统。
这种DOS攻击就是把请求客户端IP和端口弄成主机的IP端口相同,发送给主机。使得主机给自己发送TCP请求和连接。这种主机的漏洞会很快把资源消耗光。直接导致当机。这中伪装对一些身份认证系统还是威胁巨大的。
上面这些实施DOS攻击的手段最主要的就是构造需要的TCP数据,充分利用TCP协议。这些攻击方法都是建立在TCP基础上的。还有其他的DOS攻击手段。
5、塞满服务器的硬盘
通常,如果服务器可以没有限制地执行写操作,那么都能成为塞满硬盘造成DOS攻击的途径,比如:
发送垃圾邮件。一般公司的服务器可能把邮件服务器和WEB服务器都放在一起。破坏者可以发送大量的垃圾邮件,这些邮件可能都塞在一个邮件队列中或者就是坏邮件队列中,直到邮箱被撑破或者把硬盘塞满。
让日志记录满。入侵者可以构造大量的错误信息发送出来,服务器记录这些错误,可能就造成日志文件非常庞大,甚至会塞满硬盘。同时会让管理员痛苦地面对大量的日志,甚至就不能发现入侵者真正的入侵途径。
向匿名FTP塞垃圾文件。这样也可以塞满硬盘空间。
1.使用socket服务器来传递文件 2.书写udp客户端 3.使用socket封装post请求
`服务端
import socket
adress=("127.0.0.1", 30001)
ss=socket.socket(socket.AF_INET,socket.SOCK_STEAM)
ss.bind(adress)
ss.listen()
while 1:
cli,add = ss.accept()
msg = cli,recv(1024)
if not msg:
break
print(msg.decode('utf8'))
cli.send(mag.encode("utf8"))
cli.close
`
`客服端
import socket
ss=socket.socket(socket.AF_INET, socket.SOCk_STEAM)
address = ("127.0.0.0.1", 30001)
ss.connert(address)
while 1:
msg = input("请输入消息内容")
if not msg:
break
ss.send(msg.encode("utf8"))
ss_msg=ss.recv(1024)
print(ss_msg.decode("utf8"))
ss.close
`
`
import socket
from urllib.parse import urlparse
# 初始化socket
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
domain = "https://www.baidu.com"
url = urlparse(domain) #返回五个参数,字典形式
host = url.netloc
path = url.path
if path == "":
path = "/"
ss.connect((host, 80))
#'\r'的本意是回到行首,'\n'的本意是换行
data = "GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\nUser-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\r\n\r\n".format(path, host).encode('utf8')
ss.send(data)
res = b""
#报文就不像聊天数据那么小了 需要循环的拼接报文
while True:
d = ss.recv(1024)
if d:
res += d
else:
break
print(res.decode('utf8'))
ss.close()
`
`
# 两个队列,一个生产者不断的去队列里面存放任务,一个消费者,不断的取队列里面消费任务
import time #时间库
import random #随机数库
import queue #线程安全的队列
import threading #多线程
def product_task_to_queue(queue):
'''
生产者:
不断的像队列中去加入任务,每隔1,3秒加入一次
:return:
'''
task_num = 1
while True:
print('product task', str(task_num),'task_size',queue.qsize())
queue.put(str(task_num)) #向队列添加数据
sleep_time = random.randint(1, 2) #随机1-2秒的时间
time.sleep(sleep_time) #随机休息
task_num += 1 #自我加一,记录数据
def consume_task_from_queue(queue):
"""
消费者:
不断的向队列中获取任务,然后消费任务,每隔2-4秒消费一次
:param queue:
:return:
"""
while True: #循环进行拿取任务
task_num = queue.get()
sleep_time = random.randint(3, 4)
time.sleep(sleep_time) #随机进行睡眠3-4秒
consume_task(task_num,queue) #将任务交付给处理函数进行处理
def consume_task(task_num,queue):
"""
消费者处理任务的函数
:param task_num:
:return:
"""
print('consume task num:', task_num,'task_size',queue.qsize())#打印数据
def loop():
"""
启动函数
:param task_num:
:return:
"""
que = queue.Queue(maxsize=10) #添加queue队列,限制任务上限为10
t1 = threading.Thread(target=product_task_to_queue,args=(que,)) #设置线程1
t2 = threading.Thread(target=consume_task_from_queue, args=(que,))#设置线程2
t1.start() #启动线程1
t2.start() #启动线程2
t1.join() #阻塞主线程
t2.join() #阻塞主线程
loop()
`
并行:并行就是指同一时刻有两个或两个以上的“工作单位”在同时执行,单线程永远无法达到并行状态。可以利用多线程和度进程到达并行状态。
并行:使多个操作可以在重叠的时间段内进行 ,这里的重点在于重叠的时间内, 重叠时间可以理解为一段时间内(即在a事件没有执行完的过程中,还进行着B C D事件,他们执行中有着重叠的时间)。单核也可以并发(CPU分时间片);
异步: 多任务, 多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多条运行主线
同步: 多任务, 多个任务之间执行的时候要求有先后顺序,必须一个先执行完成之后,另一个才能继续执行, 只有一个主线
io多路复用模型:5种IO模型;
阻塞IO 阻塞式等待IO事件,在等的过程中不能做其他事。
非阻塞IO 非阻塞等待,不断检测IO事件是否就绪。没有就绪就可以做其他事。
信号驱动IO linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO时间就绪,进程收到SIGIO信号。然后处理IO事件。
IO复用/多路转接IO linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。知道有数据可读或可写时,才真正调用IO操作函数
异步IO linux中,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。
前4种模型都有等和IO两个阶段,并将数据从内核拷贝到调用者的缓冲区,自己等,自己进行数据搬迁。所以统称为同步IO。 与第5种异步IO相区分。
Level_triggered(水平触发):
当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!
Edge_triggered(边缘触发):
当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!
几种IO模型的触发方式
select(),poll()模型都是水平触发模式, 信号驱动IO是边缘触发模式, epoll()模型即支持水平触发,也支持边缘触发,默认是水平触发。
`
server端
# python select io多路复用测试代码
# 1. 简单的使用select来进行客户端多连接
import select
import socket
import queue
# select 把socket放入 select中,然后每当有一个连接过来,把连接conn放入select模型里面去
port = 19869
ip = "127.0.0.1"
ss = socket.socket()
ss.bind((ip, port))
ss.listen(10)
read_list = [ss]
write_list = []
msg_list = []
while 1:
rlist, wlist, xlist = select.select(read_list, write_list, [], 1)
for i in rlist:
if i is ss:
# 如果ss准备就绪,那么说明ss就可以接受连接了,当ss接受到连接
# 那么把连接返回readlist
conn, addr = i.accept()
read_list.append(conn)
# 如果不是socket对象,那么就是conn连接对象了,如果是conn连接对象,那么就代表有
# 读入数据的变化,对应recv方法
else:
try:
data = i.recv(1024)
# 如果接受不到数据了 则说明连接已经关闭了
if not data:
print('connecting close')
read_list.remove(i)
break
# 我们去发送数据,但是我们要把conn准备好了再去发送
# 所以首先把数据存在一个dict中msg_list,然后再等他准备好的时候
msg_list.append(data)
for i in read_list:
if i is not ss:
write_list.append(i)
except Exception:
read_list.remove(i)
for j in range(len(wlist)):
conn = wlist[j]
# 把对应各自的消息取出来
if j == len(wlist) -1:
msg = msg_list.pop()
else:
msg = msg_list[0]
try:
conn.send(msg)
# 回复完成后,一定要将outputs中该socket对象移除
write_list.remove(conn)
except Exception:
# 如果报错就所以连接或者已经断开了,那么我们就把他移出出去
write_list.remove(conn)
client端(多线程)
# python select io多路复用测试代码
# 1. 简单的使用select来进行客户端多连接
import select
import socket
import threading
# select 把socket放入 select中,然后每当有一个连接过来,把连接conn放入select模型里面去
port = 19869
ip = "127.0.0.1"
def input_line(ss):
while True:
msg = input("input:")
ss.send(msg.encode('utf8'))
def read_line(ss):
while True:
msg = ss.recv(1024)
print('revice from',msg)
def loop():
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ss.connect((ip, port))
t1 = threading.Thread(target=input_line,args=(ss,))
t2 = threading.Thread(target=read_line, args=(ss,))
t1.start()
t2.start()
t1.join()
t2.join()
if __name__ == '__main__':
loop()
协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。
一个程序可以包含多个协程,可以对比与一个进程包含多个线程,因而下面我们来比较协程和线程。我们知道多个线程相对独立,有自己的上下文,切换受系统控制;而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程由当前协程来控制。
从上述字面上的理解为,多线程的暂停与启动是操作系统来控制的,比如gil等。协程是单线程,在单个线程里面能够有能暂停,启动和向函数中去传递值的功能我们就叫做协程
。
python中 生成器 yield正好有这个暂停和启动的功能。我们可以思考使用生成器来作为协程使用。
##怎么去启动一个协程,怎么去关闭一个协程,怎么向协程中去发送值
next或者send(none)去激活协程,使他移动到yield处停止
使用 send 给协程发送值过去,协程向下执行
使用next就不会发送值,或者发送的值是none。协程向下执行
使用close可以主动的停止协程
`
def count(k):
total = 0.0
avg = 0.0
num = 0
while True:
data = yield avg #等待激活
if not data: #判断是否完成
break
total += data
num += 1
avg = total / num
print(avg)
print('{}完成!'.format(k))
return num, avg
def pipe( key):
print(’{}开始’.format(key))
while True:
yield from count(key)
def start(data):
for k, v in data.items(): #拆包
group = pipe( k)
next(group) #激活协程
for v1 in v:
group.send(v1) #传递值并激活yield向下执行
group.send(None) #执行完
data = {
‘1班’: [91, 79, 62, 57],
‘2班’: [92, 63, 43, 33, 63],
‘3班’: [10, 56, 73, 94, 49, 98, 99]
}
start(data)
`
要求: 1. 判断接受过来的参数,如果参数类型是str 则把他加入到一个list里面去,如果参数类型是int则把他加入到另外一个list里面去 2. 当外面的程序向yield函数抛出异常的时候,yield函数能够返回存储str的list和存储Int的list
`
def getnum():
while 1:
num = yield intlist,strlsit
try:
if type(num) is str:
strlsit.append(num)
elif type(num) is int :
intlist.append(num)
except StopIteration:
raise StopIteration
def start():
while 1:
numlist= input("请输入一个数,输入quit作为结束")
if numlist== "quit":
print("str列表",strlsit,"\n""int列表",intlist)
break
else:
num = panduan(numlist)
gg = getnum()
next(gg)
try:
gg.send(num)
except StopIteration:
print(intlist ,strlsit)
def panduan(numlist):
if numlist.isdigit(): # 判断输入是否都是数字"1231321"
return int(numlist)
else:
return numlist
if __name__=="__main__":
strlsit = []
intlist = []
start()
`
`
import collections #集合模块,提供了许多有用的集合类。
import queue
import random
class Simulator:
def __init__(self):
self.events = queue.PriorityQueue() #优先队列,根据的是第一个参数判断优先级排序
self.taxis = {}
self.Event = collections.namedtuple('Event', 'time proc action')
#namedtuple给tuple起个名字,有tuple元祖的性质
# taxi代表出租车的事件循环,默认出发时间为零分钟
# trips载客次数 ident出租车id
def taxi_process(self,ident, trips, start_time=0):
time = yield self.Event(start_time, ident, 'leave garage:出发时间') # 出发时间
for i in range(trips):
time = yield self.Event(time, ident, 'pick up passenger:接客时间') # 接客时间
time = yield self.Event(time, ident, 'drop off passenger:送达目的') # 送达目的地下客
yield self.Event(time, ident, 'going home回家') # 回家
def run(self,end_time): #激活出租车(生成器)
for k, i in self.taxis.items():
first_event = next(i) #第一个事件
self.events.put(first_event) #添加事件
sim_time = 0
while sim_time < end_time:
if self.events.empty(): #Queue.empty()如果队列为空,返回True,反之False,
# 可设置出租车数量为零时触发
print('end events')
break
current_event = self.events.get() #从优先队列中取事件,根据时间先后排序取值
sim_time,taxi_id,actions = current_event #tuple元祖拆包,比如 a,b,c = (1,2,3)
print('taxi:',taxi_id,'at:',sim_time,'do',current_event) #打印
next_time = sim_time + random.randint(1,8) #下一个事件的时间
try:
next_event = self.taxis[taxi_id].send(next_time) #从taxis字典里找到对应的出租车进行send
#send 给协程发送值过去,协程向下执行
except StopIteration:
del self.taxis[taxi_id] #删除出租车id
else:
self.events.put(next_event) #添加出租车id和时间 到优先队列queue.PriorityQueue中
else:
print('time out时间段内事件结束')
def main(self,taxi_num):
for i in range(taxi_num): # 出租车的id
# 生成出租车的对象,间隔发车,运营次数随机
self.taxis[i]=self.taxi_process(i, random.randint(10, 20), start_time=random.randint(0, 10))
self.run(120) # 调运run,运行时间120
if __name__ == '__main__':
ss = Simulator()
ss.main(2) #出租车数量
`
`
import asyncore #异步协程
import asynchat #异步通信模块asynchat
import socket
"""
相比python原生的socket api,asyncore具备有很大的优势,asyncore对原生的socket进行封装,提供非常简洁优秀的接口,利用asyncore覆写相关需要处理的接口方法,就可以完成一个socket的网络编程,从而不需要处理复杂的socket网络状况以及多线程处理等等。
"""
# asyncore.dispatcher服务器类,主要用于管理tcp的初始化,连接,关闭关闭连接等 ,一个底层套接字对象的简单封装。这个类有少数由异步循环调用的,用来事件处理的函数.
class ChatServer(asyncore.dispatcher):
def __init__(self,list_num=20,host="0.0.0.0",post=19528):
asyncore.dispatcher.__init__(self) #asyncore.dispatcher管理初始化内容
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)#使用socket协议
# tcp端口连接会有一个time_wait状态,使他能够立刻重启,不去timewait等待
self.set_reuse_addr() #即如果可能的话,尝试重用服务器端口
self.bind((host, post)) # 绑定端口
self.listen(list_num) #监听
self.clients = [] #列表
#已经启动
print('***server start***')
def handle_accept(self): #当作为server socket监听的时候,有客户端连接的时候,利用这个方法进行处理。
conn, addr = self.accept() #解析,conn套接字对象,可以用来接收和发送数据。addr用户地址ip端口
c_ip,c_port = addr #拆包 等到ip 和端口
print('client {} enter'.format(c_ip))
ChatSession(self, conn, c_ip,c_port,self.clients) #传参
def handle_close(self): #连接关闭的时候执行该方法
"""
Implied by a read event with no data available
没有可用数据的读事件
:return:
"""
print('服务器已经关闭')
# self.close()
pass
def handle_error(self):
print('服务器已经异常')
pass
class ChatSession(asynchat.async_chat):
def __init__(self, server,conn,addr,port,clients):
asynchat.async_chat.__init__(self, conn)
#把自己的连接加入到连接表里面去,向所有人广播
if self not in clients: #去除重复数据
clients.append(self)
self.server = server #服务器对象
self.addr = addr #ip
self.port = port #端口
self.set_terminator(b'out') #表示退出的动作,这里客户端敲击out就可以退出
self.clients = clients #列表
#给用户起一个名字
self.user = '用户'+str(self.addr)+str(self.port) #用户+ip+端口,
#给字符串加码,以便以传输
def encode_msg(self,msg):
#如果不是二进制 转化成二进制
if not isinstance(msg,bytes):#判断是否为字节bytes
msg = msg.encode('utf8')
return msg
def decode_msg(self,msg):
#如果不是unicode 换成undicode
if isinstance(msg,bytes):
msg = msg.decode('utf8')
return msg
def collect_incoming_data(self, data):
#如果收到了连接
if len(data) != 0: #如果消息不为空
self.push(data) #回传消息
self.broadcasting(data) #传参给广播
def broadcasting(self,msg):
"""
广播消息给其他客户端
:param msg:
:return:
"""
user_str = self.decode_msg(self.user) #解码
msg_str= self.decode_msg(msg) #解码
total_str = "{} 说:{}".format(user_str,msg_str)
total_str = self.encode_msg(total_str) #编码
for client in self.clients:
if client is not self:
client.push(total_str) #回传消息
def found_terminator(self): #当客服端关闭时
print('accept close')
#xx客户端关闭
self.close_when_done() #完成消息后关闭
self.clients.remove(self) #从list用户中移除
user_str = self.decode_msg(self.user)
total_str = "{}退出聊天".format(user_str)
total_str = self.encode_msg(total_str)
self.broadcasting(total_str) #广播消息
s = ChatServer()
asyncore.loop() #用于循环监听网络事件,loop()激活所有通道服务,执行直到最后一个通道关闭
客服端
# python select io多路复用测试代码
# 1. 简单的使用select来进行客户端多连接
import select #io多路复用
import socket # 用于在两个基于tcp/ip协议的应用程序之间相互通信
import threading #多线程
# select 把socket放入 select中,然后每当有一个连接过来,把连接conn放入select模型里面去
port = 19869
ip = "127.0.0.1"
def input_line(ss): #传送消息
while True:
msg = input("input:")
ss.send(msg.encode('utf8'))
def read_line(ss): #接收消息
while True:
msg = ss.recv(1024)
print('revice from',msg)
def loop():
ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ss.connect((ip, port))
t1 = threading.Thread(target=input_line,args=(ss,))
t2 = threading.Thread(target=read_line, args=(ss,))
t1.start()
t2.start()
t1.join()
t2.join()
if __name__ == '__main__':
loop()
`