day18-网络编程基础(一)
勿骄勿燥,还是要定下心学习,还有有些没定下心
1.基础知识
2.tcp与udp协议
3.网络套接字
4.基于c/s结构的服务器客户端的实验
开始今日份总结
1.基础知识
现有的软件,绝大多数是基于C/S结构,那么就需要介绍网络编程,毕竟现在的绝大多数数据还是在网络中传输。下面先说明一些网络的基础知识,不过对于从事网络工程的来说只是很简单的基础知识,
1.1 C/S架构
C/S架构中C指的是client(客户端软件),s指的是server(服务器端软件),而本章的主要学习目的是写一个基于C/S架构的软件,客户端软件与服务器端基于网络通信。现在基本的C/S架构基本是下图这样:客户端与服务器基于网络传输互相传输数据。
1.2 B/S架构
B/S架构中的B指的是Brower(浏览器),S指的是Server(服务器端),日常中的网页浏览也是B/S架构。(不是很了解)
1.3 OSI的七层协议
了解了C/S结构的大概构成,就说一下OSI七层协议,在美国军方发展ARPA网络之后,将他公布用于学术网络之后,就大爆发一样的发展了,由于网络协议的公开性,各家发展各自的标准,以至于各种网络之间并不能互通,国际ISO标准组织就颁发一套标准七层网络协议,七层网络协议有应用层,表示层,会话层,传输层,网络层,数据链路层,物理层
应用层(7) -------------------------------------------------- 提供应用程序间的通信
表示层 (6)-------------------------------------------------- 处理数据格式以及数据加密等
会话层 (5)-------------------------------------------------- 建立,维护管理会话
传输层 (4)-------------------------------------------------- 建立主机端到端的连接
网络层 (3)-------------------------------------------------- 寻址以及路由选择
数据链路层(2)--------------------------------------------- 提供介质访问,链路管理等
物理层(1) -------------------------------------------------- 比特流传输
一般567整合为应用程序,1234为数据流层
1.4 常用的TCP/IP的五层协议
由于IOS标准组织制定标准时间长,在厂商中TCP/IP更容易理解,虽然有一些结构性的缺陷,但是TCP/IP已经成为名副其实的标准了
主要有应用层,传输层,网络层,数据链路层,物理层
应用层 -------------------------------------------------- 用户数据
传输层 -------------------------------------------------- TCP报头+上层数据
网络层 -------------------------------------------------- IP报头+上层数据
数据链路层 --------------------------------------------- LLC报头+上层数据+FCS MAC报头+上层数据+FCS
物理层 -------------------------------------------------- 0101的Bit
以上都是由上向下传输或者是由下向上传输
2. TCP与UDP
基于TCP/IP协议,主要有俩种传输形式,一种是TCP,一种UDP
TCP(传输控制协议):面向连接 重传机制 确认机制 流量控制 (保证可靠)
UDP:面向无连接 低开销 传输效率高,速度快
2.1 TCP的三次握手与四次挥手
TCP由于传输数据,要和对端要先建立通道才可以传输数据,所以被称之为可靠的传输协议,传输数据之前需要建立通道,等通道建立成功后,发送数据片段,每发送一个数据片段,发送一个确认码ack,发送端只有在收到ack确认码才会发送下一个数据片段,否则会重新发送未被确认数据片段。由于要确认的东西很多,所以TCP的报头有20字节。这样TCP传输就很占用传输带宽。
以下图片就是三次握手以及四次挥手的过程,这个会后面网络编程中较大联系
2.2 UDP协议
UDP协议由于不需要和对端确认通道以及对方是否存在,只需要知道对端是谁就可以,所以UDP也被称之为不可靠传输协议。UDP协议只负责传送,不负责数据是否到达,所以低开销,传输速率高,UDP头部只有8字节。
2.3端口基础
端口范围在0----65535(2*16)
知名端口号(0----1023,其他软件禁止使用),
注册端口号(1024----49151,一般用于软件注册,不过一些知名的端口还是建议不使用)
随机端口号(49152----65535,一般用于客户端软件,随机端口)
3.基于c/s结构的服务器客户端的实验
3.1基础知识点-socket
对于上面网络基础了解后,我们可以这么想以后我们自己敲代码了,那我是不是就需要记住这些几层协议,传输层,网络层具体做什么,这个时候就需要一个新的模块了,socket,python中处理网络编程相关的问题,Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部。这样的话,用户就不需要再次了解下面具体怎么实施,只需要知道怎么操作socket就好了,结构如图。
3.2.1socket的套接字
family(socket家族)
- socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成
- socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
socket type类型
- socket.SOCK_STREAM #for tcp
- socket.SOCK_DGRAM #for udp
- socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
- socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
- socket.SOCK_SEQPACKET #废弃了
(Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)
服务器端套接字类型
- s.bind() 绑定(主机,端口号)到套接字
- s.listen() 开始TCP监听
- s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
用户端套接字类型
- s.connect() 主动初始化TCP服务器连接
- s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公用套接字类型
- s.recv() 接收数据
- s.send() 发送数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完,可后面通过实例解释)
- s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
- s.recvfrom() Receive data from the socket. The return value is a pair (bytes, address)
- s.getpeername() 连接到当前套接字的远端的地址
- s.close() 关闭套接字
- socket.setblocking(flag) #True or False,设置socket为非阻塞模式,以后讲io异步时会用
- socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) 返回远程主机的地址信息,例子 socket.getaddrinfo('luffycity.com',80)
- socket.getfqdn() 拿到本机的主机名
- socket.gethostbyname() 通过域名解析ip地址
3.3 tcp建立连接的过程
4.实验
4.1实验一
目的:基于TCP建立一个简单的服务端与客户端,服务端能够接收客户端传输过来的值
服务器端
import socket sk = socket.socket()#实例化对象
sk.bind(('127.0.0.1',8500))#对象绑定
sk.listen(3)#服务端监听 print('loading.....')
conn,addr = sk.accept()
msg = conn.recv(1024)
print(msg) conn.close()
sk.close()
客户端
import socket sk = socket.socket()
sk.connect(('127.0.0.1',8500))#客户端连接 sk.send(b'') sk.close()
4.2实验二
目的:基于TCP实现客户端与服务器之间的可以退出的聊天程序
#服务器
import socket sk = socket.socket()#实例化对象
sk.bind(('127.0.0.1',8500))#对象绑定
sk.listen()#服务端监听 print('loading.....')
conn,addr = sk.accept()
print(addr)
while True:
msg = conn.recv(1024).decode()
if msg =='q':break
else:
print(msg)
msg1 = input('>>>').strip().encode()
conn.send(msg1)
if msg1 =='q':break conn.close()
sk.close()
#客户端
import socket sk = socket.socket()
sk.connect(('127.0.0.1',8500))#客户端连接 while True:
msg = input('>>>').strip().encode()
sk.send(msg)
if msg =='q':break
ret = sk.recv(1024).decode()
if ret =='q':break
else:print(ret) sk.close()
我们来说一下程序中系统的实现方法,对于我们写的小程序,并不是我们直接建立连接,服务器端与客户端之间直接收发数据,真正的做法就是我们把发送的数据交给操作系统,操作系统在调用物理硬件将数据发出,接收端也是发送信息交给操作系统让他从网卡这里获取收到的数据,传递给应用程序。
补充一个:服务器端接收数据中的conn是一个套接字对象,是一个基于TCP协议建立的一个链接
4.3实验三
目的:基于TCP实现简单的时间服务器
#服务器端
import time
import socket sk = socket.socket()
sk.bind(('127.0.0.1',8500))
sk.listen() conn,addr = sk.accept()
data = conn.recv(1024).decode()
time_date = time.strftime(data).encode()
conn.send(time_date) conn.close()
sk.close()
#客户端
import socket sk = socket.socket()
sk.connect(('127.0.0.1',8500)) msg ='%Y.%m.%d %H:%M:%S'
sk.send(msg.encode())
date=sk.recv(1024).decode()
print(date) sk.close()
4.4实验
基于TCP测试一次性发送多个字符串
#服务端
import socket sk = socket.socket()
sk.bind(('127.0.0.1',8500))
sk.listen() conn,addr = sk.accept()
msg = conn.recv(1024).decode()
print(msg) conn.close()
sk.close()
#客户端
import socket sk = socket.socket()
sk.connect(('127.0.0.1',8500)) sk.send(b'hello')
sk.send(b'world')
print('......') sk.close()
我们会发现服务端收到了一个b’helloworld’这么一个bytes类型的字符串,这个现象就叫做黏包现象
先解释一下黏包的产生:
它的发生主要是因为socket缓冲区导致的,你的程序实际上无权直接操作网卡的,你操作网卡都是通过操作系统给用户程序暴露出来的接口,那每次你的程序要给远程发数据时,其实是先把数据从用户态copy到内核态,这样的操作是耗资源和时间的,频繁的在内核态和用户态之前交换数据势必会导致发送效率降低, 因此socket 为提高传输效率,发送方往往要收集到足够多的数据后才发送一次数据给对方。若连续几次需要send的数据都很少,通常TCP socket 会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
还是看上图,发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。怎样定义消息呢?可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候,无论底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。
这里我们说一下send与recv的区别
- send:不管recv还是send并不是直接接受或者发送对方的数据,而是操作自己操作系统的内存,将数据copy一份给内存,不过并不是一个send对应一个recv
- recv:主要是有俩个过程wait data与copy data 俩个过程,wait data 时间是最长的,中间要经过网络传输,内存获取到数据将数据copy到应用层
例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
既然有问题,那就解决问题,既然为了解决这个问题,就会引入管道这个类
#服务端
import socket
import struct sk = socket.socket()
sk.bind(('127.0.0.1',8500))
sk.listen() conn,addr = sk.accept()
while True:
send_msg = input('>>>').strip().encode('utf-8')
msg_len = struct.pack('i',len(send_msg))
conn.send(msg_len)
conn.send(send_msg) conn.close()
sk.close()
import socket
import struct sk = socket.socket()
sk.connect(('127.0.0.1',8500)) data =sk.recv(4)
msg_len = int(struct.unpack('i',data)[0])
print(msg_len)
recive_len = 0
msg =b''
while recive_len<msg_len:
date2 = sk.recv(5)
msg +=date2
recive_len+=5
print(msg.decode()) sk.close()
4.5实验
目的:基于TCP服务器端给客户端传文件,验证客户端接收的文件完整性,动态的显示接收进度
#服务器端
import socket
import struct
import json
sk = socket.socket()
sk.bind(('127.0.0.1',8500))
sk.listen()
conn,addr = sk.accept() header ={'filename':r'D:\test.zip','file_size':922933359,'MD5':'6237eb2c55b34f87e856422896c1f440'} header_json = json.dumps(header)#将头文件字典转换为json模式
header_bytes = header_json.encode('utf-8')#将json文件转换为bytes类型
header_len = struct.pack('i',len(header_bytes))#将头文件从管道发送过去
conn.send(header_len)
conn.send(header_bytes)
with open(header['filename'],'rb')as f1:
while True:
contact = f1.read(1024)
if contact:
conn.send(contact)
else:
break
print('文件传输完毕!')
conn.close()
sk.close()
#客户端
import socket
import struct
import json
import hashlib
import sys sk = socket.socket()
sk.connect(('127.0.0.1',8500)) data =sk.recv(4)#接收头文件那四个字节
head_len = int(struct.unpack('i',data)[0])
head_bytes = sk.recv(head_len)#接收头文件 head_json = head_bytes.decode('utf-8')#将bytes类型的头文件解析成json格式
header = json.loads(head_json)#将json格式的文件反解成字典 def check_md5(file):#验证文件MD5
ret = hashlib.md5()
with open(file,mode='rb')as f2:
while True:
contect = f2.read(1024)#读取1024字节
if contect:
ret.update(contect)
else:
break
return ret.hexdigest() receive_num =0
with open('test.zip','wb')as f1:
while receive_num < header['file_size']:
contact = sk.recv(1024)
if contact:
f1.write(contact)
receive_num += 1024
float_rate =receive_num/header['file_size']
rate = round(float_rate * 100, 2)
sys.stdout.write('\r已下载:\033[1;32m{0}%\033[0m'.format(rate))#动态显示接收进度!
else:
break
print('文件下载成功!')
num =check_md5('test.zip')
if num ==header['MD5']:
print('文件校验成功,文件完整')
else:
print('文件校验失败,文件不完整') sk.close()
day18-网络编程基础(一)的更多相关文章
- C#网络编程基础知识
C#网络编程基础知识一 1.IPAddress类 用于表示一个IP地址.IPAddress默认构造函数 public IPAddress(long address);一般不用 其中Parse()方法最 ...
- iOS开发网络篇—网络编程基础
iOS开发网络篇—网络编程基础 一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过 ...
- Android 网络编程基础之简单聊天程序
前一篇讲了Android的网络编程基础,今天写了一个简单的聊天程序分享一下 首先是服务端代码: package com.jiao.socketdemo; import java.io.Buffered ...
- 服务器编程入门(4)Linux网络编程基础API
问题聚焦: 这节介绍的不仅是网络编程的几个API 更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系. 这节主要介绍三个方面的内容:套接字( ...
- Java网络编程基础(Netty预备知识)
今天在家休息,闲来无事,写篇博客,陶冶下情操~~~ =================我是分割线================ 最近在重新学习Java网络编程基础,以便后续进行Netty的学习. 整 ...
- 用Netty开发中间件:网络编程基础
用Netty开发中间件:网络编程基础 <Netty权威指南>在网上的评价不是很高,尤其是第一版,第二版能稍好些?入手后快速翻看了大半本,不免还是想对<Netty权威指南(第二版)&g ...
- Linux 高性能服务器编程——Linux网络编程基础API
问题聚焦: 这节介绍的不仅是网络编程的几个API 更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系. 这节主要介绍三个方面的内容:套接字(so ...
- Python网络编程基础pdf
Python网络编程基础(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1VGwGtMSZbE0bSZe-MBl6qA 提取码:mert 复制这段内容后打开百度网盘手 ...
- 【网络编程1】网络编程基础-TCP、UDP编程
网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...
- python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)
python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...
随机推荐
- String str=null; 和String str=""的区别
1.最大的区别在于String str=null没有分配内存,String str=""分配了内存 2.String str=null 这个引用指向了一个null ,没有地址没 ...
- Java序列化和反序列化,你该知道得更多
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后,可以通过从存储区中读取或反序列化对象的状态,重新 ...
- what a fuck!这是什么鬼东西?
Topic Link http://ctf5.shiyanbar.com/DUTCTF/1.html 1) 打开链接发现一片看不懂的东西,还真是WTF? 2)分析发现是Jother编码 将其放到浏览器 ...
- cache2go - cachetable源码分析
今天我们来看cachetable.go这个源码文件,除了前面介绍过的主要数据结构CacheTable外还有如下2个类型: 下面先看剩下2个类型是怎么定义的: CacheItemPair非常简单,注释一 ...
- Android 发送多个不同的快捷方式(shortcut)到桌面并向其启动的Activity传参
需求: 对于创建快捷方式到桌面,网上能查到不少资料,但一般都是针对应用程序本身的. 前阵子在做项目时,遇到了一个类似于百度贴吧里面的一个需求:对于每个具体的贴吧,都可以将其发送到桌面(HomeScre ...
- ASP.NET Core使用Jaeger实现分布式追踪
前言 最近我们公司的部分.NET Core的项目接入了Jaeger,也算是稍微完善了一下.NET团队的技术栈. 至于为什么选择Jaeger而不是Skywalking,这个问题我只能回答,大佬们说了算. ...
- Spring Boot 2.x(十三):你不知道的PageHelper
PageHelper 说起PageHelper,使用过Mybatis的朋友可能不是很陌生,作为一款国人开发的分页插件,它基本上满足了我们的日常需求.但是,我想去官方文档看看这个东西配合Spring B ...
- DSAPI多功能组件编程应用-图形图像篇(中)
[DSAPI.DLL下载地址] 说到计算机上使用代码来处理各种图像特效,是一份太有挑战性的工作.以下涉及的所有图像效果均不是从网上复制的源码,而是本人试验数次并编写的,所以原理上会和网上的有所不同 ...
- .NET CORE 使用Session报错:Session has not been configured for this application or request
报错信息:Session has not been configured for this application or request 解决方案:在Startup.cs文件中的Configure方法 ...
- 20190326-HTML5标签、CSS的引用
目录 1.HTML5标签 H5前的DIV+CSS布局 H5解决的问题 H5新标签header.footer.nav.aside.article.address.main.figure.figcapti ...