勿骄勿燥,还是要定下心学习,还有有些没定下心

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-网络编程基础(一)的更多相关文章

  1. C#网络编程基础知识

    C#网络编程基础知识一 1.IPAddress类 用于表示一个IP地址.IPAddress默认构造函数 public IPAddress(long address);一般不用 其中Parse()方法最 ...

  2. iOS开发网络篇—网络编程基础

    iOS开发网络篇—网络编程基础 一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过 ...

  3. Android 网络编程基础之简单聊天程序

    前一篇讲了Android的网络编程基础,今天写了一个简单的聊天程序分享一下 首先是服务端代码: package com.jiao.socketdemo; import java.io.Buffered ...

  4. 服务器编程入门(4)Linux网络编程基础API

      问题聚焦:     这节介绍的不仅是网络编程的几个API     更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系.     这节主要介绍三个方面的内容:套接字( ...

  5. Java网络编程基础(Netty预备知识)

    今天在家休息,闲来无事,写篇博客,陶冶下情操~~~ =================我是分割线================ 最近在重新学习Java网络编程基础,以便后续进行Netty的学习. 整 ...

  6. 用Netty开发中间件:网络编程基础

    用Netty开发中间件:网络编程基础 <Netty权威指南>在网上的评价不是很高,尤其是第一版,第二版能稍好些?入手后快速翻看了大半本,不免还是想对<Netty权威指南(第二版)&g ...

  7. Linux 高性能服务器编程——Linux网络编程基础API

    问题聚焦:     这节介绍的不仅是网络编程的几个API     更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系.     这节主要介绍三个方面的内容:套接字(so ...

  8. Python网络编程基础pdf

    Python网络编程基础(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1VGwGtMSZbE0bSZe-MBl6qA 提取码:mert 复制这段内容后打开百度网盘手 ...

  9. 【网络编程1】网络编程基础-TCP、UDP编程

    网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...

  10. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...

随机推荐

  1. (五)通过Python的select监控多个描述符实现并发连接

    概述 本文通过使用select改写之前的服务器程序通过监控多个套接字描述符来实现并发连接并加入了一些机制让程序更加健壮,不过我们所有的实验都是建立在单词发送数据不会超过1024字节,如果超过你需要做特 ...

  2. UGUI 中Dropdown控件的使用经验

    UGUI 中Dropdown控件的使用经验 在Untiy的UGUI 刚出来的时候,是没有“下拉列表”(Dropdown)控件的,这在无形中给我们的UI界面开发带来困难,不过在Untiy5.2.2之后这 ...

  3. 如何把你的.net程序打包上传到nuget

    写在前面 每个.net开发者都经常用nuget管理自己的程序包,install一个json组件啊,一个工具类什么的,这些都是别人写好的.如果我也写好了一个自己感觉很拿的出手的组件,想轻松的使用nuge ...

  4. LeetCode专题-Python实现之第13题:Roman to Integer

    导航页-LeetCode专题-Python实现 相关代码已经上传到github:https://github.com/exploitht/leetcode-python 文中代码为了不动官网提供的初始 ...

  5. Magicodes.NET框架之路——产品之路(谈谈产品管理)

    虽然Magicodes.NET现在还不属于产品,但是却不妨碍她想成为产品的心. 为什么突然有了此篇,这篇不是空穴来风,而是我思考良久的结果: 为了让大家知道我在干什么,我想干什么,我将要干什么还有我干 ...

  6. 日志模块logging用法

    一.常用日志记录场景及最佳解决方案: 日志记录方式 最佳记录日志方案 普通情况下,在控制台显示输出 print() 报告正常程序操作过程中发生的事件 logging.info()(或者更详细的logg ...

  7. rpm和yum软件管理(week2_day5)--技术流ken

    rpm简介 这是一个数据库管理工具,可以通过读取数据库,判断软件是否已经安装,如果已经安装可以读取出来所有文件的所在位置等,并可以实现删除这些文件. rpm:RPM is Redhat Package ...

  8. 分享:Java 开发精美艺术二维码

    博客地址:https://ainyi.com/58 Java 开发精美艺术二维码 看到网络上各种各样的二维码层出不穷,好像很炫酷的样子,一时兴起,我也要制作这种炫酷二维码效果 例如: 根据以往例子 根 ...

  9. ROS笔记3 理解nodes

    http://wiki.ros.org/ROS/Tutorials/UnderstandingNodes 介绍几个命令行工具用法 roscore rosnode rosrun A node reall ...

  10. Mysql启动时执行文件init-file的使用

    可以在配置文件里指定mysql启动以后初始执行的SQL文件, 其语法是: 在[mysqld]下指定: init-file="D:/mysql/test.sql",  后面为具体的s ...