首页 > 技术文章 > 网络编程普及篇

ddjl 2018-01-30 18:45 原文

网络编程说白了就是通过网络进行数据的传输。分为两种架构,c/s和b/s,b/s是c/s的一种。

c/s就是client(客户端)和server(服务端),例如qq,客户端就是个人pc上的小企鹅标志。

b/s是Browser(浏览器)和server(服务端),但是现在b/sbic/s火的多,原因就在于c/s架构每次使用一个产品或服务都需要下载一个app式的客户端,而b/s架构:比如微信内部的小程序,它实际上做的事是统一了入口。

osi七层

须知一个完整的计算机系统是由硬件、操作系统、应用软件三者组成,具备了这三个条件,一台计算机系统就可以自己跟自己玩了(打个单机游戏,玩个扫雷啥的)

如果你要跟别人一起玩,那你就需要上网了,什么是互联网?

互联网的核心就是由一堆协议组成,协议就是标准,比如全世界人通信的标准是英语

如果把计算机比作人,互联网协议就是计算机界的英语。所有的计算机都学会了互联网协议,那所有的计算机都就可以按照统一的标准去收发信息从而完成通信了。

人们按照分工不同把互联网协议从逻辑上划分了层级,网络的核心即一堆协议,协议即标准,你想开发一款基于网络通信的软件,就必须遵循这些标准。

互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层

 

每层运行常见物理设备

 

物理层:电缆等物理连接设备

数据链路层:电信号0,1,以太网协议ethernet

网络层:ARP协议,作用是通过ip找到mac地址

传输层:TCP/UDP协议

应用层:各种www,FTB,Email等程序的协议

 

 

在每台计算机上都有一个端口的概念,每个应用程序每次只能使用一个端口

端口的范围是0~65535,一般使用8000以后的,前面的8000一般都是系统占用端口

 

mac:每台计算机独一无二的物理地址

 

ip地址是由四个8位二进制表示的,每个字节最多表示256位,所以ip地址的范围是:0.0.0.0~255.255.255.255

ip地址实际上就是一个交换机内的局域网地址,分为公网和内网。公网就是注册过的网址

每个计算机都有一个本地地址127.0.0.1(回环地址)

 

TCP协议:每一次发送消息都需要对方回拨确认信息,信息安全

UDP协议:每次发送信息只管发送,不管对方是否能够接收,可能导致丢包(丢数据)

TCP应用:web浏览器,电子邮件,文件传输等

UDP应用:域名系统,视频,ip语音(qq)等

 

TCP协议通过三次握手建立了双全工链接,四次挥手断开了链接

三次挥手就是客户端先发送信息:嘿,哥们我要连你;服务端回复:我也要连你;客户端:好啊

四次挥手是:客户端:嘿哥们咱俩断了吧;服务端:好啊,那就断吧;服务端:我断了啊;客户端:断了吧。

 

套接字发展史及分类

套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。 

基于文件类型的套接字家族

套接字家族的名字:AF_UNIX

unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

基于网络类型的套接字家族

套接字家族的名字:AF_INET

(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

 

所有的网络编程(无论是什么语言)都是基于socket套接字来进行的,在python当中socket是以个模块,用法分别为一个服务端一个客户端:

服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字

下面来一段简单代码来看一下socket套接字怎么用
# #服务端
# import socket
# sk = socket.socket() #买手机
# sk.bind(('127.0.0.1',8080)) #绑定电话卡
# sk.listen() #监听,等着别人打电话
# conn,addr = sk.accept() # 接收到别人的电话 connection 连接,address 地址
# ret = conn.recv(1024) # 听别人说话
# print(ret)
# conn.send(b'hello') # 和别人说话,必须传一个bytes类型
# conn.close() # 挂电话
# sk.close() # 关手机
#
# #客户端
# import socket
# sk = socket.socket() #买电话
# sk.connect(('127.0.0.1',8080)) #链接服务器
# sk.send(b'olleh') #发送信息,且只能发送bytes类型的
# ret = sk.recv(1024) #一次接收的最大字节数
# print(ret)
# sk.close() #关机

                              黏包

#解决黏包问题
# 为什么会出现黏包现象?
# 首先只有在TCP协议中才会出现黏包现象,
# 是因为TCP协议是面向流的协议
# 在发送的数据传输的过程中还有缓存机制来避免数据丢失
# 因此 在连续发送小数据的时候 以及接收大小不符的时候都容易出现黏包现象
# 本质还是因为我们在接收数据的时候不知道发送的数据的长短
# 解决黏包问题
# 在传输大量数据之前先告诉接收端要发送的数据大小
# 如果想更漂亮的解决问题,可以通过struct模块来定制协议

# struct模块
#pack unpack
#模式 :'i'
#pack之后的长度 : 4个字节
#unpack之后拿到的数据是一个元祖 :元祖的第一个元素才是pack的值

解决黏包问题两种方法:第一在连续的send()中间插上一个接收或者sleep,让程序停顿一会,
第二利用struct模块来定制报头及报文,通过len定制的数据获取要发送数据的长度来判断输入

socketsever模块,基于TCP协议来实现一个服务端多个客户端之间的相互通信



 

推荐阅读