一、网络编程
网络通信概述
什么是⽹络?⽹络就是⼀种辅助双⽅或者多⽅能够连接在⼀起的⼯具。
使⽤⽹络的⽬的:联通多⽅然后进⾏通信⽤的,即把数据从⼀⽅传递给另外⼀⽅。
TCP/IP协议
如何实现⽹络通信?协议
有的说英语,有的说中⽂,有的说德语,说同⼀种语⾔的⼈可以交流,不同的语⾔之间就不⾏了 为了解决不同种族⼈之间的语⾔沟通障碍,现规定国际通⽤语⾔是英语,这就是⼀个规定,这就是协议。
TCP/IP协议(族)
互联⽹协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP 协议,所以,⼤家把互联⽹的协议简称TCP/IP协议。
IP地址
⽤来在⽹络中标记⼀台电脑的⼀串数字,⽐如192.168.1.1;在本地 局域⽹上是惟⼀的。
每⼀个IP地址包括两部分:⽹络地址和主机地址。
回环地址IP
IP地址127.0.0.1 代表本机IP地址,等价于localhost, ⽤ http://127.0.0.1 就可以测试本机中配置的Web服务器。
UDP
UDP ⽤户数据报协议,是⼀个⽆连接的简单的⾯向数据报的运输层协议。UDP不提供可靠性,它只是把应⽤程序传给IP层的数据报发送出去,但 是并不能保证它们能到达⽬的地。由于UDP在传输数据报前不⽤在客户和服 务器之间建⽴⼀个连接,且没有超时重发等机制,故⽽传输速度很快。
UDP是⼀种⾯向⽆连接的协议,每个数据报都是⼀个独⽴的信息,包括完整 的源地址或⽬的地址,它在⽹络上以任何可能的路径传往⽬的地,因此能否到达⽬的地,到达⽬的地的时间以及内容的正确性都是不能被保证的。
UDP特点
UDP是⾯向⽆连接的通讯协议,UDP数据包括⽬的端⼝号和源端⼝号信息, 由于通讯不需要连接,所以可以实现⼴播发送。 UDP传输数据时有⼤⼩限 制,每个被传输的数据报必须限定在64KB之内。 UDP是⼀个不可靠的协 议,发送⽅所发送的数据报并不⼀定以相同的次序到达接收⽅。
socket编程
什么是socket?
socket(简称套接字) 是进程间通信的⼀种⽅式, 能实现不同主机间的进程间通信,我们⽹络上各种各样的服务⼤多都是基于 Socket 来完成通信的。
在 Python 中 使⽤socket 模块的函数 socket 就可以完成:
socket.socket(AddressFamily, Type)
1). Address Family:
AF_INET: IPV4⽤于 Internet 进程间通信
AF_INET6: IPV6⽤于 Internet 进程间通信
2). Type:套接字类型
SOCK_STREAM: 流式套接字,主要⽤于 TCP 协议
SOCK_DGRAM: 数据报套接字,主要⽤于 UDP 协 议
UDP
UDP是⾯向消息的协议,通信时不需要建⽴连接,数据的传输⾃然是不可靠 的,UDP⼀般⽤于多点通信和实时的数据业务,⽐如:
语⾳⼴播 ,视频,QQ,TFTP(简单⽂件传送) ,SNMP(简单⽹络管理协议), DNS(域名解释)
udp连接的过程
根据上面的模型,我们用udp做一个模拟聊天的工具。
首先先搭建好服务器端:
创建,绑定端口,连接
import socket
udpServer = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
udpServer.bind(('0.0.0.0', 9000))
print("正在等待客户端udp的连接。。。。")
while True:
recvdata, address = udpServer.recvfrom(1024)
print("客户端:", recvdata.decode('utf-8'))
server = input("服务器端>>:").encode('utf-8')
udpServer.sendto(b'%s' % server, address)
if recvdata== b'quit' or server.decode('utf-8') =='quit':
break
udpServer.close()
接着写一个客户端去测试服务器。
也是先创建,
import socket
udpClient= socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM)
while True:
Client = input("客户端:>>").encode('utf-8')
udpClient.sendto(b' %s '%Client, ('172.25.254.39', 9000))
recvData, address = udpClient.recvfrom(1024)
print("服务器端:", recvData.decode('utf-8'))
if recvData==b'quit' or Client.decode('utf-8') =='quit':
break
udpClient.close()
运行之后看我们测试的结果。
客户端:
服务器端结果:
这样就说明我们的模拟聊天就成功了。
当然,可以用UDP做一个拓展,就是我客户端发送linux命令,服务器反馈命令的结果。
客户端实现:
import socket
udpClient= socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM)
while True:
desktop = input("客户端:>>").encode('utf-8')
udpClient.sendto(b' %s '%desktop, ('172.25.254.39', 9000))
if desktop==b'quit':
break
udpClient.close()
服务器实现:
import socket
import os
udpServer = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
udpServer.bind(('0.0.0.0', 9000))
while True:
recvdata, address = udpServer.recvfrom(1024)
s=recvdata.decode('utf-8')
if recvdata ==b'quit':
break
obj=os.popen(s)
content=obj.read()
print('cmd >>#: %s'%content)
udpServer.close()
测试结果:
输入的客户端命令
服务器返回的结果。
TCP
TCP: 传输控制协议(英语:Transmission Control Protocol,缩写为TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP/UDP对比
TCP建立连接的过程。
tcp连接的时候有三次握手,断开的时候有四次分手。
TCP三次握⼿
两个包: 同步序列标号 SYN 确认包 ACK
四种状态: SYN_SENT, LISTEN, SYN_RECV, ESTABLISHED
在三次握手中,客户端和服务器端都发送两个包SYN和ACK,只不过服务器端的两个包是一次性发过来的,客户端的两个包是分两次发送的。
三次握手
TCP连接是通过三次握手来连接的。
第一次握手
当客户端向服务器发起连接请求时,客户端会发送同步序列标号SYN到服务器,在这里我们设SYN为x,等待服务器确认,这时客户端的状态为SYN_SENT。
第二次握手
当服务器收到客户端发送的SYN后,服务器要做的是确认客户端发送过来的SYN,在这里服务器发送确认包ACK,这里的ACK为x+1,意思是说“我收到了你发送的SYN了”,同时,服务器也会向客户端发送一个SYN包,这里我们设SYN为y。这时服务器的状态为SYN_RECV。
一句话,服务器端发送SYN和ACK两个包。
第三次握手
客户端收到服务器发送的SYN和ACK包后,需向服务器发送确认包ACK,“我也收到你发送的SYN了,我这就给你发个确认过去,然后我们即能合体了”,这里的ACK为y+1,发送完毕后,客户端和服务器的状态为ESTABLISH,即TCP连接成功。
在三次握手中,客户端和服务器端都发送两个包SYN和ACK,只不过服务器端的两个包是一次性发过来的,客户端的两个包是分两次发送的。
TCP四次分手
当A端和B端要断开连接时,需要四次握手,这里称为四次挥手。
两个包: FIN:Finish, ACK确认序号。
四次挥手
当A端和B端要断开连接时,需要四次握手,这里称为四次挥手。断开连接请求可以由客户端发出,也可以由服务器端发出,在这里我们称A端向B端请求断开连接。
第一次挥手
A端向B端请求断开连接时会向B端发送一个带有FIN标记的报文段,这里的FIN是Finish的意思。
第二次挥手
B端收到A发送的FIN后,B段现在可能现在还有数据没有传完,所以B端并不会马上向A端发送FIN,而是先发送一个确认序号ACK,意思是说“你发的断开连接请求我收到了,但是我现在还有数据没有发完,请稍等一下呗”。
第三次挥手
当B端的事情忙完了,那么此时B端就可以断开连接了,此时B端向A端发送FIN序号,意思是这次可以断开连接了。
第四次挥手
A端收到B端发送的FIN后,会向B端发送确认ACK,然后经过两个MSL时长后断开连接。
MSL是Maximum Segment Lifetime,最大报文段生存时间,2个MSL是报文段发送和接收的最长时间
理解例连接过程,我们用TCP来实现上面的模拟聊天。
首先也是服务器的搭建。
import socket
#1、创建服务端Socket对象
server=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
#2、绑定端口(Ip:Port)
server.bind(('0.0.0.0',8999))
#3、监听是否有客户端连接?
server.listen(5)
print("server start.....")
#4、接受客户端的accept
clientSocketobj,clientAddress=server.accept()
while True:
#5、接受客户端消息
recv_data=clientSocketobj.recv(1024).decode('utf-8')
if recv_data == 'quit':
break
print("客户端:",recv_data)
#6、给客户端发送消息
res=input("服务器:>>").encode('utf-8')
if not res:
continue
clientSocketobj.send(res)
if res ==b'quit':
break
#7、关闭socket对象
print("power off.....")
clientSocketobj.close()
server.close()
接着就是客户端的测试了。
import socket
# 1、创建客户端socket对象
client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
# 2、连接服务器
client.connect(('172.25.254.39',8999))
# 3、给服务器发消息
while True:
res=input("客户端:>>").encode('utf-8')
if not res:
continue
client.send(res)
# 4、接受服务器端的消息
recv_data=client.recv(1024).decode('utf-8')
if recv_data=='quit' or res==b'quit':
break
print("服务器:", recv_data)
# 5、关闭socket对象
print("power off.....")
client.close()
客户端结果:
服务器结果:
并发服务器
并发服务器是socket应用编程中最常见的应用模型。根据连接方式分为长连接和短连接。并发服务器模型根据处理方式可分为同步方式和异步方式。
多进程服务器
优点: 通过为每个客户端创建⼀个进程的⽅式,能够同时为多个客户端进⾏服务
缺点: 当客户端不是特别多的时候,这种⽅式还⾏,如果有⼏百上千个,就不 可取了,因为每次创建进程等过程需要好较⼤的资源。
实现如下:
首先也是服务器搭建。
def dealwithClient(clientSocketobj,clientAddress):
while True:
# 5、接受客户端消息
recv_data = clientSocketobj.recv(1024).decode('utf-8')
print(clientAddress[0]+' '+str(clientAddress[1])+':>>'+recv_data)
if recv_data == 'quit':
break
clientSocketobj.close()
import socket
from multiprocessing import Process
#1、创建服务端Socket对象
server=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
#2、绑定端口(Ip:Port)
server.bind(('0.0.0.0',8999))
#3、监听是否有客户端连接?
server.listen(5)
print("server start.....")
while True:
#4、接受客户端的accept
clientSocketobj,clientAddress=server.accept()
p=Process(target=dealwithClient,args=(clientSocketobj,clientAddress))
p.start()
然后也是客户端去测试。
import socket
# 1、创建客户端socket对象
client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
# 2、连接服务器
client.connect(('172.25.254.39',8999))
# 3、给服务器发消息
while True:
res=input("客户端:>>").encode('utf-8')
if not res:
continue
client.send(res)
if res==b'quit':
break
# # 5、关闭socket对象
print("power off.....")
client.close()
然后多复制几个客户端去测试我们的多进程服务器。
服务器的结果: