网络编程

利用网络来与另一台计算机相互传输数据

网络通讯的基本要素:物理连接介质、通讯协议/网络协议

网络协议就是标准

TCP/IP协议:指利用IP进行通信时所必需用到的协议群的统称·

osi开放系统互连模式

1、物理层:两台相互独立的计算机,想要建立物理连接,连接的方式多种多样包括电缆,光缆,无线电等,基于电子器件发送电流信号,根据电流的高低可以对应到数字0和1,也就是二进制数据。

2、数据链路层:(以太网协议)

由来:单纯的电信号0和1没有任何意义,想要变得有意义则必须规定电信号多少位一组,每组什么意思

以太网协议工作在数据链路层,其规定了电信号分组方式,以及一组电信号应该包含哪些内容

以太网协议规定一组电信号构成一个数据包,叫做’帧‘

每一个数据帧分成:报头head和数据data,head中包含的源和目标地址指的是网卡的地址,即mac地址,每个网卡都是唯一的

有了mac地址同一网络内的两台主机就可以通信了:

1)以太网协议采用最原始的方式是以广播的方式进行通信

2)交换机不仅负责网络中的计算机能够互相通信,还要优化网络传输,只要连接一次MAC地址就被交换机记录下了下一次就不用广播了。交换机的工作原理类似早期的BB机

3、网络层: 由来:世界范围的互联网是由一个个彼此隔离的小的局域网组成的,如果所有的计算机都采用以太网的广播方式来寻找其他计算机,那么一台机器发送的包全世界都会收到,这就不仅仅是效率底的问题了,如果属于同一广播域,就采用广播的方式,如果不是就是采用路由的方式向不同广播域/子网分发数据包

引入一套新的地址用来区分不同的广播域/子网,这套地址即网络地址,网络地址就是由IP协议定义的地址,叫做IP地址

子网掩码就是用来判断两个ip是否属于同一子网/局域网

4、传输层:(TCP和UDP就是工作在传输层的协议)

端口是需要联网的应用程序与网卡关联的编号或者简单的理解成应用程序的唯一标识

重点:ip地址精确到具体的一台电脑,而端口精确到具体的程序

传输层作用:建立端口到端口的通信

TCP协议:

是面向有链接且可靠的通信传输协议,面向有连接是指在传送数据之前必须先建立连接,数据传送完成后释放连接,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化且通过四次挥手关闭连接。

效率要求相对较低,但对准确性要求相对高的场景,因为传输中需要对数据进行确认、重发、排序等操作,效率没有UDP高。文件传输、发送接收邮件、远程登陆

 三次握手:
 面向有连接是指在数据通信开始之前先做好两端之间的准备工作
 三次握手是指建立一个TCP连接时需要客户端和服务器端总发送三个包以确认连接的建立
 四次挥手:
 四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开,中断连接端可以是客户端,也可以是服务器端

UDP协议:

是面向报文的通信传输协议,所谓面向报文,指它的传输方式是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文

不具有可靠性的数据报协议,虽然可以确保发送数据的大小,却不能保证信息一定会送达,效率高,数据传输不安全,容易丢包。对数据准确性和丢包要求较低,但速度比较块。发短信,不需要双方建立连接、在线视频、网络语音电话

5、应用层

用户使用的都是应用程序(微信、支付宝)等均工作与应用层

作用:规定应用程序之间交互数据的数据格式,这些数据交互的格式也就是作用在应用层之间的各种协议,这些应用程序的协议就构成了“应用层”(HTTP/FTP/SMTP)

 

socke套接字

是在应用层和传输层之间的一个抽象层

socket就是一个python中的模块,通过调用模块中已经实现的方法建立两个进程/应用之间的连接和通信

TCP协议下的Socket通讯流程图:

 

 

 

服务端与客户端交互:

服务端:

 import socket
 
 # 创建socket对象
 sk = socket.socket(type = socket.SOCK_STREAM)
 
 # 绑定IP和端口
 sk.bind(('192.168.21.144',8898))
 
 # 监听该IP和端口,定最多允许多少个客户连接到服务器。它的值至少为1。收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求
 sk.listen()
 
 # 等待client的连接
 # conn表示建立好的连接通道抽象成对象
 # address就是接收到客户端的ip和port (ip地址和端口号)
 # accept是一个阻塞方法,程序运行到此处就hang住了,除非某种条件成立后,结束阻塞
 # 结束阻塞的条件就是成功的接收到一个客户端连接
 conn,address = sk.accept()
 
 # 基于建立好的连接通道进行数据的收发
 # 接收数据,1024指最多可以接受的数量
 resv = conn.recv(1024).decode()
 print('服务端接收信息:',recv_msg)
 send_msg = input('服务端发送信息:')
 conn.send(send_msg.encode())
 
 conn.close()
 sk.close()
 

客户端:

 import socket
 
 创建套接字对象
 sk = socket.socket(type = socket.SOCK_STREAM)
 
 连接目的服务器
 sk.connect(('192.168.21.144',8898))
 
 数据的收发
 send_msg = input('客户端发送请求:')
 sk.send(send_msg.encode())
 recv_msg = sk.recv(1024).decode()
 print('客户端接收信息:',recv_msg)
 
 sk.close()
 

注意:socket绑定IP和端口时可能出现下面问题:

 sk.bind(('127.1.0.1',8898))
 OSError:[Error 48] Address already in use

解决办法:

 加入一条socket配置,重用ip和端口
 import socket
 from socket import SOL_SOCKET,SO_REUSEADDR
 sk = socket.socket()
 sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 在bind前加,允许地址重用

如果问题未解决,那么只能更换端口,因为电脑不支持端口重用,因为可能你的电脑上其他应用正在使用这个端口。

尝试不间断的让客户端和服务端进行数据交互

 Server端
 import socket
 sk = socket.socket(type = socket.SOCK_STREAM)
 sk.bind(('127.0.0.1',8898))
 sk.listen(5)
 conn,adrr = sk.accept()
 whilr True:
  recv_data = conn.recv(1024).decode()
  print('服务端接收数据:',recv_data)
 
  send_data = input('服务端发送数据:')
  sk.send(send_data.encode())
 
  if send_data == 'bye':
  break
 conn.close()
 sk.close()
 client端1
 import socket
 sk = socket.socket(type = socket.SOCK_STREAM)
 sk.connect(('127.0.0.1',8898))
 while True:
  send_data = input('客户端发送数据:')
  sk.send(send_data.encode())
 
  recv_data = sk.recv(1024).decode()
  print('客户端接收数据:',recv_data)
  if recv_data == 'bye':
  break
 sk.close()
 client端2
 import socket
 sk = socket.socket(type = socket.SOCK_STREAM)
 sk.connect(('127.0.0.1',8898))
 while True:
  send_data = input('客户端发送数据:')
  sk.send(send_data.encode())
 
  recv_data = sk.recv(1024).decode()
  print('客户端接收数据:',recv_data)
  if recv_data == 'bye':
  break
 sk.close()

第一个连接客户端可以和服务端收发消息,但是第二个连接的客户端发消息服务端收不到

原因:TCP属于长连接,长连接就是一直占用着这个连接,这个链接的端口被占用了,第二个客户端过来连接的时候,它是可以连接的,但是处于一个占线的状态,就只能等着去跟服务端建立连接,除非一个客户端断开了(优雅的断开可以,如果是强制断开就会报错,因为服务端的程序还在第一个循环里面),然后就可以进行和服务端的通信了

完整代码:

服务端
import socket
sk = socket.socket(type = socket.SOCK_STREAM)
sk.bind(('127.0.0.1',8898))
sk.listen(5)
while True:
conn,addr = sk.accept()
while True:
recv_data = conn.recv(1024)
print('服务端接收数据:',recv_data)

send_data = input('服务端发送数据:')
conn.send(send_data.encode())

if send_data == 'bye':
conn.close()
break
sk.close()
客户端
import socket
sk = socket.socket(type = socket.SOCK_STREAM)
sk.connect(('127.0.0.1',8898))
while True:
send_data = input('客户端发送数据:')

 

posted @ 2021-12-11 21:39  多啦a梦与时光机  阅读(40)  评论(0编辑  收藏  举报