第二十七天- 网络通信协议 TCP UDP 缓冲区
1.网络通信协议
osi七层模型:按照分工不同把互联网协议从逻辑上划分了层级
socket层
2.理解socket:
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。我们可理解成模块,直接拿来用。
套接字socket历史:
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
基于文件类型的套接字家族:
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族:
套接字家族的名字:AF_INET
AF_INET6被用于ipv6,还有一些其他的地址家族,不过,基本没用,所有地址家族中,AF_INET是使用最广泛的一 个 ,python支持多种地址家族,不过我们主要用网络编程,所以主要还是AF_INET
3.基于TCP和UDP两个协议下socket的通讯
TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
tcp协议下的socket:
服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
注意: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() #关闭服务器套接字(可选)
client端
import socket
sk = socket.socket() # 创建客户套接字
sk.connect(('127.0.0.1',8898)) # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024) # 对话(发送/接收)
print(ret)
sk.close() # 关闭客户套接字
相关bug:
1.socket绑定IP和端口时可能出现下面的问题:不让重复使用端口
#加入一条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() #关闭服务器套接字(可选)
View 解决办法 Code
若任然报错,出现 OSError: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。那么只能换端口了,因为你的电脑不支持端口重用。
2.远程主机强迫关闭了一个先有连接
这是由于强制断开造成的,解决很简单,谁依赖于谁,先关掉依赖者,再关闭被依赖者就好;还有一种是和多个连接造成,tcp协议下最好一对一,一对多可见下面代码。
import socket server = socket.socket()
ip_port = ('127.0.0.1',8081)
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 固定写法,允许地址重用,若还报错OSerror,系统原因,改端口 server.bind(ip_port)
server.listen() # 监听 可跟参数 n 代表监听n+1次 如,listen(3),意思是我连接着一个,后面还有3排队,共4个. while 1:
conn,addr = server.accept() # 阻塞等待连接
while 1:
server_msg = input('>>>>> ')
server_msg = server_msg.encode('utf-8')
conn.send(server_msg) # 发消息
if server_msg == 'byebye': # 多个客户端连接时,结束前一个后,跳出当前循环到上一个while,重新获得连接
break from_client_msg = conn.recv(1024) # 接消息
from_client_msg = from_client_msg.decode('utf-8')
if from_client_msg == 'byebye':
break
print('来自客户端的消息:',from_client_msg)
conn.close()
View 一对多_服务端 Code
import socket client = socket.socket()
server_ip = ('127.0.0.1',8081)
client.connect(server_ip) while 1:
from_server_msg = client.recv(1024)
from_server_msg = from_server_msg.decode('utf-8')
print('来自服务器>>>',from_server_msg)
if from_server_msg == 'byebye':
break client_msg = input('>>>>> ')
client_msg = client_msg.encode('utf-8')
client.send(client_msg)
if client_msg == 'byebye':
break client.close()
View 一对多_客户端 Code
# 再来一份即可
import socket client = socket.socket()
server_ip = ('127.0.0.1',8081)
client.connect(server_ip) while 1:
from_server_msg = client.recv(1024)
from_server_msg = from_server_msg.decode('utf-8')
print('来自服务器>>>',from_server_msg)
if from_server_msg == 'byebye':
break client_msg = input('>>>>> ')
client_msg = client_msg.encode('utf-8')
client.send(client_msg)
if client_msg == 'byebye':
break client.close()
View 一对多_客户端01 Code
总结:用socket进行通信,必须是一收一发对应好。
udp协议下的socket
服务器端先初始化Socket,然后与端口绑定(bind),recvform接收消息,这个消息有两项,消息内容和对方客户端的地址,然后回复消息时也要带着你收到的这个客户端的地址,发送回去,最后关闭连接,一次交互结束。
注意:udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接,但在发消息时要跟上地址。
基本 代码:
server端
import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000)) #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr) # 对话(接收与发送)
udp_sk.close() # 关闭服务器套接字
client端:
import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)
4.练习代码:
# 用户登录作业用tcp协议下的socket写:
# 1. 服务端
# - 等待客户端来发送数据:用户名、密码
# - 本地文件中查看用户名密码是否合法。
# - 合法:登录成功
# - 否则:用户名或密码错误 # 2. 客户端
# - 用户输入:用户名、密码
# - 发送到服务端进行校验。
import socket
import time server = socket.socket()
ip_port = ('127.0.0.1',8083)
server.bind(ip_port)
server.listen()
conn,addr = server.accept() # 等待conn dic = {'张三':'','赵四':'','王八':''} client_msg = conn.recv(1024)
client_msg = client_msg.decode('utf-8') # 还原成字典
client_msg = eval(client_msg)
print(client_msg)
time.sleep(5) for k in dic:
if {k:dic[k]} == client_msg:
conn.send('登录成功!'.encode('utf-8'))
else:
conn.send('用户名或密码错误!'.encode('utf-8'))
View 服务端 Code
import socket client = socket.socket()
server_ip = ('127.0.0.1',8083)
client.connect(server_ip) k = input('请输入账户:')
v = input('请输入密码:')
# 存入字典,发送给服务端
msg = {k:v}
client.send(str(msg).encode('utf-8')) from_server_msg = client.recv(1024)
print(from_server_msg.decode('utf-8'))
View 客户端 Code
# udp协议下的socket聊天工具(类10086)
# 1. 服务端
# - 接收客户端发送的信息并作出回复。
# - 检查是否有某些指定关键字并回复消息,如果发送过来的消息中还有sb字符串,那么将sb替换成alexsb,然后和你要输入的内容组合起来发送给客户端。
# 2. 多个客户端
# - 客户端向服务端发送信息
import socket talk_server = socket.socket(type=socket.SOCK_DGRAM)
ip_port = ('127.0.0.1',8086)
talk_server.bind(ip_port) while 1:
from_client_msg,addr = talk_server.recvfrom(1024)
from_client_msg = from_client_msg.decode('utf-8')
print('来自客户端>>>',from_client_msg)
if from_client_msg == 'byebye':
break msg = input('>>> ')
if 'sb'in from_client_msg:
msg2 = from_client_msg.replace('sb', 'alexsb')
talk_server.sendto((msg+msg2).encode('utf-8'),addr)
else:
talk_server.sendto(msg.encode('utf-8'), addr) talk_server.close()
View 服务端 Code
# udp下复制多个以下代码即可实现多客户端 import socket talk_client = socket.socket(type=socket.SOCK_DGRAM)
server_ip_port = ('127.0.0.1',8086) while 1:
msg = input('>>>')
if msg == 'byebye':
break
msg = msg.encode('utf-8')
talk_client.sendto(msg,server_ip_port) from_server_msg,addr = talk_client.recvfrom(1024)
print('来自服务端>>>',from_server_msg.decode('utf-8')) talk_client.close()
View 客户端 Code
5.缓冲区:
# 缓冲区: socket对象 在接收和发送数据时都是先放到缓冲区,再到目标地址的,这样可避免网络延迟、数据丢包等.
socket缓冲区解释:
每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。
write()/send() 并不立即向网络传数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。
TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。
read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。
第二十七天- 网络通信协议 TCP UDP 缓冲区的更多相关文章
- 网络通信协议tcp,udp区别
1 网络通信协议 Tcp udp的区别 重点(*****) Tcp三次握手四次挥手(******) udp客户端多人聊天 import socket udp_client = socket.socke ...
- 分布式系统(二) --SOA 以及一些网络通信协议TCP/UDP SYN攻击
SOA(面向服务的架构):Service Oriented Architecture面向服务的架构.也就是把工程拆分成服务层.表现层两个工程.服务层中包含业务逻辑,只需要对外提供服务即可.表现层只需要 ...
- day27 网络通信协议 tcp/udp区别
今日主要内容: 一.网络通信协议 二.tcp udp协议下的socket 一.网络通信协议 1.1互联网的本质就是一系列的网络协议 本机IP地址('127.0.0.1',xxxx) 互联网连接的电脑互 ...
- Python 网络通信协议 tcp udp区别
网络通信的整个流程 在这一节就给大家讲解,有些同学对网络是既熟悉又陌生,熟悉是因为我们都知道,我们安装一个路由器,拉一个网线,或者用无限路由器,连上网线或者连上wifi就能够上网购物.看片片.吃鸡了, ...
- 27 网络通信协议 tcp udp subprocess
1.模块subprocess import subprocess cmd_str = input('请输入指令>>>') sub_obj = subprocess.Popen( cm ...
- Java网络通信协议、UDP、TCP类加载整理
网络通信协议 网络通信协议 网络通信协议有很多种,目前应用最广泛的是TCP/IP协议(Transmission Control Protocal/Internet Protoal传输控制协议/英特网互 ...
- Java第三阶段学习(八:网络通信协议、UDP与TCP协议)
一.网络通信协议 1.概念: 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传 ...
- JAVA基础之网络通信协议--TCP与UDP
个人理解: 了解区分UDP与TCP的不同,正常情况都是两者结合的使用模式!为了更好的传输,经常会开多线程进行传输的! 一.网络通信协议: 1.TCP/IP协议: 四层:应用层.传输层.网络层和链路层: ...
- java基础(31):网络通信协议、UDP、TCP
1. 网络通信协议 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样.在计算机网络中,这些连接和通 ...
随机推荐
- 小记 ArchLinux 下 Typora 无法输入中文
今天准备写一篇 Linux 下的打印机文章,打开 Typora 时我发现不管我怎么设置都无法输入中文. pacman -R typora pacman -S typora 重新安装是无效的,我突然想起 ...
- PowerDesigner生成OOM时类名属性名转换
Examples Script 1: Convert a name into a class code (JAVA naming convention)转换类名 .foreach_part(%Name ...
- 如何做好错误处理?(PHP篇)
起因 之前我在封装 PHP 一个类库的时候,如果有遇到错误(例如构造函数传参不合法的话),则直接 die() ,后来发现这种方法很不好,会直接退出程序. 所以我想到给 PHP 上异常捕获的机制了. 错 ...
- WebRTC开发基础(WebRTC入门系列2:RTCPeerConnection)
RTCPeerConnection的作用是在浏览器之间建立数据的“点对点”(peer to peer)通信. 使用WebRTC的编解码器和协议做了大量的工作,方便了开发者,使实时通信成为可能,甚至在不 ...
- Python(28)---模块和包的基本概念
一.模块 定义:在python中,一个 .py 文件就称为一个模块 使用模块的好处:最大的好处就是提高了代码的可维护性 分类(三种): python标准库 第三方模块 应用程序自定义模块 模块导入方法 ...
- 【xsy1012】KSHKM的基因工程 AC自动机DP
题目大意:给你$n$个串$p_i$,最后再给一个串$s$(字符集均为A,C,G,T四个字符中的一个).问你串$s$最少要更改多少个字符(更改后的字符也只能是ACGT),才能满足s中不包含$p_i$$( ...
- POJ 2453
#include <iostream> #include <algorithm> #include <cmath> #define MAXN 1000 #defin ...
- Android_EditText 打勾显示输入的密码 --EditText与setTransformationMethod
实现目标: 实现原理: 为CheckBox添加一个监听器事件; 实现的源码: package edu.cquptzx.showPassword; import android.app.Activity ...
- 【详解】JNI (Java Native Interface) (三)
案例三:C代码访问Java对象的实例变量 获取对象的实例变量的步骤: 1. 通过GetObjectClass()方法获得此对象的类引用 2. 通过类引用的GetFieldID()方法获得实例变量的 ...
- 行为型设计模式之模板方法(TEMPLATE METHOD)模式 ,策略(Strategy )模式
1 模板方法(TEMPLATE METHOD)模式: 模板方法模式把我们不知道具体实现的步聚封装成抽象方法,提供一些按正确顺序调用它们的具体方法(这些具体方法统称为模板方法),这样构成一个抽象基类.子 ...