一、网络协议
硬件在操作系统之下,操作系统在应用软件之下
1、分层模型:
应用层
传输层
网络层
数据链路层
物理层
2、协议:
(1)物理层:主要是用来发送电信号(高低电平10101011011......)
(2)数据链路层:以太网协议,主要用来把电信号进行分组,主要用到mac地址
以太网数据帧:头 + 数据 其中头占18字节,源MAC地址和目的mac地址和帧类型各6字节
(3)网络层:IP协议
ip数据帧:IP头 + IP数据
IP地址和掩码按位与运算得到的就是网络地址,即是网络标识
网关:网络的关口
ARP协议:就是用来建立IP地址到mac地址的映射关系
注意:MAC地址是可以确定自己在局域网中的位置,而IP地址是可以确定自己在哪个局域网,所以IP地址 + MAC地址就能唯一确定某个计算机所处的位置
(4)传输层:TCP和UDP协议,主要是基于端口工作,总共0-65535个端口
注意:IP地址 + 计算机的端口号就能唯一确定是哪个应用软件
(5)应用层:主要是一些http ftp mail等协议,也可自己定义协议
补充:DNS主要用来将域名解析成IP地址
3、各层之间的工作流程:
应用层将原始数据包装应用层的标识后整体送到传输层,传输层加上源和目的端口号后整体送给网络层,网络层加上源和目的IP地址后整体送给数据链路层,数据链路层加上源和目的mac地址后整体送给物理层,物理层解析成高低电平信号输出————》》传到目的计算机后,操作系统进行一层一层的解析剥离,得到最后的原始数据
4、TCP协议:主要用来建立和断开连接,不清空内存,可靠协议
(1)信号位:syn=1 代表请求 seq=x 代表序号位 ack=1+x 既代表回应意思就是告诉对方自己收到了,又代表序号位+1意思是表示对序号位为x的请求信息做出的回应 fin =1 代表断开
(2)
建立连接:3次握手,第2次第3次合并为一次
断开连接:必须4次握手
(3)工作过程:A——B
A发送连接请求到B,B同时发送回应A请求的回应包和连接请求发送到A的包,A接受到回应以后就代表A到B这条路连接起来了,同时A又收到B的连接请求包所以再发送回应B连接请求的回应包,当B收到A的回应包时就代表B到A这条路也连接起来了,所以是3次握手,B的请求和回应合为一次发送。
若是断开连接的请求,B的回应包和请求包就不能合为一次,因为A发送断开请求就代表A没有要发送的了,但是此时B不一定已经发送完毕,所以B的发送和回应不能合为一次,故应该是必须4次握手
二、网络编程:简单的套接字实现
1、导入socket模块,作用就是跟TCP或UDP协议通信
注意:本质上还是跟操作系统进行交互
2、什么是套接字:用于不同程序之间的通信,分为基于文件型(AF_UNIX)和基于网络型(AF_INET) SOCK_STREAM是TCP,SOCK_DGRAM是UDP
3、整个过程:
服务端(S)先对socket初始化(参数设定套接字家族,套接字类型)(这里我觉得可以理解为创建套接字,启动应用程序),可以赋值给新变量;然后绑定地址与端口(bind),使服务端的程序唯一性,让客户端能够知道自己和谁连接;然后对端口进行监听(listen这里可以设置数字,代表可同时挂起多少个连接);然后服务端就等待接受连接(accept)。客户端(C)首先也要对socket初始化,然后针对服务端的IP和端口进行连接(connect)。这时服务端接受到客户端的连接请求,回应以后建立起连接。客户端发起数据请求(send),服务端接收到(recv)后对数据进行处理再返回给客户端,如此往复直至客户端断开连接,服务端接受断开连接的请求后首先关闭连接(close),这样服务端与这一个客户端的一次交互就完成了,此时服务端又处在等待接受连接的状态,若再次接受到连接请求就会重复上述状态。直至服务端关闭套接字即socket初始化的变量,这样服务端就关闭了(关闭服务)。但在实际应用中,服务端理论上是不应该关闭的。
注意:
(1)对于TCP,一定要先启动服务端
(2)一台主机上的不同程序端口号是不一样的,所以在一台机上运行客户端和服务端,端口号一定是不一样的
(3)客户端发送数据时必须是字节类型,因为字符串不能用于网络传输
(4)监听时同时挂起几个连接,这里可以理解为最多支持同时有5个电话打进来
(5)服务端绑定IP和端口时必须是自己所在的机器的IP和端口,并且以元组的形式绑定,地址以字符串的形式显示,一般如果使用本机客户端和服务端通信,地址通常使用本机环回地址127.0.0.1,这样就绝不会和别的计算机的地址冲突
(6)客户端连接服务端时的IP和端口必须是服务端绑定的IP和端口
(7)服务端接受连接请求(accept)时输出元组,元组第一个元素就是连接,第二个元素仍是元组,元组里包含客户端的地址和端口号
(8)注意客户端和服务端在接受数据时recv(1024)一定要加上最大接受多少
(9)若遇到重启服务端时提示:
解决办法:加入一条socket配置,重用IP和端口
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
4、举例:
加入异常处理的原因是防止客户端意外关闭或者替他原因导致的异常都处理掉,直接跳出通信循环,否则没有异常处理服务端会一直处在接受的状态而报错
服务端:
import socket #1、买手机 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #初始化,INET代表基于网络型,STREAM代表局域TCP的套接字 #2、绑定手机卡 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加,防止重启服务端时端口占用的情况 phone.bind(('127.0.0.1',8082)) #绑定服务端所在的机器的IP地址和端口,只在服务端做,因为客户端要做连接必须要知道跟谁做连接 #3、开机 phone.listen(5) #监听,5代表最多挂起5个连接 #4、等电话连接 print('starting...') while True: #连接循环 conn,addr=phone.accept() #conn是客户端和服务端之间的连接,里面包含里本地地址和端口,还有远端的地址和端口,addr是客户端的地址和端口 print('IP:%s,PORT:%s' %(addr[0],addr[1])) #5、收发消息 while True: #通信循环 try: data=conn.recv(1024) #最大收1024 print(data) if not data:break #针对linux conn.send(data.upper()) #发送处理过的数据 except Exception: break #6、挂电话 conn.close() #关闭连接 #7、关机 phone.close() #关闭服务端
客户端:
import socket #1、买手机 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #socket初始化 #2、打电话 phone.connect(('127.0.0.1',8082)) #建立和服务端的连接, #3、发收消息 while True: msg=input('>>: ').strip() if not msg:continue phone.send(msg.encode('utf-8')) #发送数据请求,注意必须是字节类型 print('has send===>') data=phone.recv(1024) print('has recv') print(data.decode('utf-8')) #4、挂电话 phone.close() #关闭连接