python27期尚哥讲TCP:
TCP:传输控制协议(使用情况多于udp)
稳定:保证数据一定能收到
相对UDP会慢一点
web服务器一般都使用TCP(银行转账,稳定比快要重要)
TCP通信模型:
在通信之前,必须先等待建立链接 TCP的三次握手:
第一次握手:建立连接时,客户端发送SYN(请求同步)包到服务器,并进入SYN_SENT(请求连接)状态,等待服务器确认
第二次握手:服务器收到syn包,必须确认客户的SYN(x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV(SYN派遣)状态
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。客户端与服务器才正式开始传送数据
理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去 TCP服务器:
在tcp传输过程中,如果有一方收到了对方的数据,一定会发送一个ACK确认包给发送方 TCP的四次挥手:
断开一个TCP连接则需要“四次挥手”
第一次挥手:主动关闭方调用close,会发送一个长度为0的数据包以及FIN(结束标志)用来关闭主动方到被动关闭方的数据传送,
告诉被动关闭方:我已经不会再给你发数据了,但是,此时主动关闭方还可以接受数据
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,
我的数据也发送完了,不会再给你发数据了
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手 长连接:三次握手四次挥手之间分多次传递完所有数据(优酷看视频、在线游戏),长时间占用某个套接字
短连接:三次握手四次挥手之间传递少部分数据,多次握手挥手才传递完所有数据(浏览器),短时间占用
tcp服务器流程如下:1. socket创建⼀个套接字
2. bind绑定ip和port
3. listen设置最大连接数,收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求
4. accept等待客户端的链接、接收连接请求
5. recv/send接收发送数据 TCP服务端:
from socket import *
#创建套接字对象,参数使用TCP协议:
s = socket(AF_INET,SOCK_STREAM)
#绑定一个IP地址和端口号:
s.bind(("127.0.0.1",8881))
#设置监听状态和最大连接数:
s.listen(5)
如果有新的客户端来链接服务器, 那么就产⽣⼀个新的套接字
# newS⽤来为这个客户端服务(10086小妹)
# addr就可以省下来等待其他新客户端的链接
newS,addr = s.accept()
#发送数据:
newS.send("nihao".encode("utf-8"))
#接收1024个字节:
data = newS.recv(1024)
print(data) TCP客户端:
from socket import *
s = socket(AF_INET,SOCK_STREAM)
s.connect(("127.0.0.1",8881))
data = s.recv(1024)
print(data)
s.send(b"123")
s.close() 单进程服务器(每次只能服务一个客户端):
from socket import *
serSocket = socket(AF_INET, SOCK_STREAM)
localAddr = ('',7788)
serSocket.bind(localAddr)
serSocket.listen(5)
while True:
print("主进程等待新客户端")
newSocket,destAddr = serSocket.accept()
print("主进程接下来负责处理",str(destAddr))
try:
while True:
recvData = newSocket.recv(1024)
if len(recvData)>0: #如果收到的客户端数据长度为0,代表客户端已经调用close()下线
print("接收到", str(destAddr),recvData)
else:
print("%s-客户端已关闭" %str(destAddr))
break
finally:
newSocket.close()
serSocket.close() 并发服务器:
serSocket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
重新设置套接字选项,重复使用绑定的信息
# 当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,
你的程序就要用到SO_REUSEADDR选项。 创建多进程服务器:
from socket import *
from multiprocessing import Process
import time
def func(new):
while True:
new.send(b"nihao")
data = new.recv(1024)
time.sleep(1)
print(data)
if data.decode() == "886":
new.close()
break
if __name__ == "__main__":
s = socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(("192.168.19.138",7789))
s.listen(5)
try:
while True:
time.sleep(1)
print('-----主进程, , 等待新客户端的到来------’)
new,addr = s.accept()
print(‘-----主进程, , 接下来创建⼀个新的进程负责数据处理’)
p = Process(target=func,args=(new,))
p.start()
except ConnectionResetError:
print("非法中断")
#当为所有的客户端服务完之后再进⾏关闭,表示不再接收新的客户端的链接
s.close() 创建多进程客户端:
from socket import *
s = socket(AF_INET,SOCK_STREAM)
s.connect(("192.168.19.138",7789))
while True:
data = s.recv(1024)
print(data)
s.send(b"123")
s.close() 多线程服务器(耗费的资源比多进程小一些):
from socket import *
from threading import Thread
from time import sleep
# 处理客户端的请求并执⾏事情
def dealWithClient(newSocket,destAddr):
while True:
recvData = newSocket.recv(1024)
if len(recvData)>0:
print('recv[%s]:%s'%(str(destAddr), recvData))
else:
print('[%s]客户端已经关闭'%str(destAddr))
break
newSocket.close()
def main():
serSocket = socket(AF_INET, SOCK_STREAM)
serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1)
localAddr = ('', 7788)
serSocket.bind(localAddr)
serSocket.listen(5)
try:
while True:
print(‘-----主进程, , 等待新客户端的到来------’)
newSocket,destAddr = serSocket.accept()
print(‘主进程接下来创建⼀个新的线程负责处理 ‘, str(destAddr)))
client = Thread(target=dealWithClient, args=(newSocket,destAddr))
client.start()
#因为线程中共享这个套接字, 如果关闭了会导致这个套接字不可⽤,
#但是此时在线程中这个套接字可能还在收数据, 因此不能关闭
#newSocket.close()
finally:
serSocket.close()
if __name__ == '__main__’:
main() socketserver:
可以使用socketserver来创建socket用来简化并发服务器
socketserver可以实现和多个客户端通信(实现并发处理多个客户端请求的Socket服务端)
它是在socket的基础上进行了一层封装,也就是说底层还是调用的socket
服务器接受客户端连接请求——》实例化一个请求处理程序——》根据服务器类和请求处理程序类,调用处理方法。
例如:
基本请求程序类(BaseRequestHandler)调用方法 handle 。此方法通过属性 self.request 来访问客户端套接字 多线程的并发服务器旗舰版:
import socketserver
import time
#创建一个请求处理类,继承 BaseRequestHandler 并且重写父类中的 handle()
class Mysock(socketserver.BaseRequestHandler):
#在handle()中处理和客户端所有的交互,建立链接时会自动执行handle方法
def handle(self):
while True:
re = self.request
re.send(b"abc")
data = re.recv(1024)
time.sleep(1)
print(data)
#对socketserver.ThreadingTCPServer 类实例化对象,将ip地址,端口号以及自己定义的类名传入,并返回一个对象
sock = socketserver.ThreadingTCPServer(("192.168.19.138",8181),Mysock)
#对象执行serve_forever方法,开启服务端(handle_request()只处理一个请求)
sock.serve_forever()
#处理多个请求,永远执行 多线程的并发客户端旗舰版:
from socket import *
import time
s = socket(AF_INET,SOCK_STREAM)
s.connect(("192.168.19.138",8181))
while True:
data = s.recv(1024)
time.sleep(1)
print(data)
s.send(b"123")
s.close() socketserver服务端自定义类实现通信循环服务端:
import socketserver
# 自定义类来实现通信循环
class MyServer(socketserver.BaseRequestHandler):
# 必须写入handle方法,建立链接时会自动执行handle方法
def handle(self):
while True:
data = self.request.recv(1024)
# handle 方法通过属性 self.request 来访问客户端套接字
print('->client:', data)
self.request.send(data.upper())
socketserver.TCPServer.allow_reuse_address = True
server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyServer)
server.serve_forever() socketserver客户端自定义类实现通信循环客户端:
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 8080))
while True:
client.send('hello'.encode('utf-8'))
data = client.recv(1024)
print(data) 远程执行命令subprocess:
Python可以使用subprocess下的Popen类中的封装的方法来执行命令
构造方法 popen() 创建popen类的实例化对象
obj = Subprocess.Popen(data,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
data 命令内容
shell = True 命令解释器,相当于调用cmd 执行指定的命令
stdout 正确结果丢到管道中
stderr 错了丢到另一个管道中
PIPE 将结果转移到当前进程
stdout.read() 可以获取命令执行的结果
指定结果后会将执行结果封装到指定的对象中
然后通过对象.stdout.read()获取执行命令的结果,如果不定义stdout会将结果进行标准输出
print(obj.stdout.read().decode('gbk')) # 正确命令
print(obj.stderr.read().decode('gbk')) #错误命令 远程执行命令服务端subprocess:
import subprocess
from socket import *
s = socket(AF_INET,SOCK_STREAM)
s.bind(("192.168.19.138",8878))
s.listen(5)
while True:
ns,addr = s.accept()
while True:
try:
data = ns.recv(1024)
sub = subprocess.Popen(data.decode,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE )
a = sub.stdout.read().decode("gbk")
b = sub.stderr.read().decode("gbk")
ns.send((a + b).encode("utf-8"))
except ConnectionResetError:
print("服务结束")
break
ns.close()
s.close() 远程执行命令客户端subprocess:
客户端
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 8080))
while 1:
cmd = input('>>>')
if cmd == "zaijian":
break
phone.send(cmd.encode('utf-8'))
from_server_data = phone.recv(1024)
print(from_server_data.decode('gbk'))
phone.close()
#提出沾包问题 解决沾包问题:
TCP协议是面向流的协议,容易出现粘包问题
不管是recv还是send都不是直接接收对方的数据(不是一个send一定对应一个recv),而是操作自己的缓
存区(产生沾包的根本原因)
例如基于tcp的套接字客户端往服务端上传数据,发送时数据内容是按照一段一段的字节流发送的,
在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,
不能一次提取任意字节的数据,这一点和TCP是很不同的
只有TCP有粘包现象,UDP永远不会粘包 粘包不一定会发生
如果发生了:1.可能是在客户端已经粘了
2.客户端没有粘,可能是在服务端粘了
客户端粘包:
发送端需要等缓冲区满才发送出去,造成粘包
(发送数据时间间隔很短,数据量很小,TCP优化算法会当做一个包发出去,产生粘包)
服务端粘包
接收方没能及时接收缓冲区的包(或没有接收完),造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,
服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 粘包服务端:
from socket import *
import time
s = socket(AF_INET,SOCK_STREAM)
s.bind(("192.168.19.138",8881))
s.listen(5)
newS,addr = s.accept()
time.sleep(1)
data = newS.recv(1024)
data1 = newS.recv(1024)
print("1",data.decode())
print("2",data1.decode()) 粘包客户端:
from socket import *
import time
s = socket(AF_INET,SOCK_STREAM)
s.connect(("192.168.19.138",8881))
s.send("hello".encode("utf-8"))
s.send("world".encode("utf-8"))
s.close() 不合适的解决方案:
send时加上时间间隔,虽然可以解决,但是会影响效率。不可取。
问题的根源在于:接收端不知道发送端将要传送的字节流的长度
所以解决粘包的方法就是发送端在发送数据前,发一个头文件包,告诉发送的字节流总大小,然后接收端来一个死循环接收完所有数据 使用struct模块可以用于将Python的值根据格式符,转换为固定长度的字符串(byte类型) struct模块中最重要的三个函数是pack(), unpack(), calcsize()
pack(fmt, v1, v2, ...) 按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流) unpack(fmt, string) 按照给定的格式(fmt)解析字节流string,返回解析出来的tuple calcsize(fmt) 计算给定的格式(fmt)占用多少字节的内存 解决粘包服务端代码:
from socket import *
import struct
s = socket(AF_INET,SOCK_STREAM)
s.bind(("",7788))
s.listen(5)
ns,addr = s.accept()
d1 = ns.recv(4)
l = struct.unpack("i",d1)
print(l)
d2 = b""
while True:
if l < 1024:
d2 += ns.recv(l)
break
else:
d2 += ns.recv(1024)
l -= 1024
print(d2.decode("gbk")) 1.基于tcp协议完成登录认证
客户端输入用户名密码
发送到服务端
服务端认证
发送结果到客户端 服务端代码:
from socket import *
s = socket(AF_INET,SOCK_STREAM)
s.bind(("127.0.0.1",8881))
s.listen(5)
news,addr = s.accept()
data = news.recv(1024)
data1 = news.recv(1024)
if data.decode("utf-8") == "zd" and data1.decode("utf-8") == "1":
news.send("登陆成功!".encode("utf-8"))
客户端代码:
from socket import *
s = socket(AF_INET,SOCK_STREAM)
s.connect(("127.0.0.1",8881))
username = input("用户名:")
pwd = input("密码:")
s.send(username.encode("utf-8"))
s.send(pwd.encode("utf-8"))
data = s.recv(1024)
print(data.decode("utf-8")) 2,看代码写结果【如果有错误,则标注错误即可,并且假设程序报错可以继续执行】
class Foo ( object ):
a1 = 1
__a2 = 2
def __init__(self, num):
self.num = num
self.__salary = 1000
def show_data(self):
print ( self.num + self.a1 )
obj = Foo ( 666 )
print ( obj.num )
结果:666
print ( obj.a1 )
结果:1
print ( obj.__salary )
结果:AttributeError: 'Foo' object has no attribute '__salary'--AttributeError:“Foo”对象没有属性“\u salary”
print ( obj.__a2 )
结果:AttributeError: 'Foo' object has no attribute '__a2'--AttributeError:“Foo”对象没有属性“\uu a2”
print ( Foo.a1 )
结果:1
print ( Foo.__a2 )
结果:AttributeError: type object 'Foo' has no attribute '__a2'--AttributeError:类型对象“Foo”没有属性“\uu a2” 3,看代码写结果【如果有错误,则标注错误即可,并且假设程序报错可以继续执行】
class Foo ( object ):
a1 = 1
def __init__(self, num):
self.num = num
def show_data(self):
print ( self.num + self.a1 )
obj1 = Foo ( 666 )
obj2 = Foo ( 999 )
print ( obj1.num )
结果:666
print ( obj1.a1 )
结果:1
obj1.num = 18
obj1.a1 = 99
print ( obj1.num )
结果:18
print ( obj1.a1 )
结果:99
print ( obj2.a1 )
结果:1
print ( obj2.num )
结果:999
print ( Foo.a1 )
结果:1
print ( obj1.a1 )
结果:1 4,看代码写结果,注意返回值。
class Foo ( object ):
def f1(self):
return 999
def f2(self):
v = self.f1 ()
print ( 'f2' )
return v
def f3(self):
print ( 'f3' )
return self.f2 ()
def run(self):
result = self.f3 ()
print ( result )
obj = Foo ()
v1 = obj.run ()
print ( v1 )
结果: f3
f2
999
None 5,看代码写结果【如果有错误,则标注错误即可,并且假设程序报错可以继续执行】
class Foo ( object ):
def f1(self):
print ( 'f1' )
@classmethod
def f2(cls):
print ( 'f2' )
obj = Foo ()
obj.f1 ()
结果:f1
obj.f2 ()
结果:f2
Foo.f1 ()
结果:TypeError: f1() missing 1 required positional argument: 'self'--TypeError:f1()缺少1个必需的位置参数:“self”
Foo.f2 ()
结果:f2 6,看代码写结果
class Department ( object ):
def __init__(self, title):
self.title = title
class Person ( object ):
def __init__(self, name, age, depart):
self.name = name
self.age = age
self.depart = depart
def message(self):
msg = "我是%s,年龄%s,属于%s" % (self.name, self.age, self.depart.title)
print ( msg )
d1 = Department ( '人事部' )
d2 = Department ( '销售部' )
p1 = Person ( '武沛齐', 18, d1 )
p2 = Person ( 'alex', 18, d1 )
p1.message ()
结果:我是武沛齐,年龄18,属于人事部
p2.message ()
结果:我是alex,年龄18,属于人事部 8,什么是C / S架构?
答:客户端/服务器 9,为何基于tcp协议的通信比基于udp协议的通信更可靠?
1.基于连接与无连接
2.对系统资源的要求(TCP较多,UDP少)
3.UDP程序结构较简单
4.流模式与数据报模式
5.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证 10,流式协议指的是什么协议,数据报协议指的是什么协议?
cp,udp 11,什么是socket?简述基于tcp协议的套接字通信流程
1. socket创建⼀个套接字
2. bind绑定ip和port
3. listen设置最大连接数,收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求
4. accept等待客户端的链接、接收连接请求
5. recv/send接收发送数据 12,什么是粘包? socket中造成粘包的原因是什么? 哪些情况会发生粘包现象?
粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。 14,简述TCP / IP四层协议,七层协议
应用层,传输层,网络层,数据链路层
应用层,表示层,会话层,传输层,网络层,数据链路层,物理层 18,网络编程中设计并发服务器, 使用多进程与多线程, 请问有什么区别?
并发服务器使用多进程: 重新设置套接字选项,重复使用绑定的信息
当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,
你的程序就要用到SO_REUSEADDR选项。
并发服务器使用多线程:耗费的资源比多进程小一些
python27期尚哥讲TCP:的更多相关文章
- python27期尚哥讲TFTP:
TFTP介绍 :TFTP(Trivial File Transfer Protocol,简单⽂件传输协议)是TCP/IP协议簇中的⼀个⽤来在客户端与服务器之间进⾏简单⽂件传输的协议使用tftp这个协议 ...
- python27期尚哥讲网络编程:
python27day26网络编程----------------------------------------------------------------------------------- ...
- python27期尚哥讲并发编程:
python27day23并发编程----------------------------------------------------------------------------------- ...
- python27期尚哥讲数据库:
1.下载:https://www.mysql.com/ DOWNLOADS MySQL Community (GPL) Downloads »-- MySQL Community Server Loo ...
- 精神哥讲Crash(一):UnsatisfiedLinkError
版权声明:本文为腾讯Bugly原创文章,如需转载,请标明出处. 大家好,我是腾讯Bugly的精神哥(英文名:spirit),是Bugly资深码奴的同时,又是Bugly神秘的Crash实验室研究员哦 ...
- 继续我们的学习。这次鸟哥讲的是LVM。。。磁盘管理 最后链接文章没有看
LVM...让我理解就是一个将好多分区磁盘帮到一起的玩意,类似于烙大饼...然后再切 新建了一个虚拟机,然后又挂了一个5G的硬盘,然后分出了5块空间,挂载到了虚拟机上.这些步骤很简单 fdisk ...
- 第12讲 | TCP协议(下):西行必定多妖孽,恒心智慧消磨难
第12讲 | TCP协议(下):西行必定多妖孽,恒心智慧消磨难 如何做个靠谱的人? 有问有答,任务发送和接收有记录,完成有反馈. 如何实现一个靠谱的协议? TCP 协议使用的也是同样的模式.为了保证顺 ...
- 第11讲 | TCP协议(上):因性恶而复杂,先恶后善反轻松
第11讲 | TCP协议(上):因性恶而复杂,先恶后善反轻松 TCP 包头格式 我们先来看 TCP 头的格式.从这个图上可以看出,它比 UDP 复杂得多. 首先,源端口号和目标端口号是不可少的,这一点 ...
- python27期前端
第一天笔记:HTML:是一种标识性的语言css:层叠样式表是一种用来表现HTML等文件样式(效果)的计算机语言JavaScript:简称“JS”,是一种属于网络的脚本语言 常用来为网页添加各式各样的动 ...
随机推荐
- springmvc的文件上传和JWT图形验证码
相关pom依赖 <dependency> <groupId>commons-fileupload</groupId> <artifactId>commo ...
- Eclipse优化之设置不自动弹出控制台和Server
有时候Eclipse启动,控制台console不会自动跳出来,需要手工点击该选项卡才行, 按下面的设置,可以让它自动跳出来(或不跳出来): windows -> preferences ...
- Linux 部署redis集群
下载Redis 下载网址 http://www.redis.cn/download.html Redis需要gcc环境(如果已经有该环境跳过此步骤) yum install gcc-c++ 安装Red ...
- IT兄弟连 Java语法教程 流程控制语句 经典案例
使用continue忽略本次循环剩下的语句 continue的功能和break有点类似,区别是continue只是忽略本次循环剩下的语句,接着开始下一次循环,并不会终止循环:而break则是完全终止循 ...
- Unity BehaviorDesigner行为树基础总结
BehaviorDesigner——行为树,用于控制和实现AI逻辑,类似于这样: 上面这个行为树实现了这样的逻辑: 当Player有Input时按照Input值来移动,无Input时查找最近的可攻击目 ...
- 搭建Jupyter学习环境
`python notebook`是一个基于浏览器的python数据分析工具,使用起来非常方便,具有极强的交互方式和富文本的展示效果.jupyter是它的升级版,它的安装也非常方便,一般`Anacon ...
- C#上手练习1(if语句、Swich语句)
1.打印字符串. 2.调用简单方法,方法里有if语句.Swich语句. C# if else 语句是最常用的条件语句,并且 if else 语句的形式有多种,包括单一条件的 if 语句.二选一条件的 ...
- Libs - Blog签名
<div id="AllanboltSignature"> <p id="PSignature" style="padding-to ...
- 5-网宿CDN客户端推流NGB
网宿NGB调度系统(类似httpdns原理)从服务端分发给客户端推流IP,实现基于APP realip精准调度模式. 参考官网介绍:https://www.wangsu.com/content/det ...
- 1.springMVC Controller配置方式
一.手动配置方式 1.web.xml中DispatcherServlet控制器的的配置 SpringMVC也是一种基于请求驱动的WEB框架,并且使用了前端控制器的设计模式.前端控制器就是Dispatc ...