python三十五期---TCP与UDP协议,socket模块基本使用

昨日内容回顾

  • 软件开发架构

    1.C/S架构
    	客户端、服务端
    2.B/S架构
    	浏览器、服务器
    B/S本质也是C/S架构
    
  • 网络编程前戏

    基于网络实现数据交互
    计算机之间要想实现数据交互必须有物理链接介质(前提)
    
  • OSI七层协议

    应用层、表示层、会话层、传输层、网络层、数据链路层、物理链接层
    
    应用层、传输层、网络层、数据链路层、物理链接层			(核心)
    
    应用层、传输层、网络层、网络接口层
    

1.物理链接层
建立物理链接介质
2.数据链路层
规定了电信号的分组方式
以太网协议
3.网络层
IP协议:规定了接入互联网的计算机都必须有一个ip地址
IP地址:
IPV4
点分十进制 数量有限
IPV6
能够表示地球上的每一粒沙子
特征:IP能够标识出地球上独一无二的一台接入互联网的计算机
"""
ARP协议:能够根据IP地址动态解析到一个MAC地址
谁正在使用某个IP地址 我们就可以根据IP地址动态解析到使用者的MAC地址
"""
4.传输层
PORT协议:计算机通过端口号管理正在运行的诸多应用程序
端口号:0-65535
特征:可以指定也可以动态分配
能够标识出计算机上独一无二的一个应用程序


* 各种协议

```python
1.以太网协议
	规定了计算机在出厂的时候都必须有一块网卡 上面有一串数字
  该数字就是计算机的物理地址>>>:MAC地址(身份证号码)
2.IP地址:
    	IPV4
      	点分十进制 数量有限
      IPV6
      	能够表示地球上的每一粒沙子
 	特征:IP能够标识出地球上独一无二的一台接入互联网的计算机
 	"""
 	ARP协议:能够根据IP地址动态解析到一个MAC地址
 			谁正在使用某个IP地址 我们就可以根据IP地址动态解析到使用者的MAC地址
 	"""
3.端口号:0-65535
 	特征:可以指定也可以动态分配
    	能够标识出计算机上独一无二的一个应用程序
  • 专业名词解释

    1.交换机
    2.广播
    3.单播
    4.广播风暴
    5.局域网、广域网、互联网
    6.路由器
    7.域名就是URL就是网址
    8.域名解析(DNS服务器)    域名 >>>>>> ip:port
    
    

今日内容概要

  • 传输层之TCP与UDP协议
  • 应用层
  • socket模块简介
  • socket模块基本使用
  • 代码优化处理
  • socket黏包问题
  • 黏包问题的解决方案

今日内容详细

.
.

socket模块


如果我们需要编写基于网络进行数据交互的程序
意味着我们需要自己通过代码来控制我们之前所学习的OSI七层(很繁琐 很复杂 类似于我们自己编写操作系统)

socket类似于操作系统 封装了丑陋复杂的接口提供简单快捷的接口
-----------------------------------------

理解socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,
Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,
一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

-----------------------------------------

Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,
一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

-----------------------------------------

socket也叫套接字
	基于文件类型的套接字家族(单机)
	AF_UNIX
-------------
	基于网络类型的套接字家族(联网)
	AF_INET

-----------------------------------------

在这里主要学习利用socket完成TCP/IP通讯,首先需要生成两个对象,
一个是客户端(client),一个是服务端(sever)。

image
.
.
.
image
.
.

socket代码简介

socket 插座;窝(n);使装入插座(v) 也有端口的意思!!!
bind 捆绑, 捆扎 (v)
listen
.
.
.
.

暂时只要想实现网络通信就要使用socket模块!!!


## 先建立服务端代码

import socket

# 1.  产生一个socket对象并指定采用的通信版本和协议(TCP)
server = socket.socket()
# 括号内不写参数 默认就是TCP协议
# family=AF_INET基于网络的套接字 type=SOCK_STREAM流式协议即TCP


# 2.  绑定要连接的端口 (服务端ip地址,服务端的端口号)    元祖形式(ip地址,端口号)
server.bind(('127.0.0.1', 8080))
# 127.0.0.1为本地回环地址 只有自己的电脑可以访问,
# 如果没有申请公ip,只能在局域网实现让别的客户端访问该服务端的操作!!


# 3.  设置监听端口的状态,设立半连接池
server.listen(5)  # 有5个空位


# 4.  等待客户连接
sock, addr = server.accept()  #该方法有两个返回值 return sock, addr  三次握手!
print(sock, addr)  # sock就是双向通道, 注意此处addr是客户端的IP地址加端口!!!


# 5.  接收客端发来的消息
data = sock.recv(1024)  # 通过双向通道接收客户端发送过来的消息,只接收1024字节,剩下的就不要了!
print(data.decode('utf8'))


# 6.  给客户端发送消息 注意消息必须是bytes类型
sock.send('尊敬的客人 您说什么就是什么 一切按照您的要求来'.encode('utf8'))


# 7.  关闭双向通道
sock.close()  # 四次挥手???


# 8.  关闭服务端
server.close()  # 店倒闭了


# 这样服务端就写好了,运行该程序后,会卡在等待客户连接的状态!!!
# 服务端代码必须要先运行起来,才能再去运行客户端代码!!!
-----------------------------------------------

.
.
.

再建立客户端代码!!!!!

## 再建立客户端代码!!

import socket

# 1.生成socket对象指定类型和协议
client = socket.socket()


# 2.通过服务端的地址链接服务端!!!!!!
client.connect(('127.0.0.1', 8080))  # 三次握手


# 3.直接给服务端发送消息 ,客户端先发消息,服务端接收到消息后再发送消息给客户端,客户端再接收消息!!!
client.send('大爷有钱 把你们店最好的给我叫出来'.encode('utf8'))


# 4.接收服务端发送过来的消息
data = client.recv(1024)
print(data.decode('utf8'))


# 5.断开与服务端的链接
client.close()

------------------------------

整体的一个思路:
就是服务端代码先跑起来,卡在等待客户连接的状态
客户端代码跑起来,走到connnect连接代码后,向服务端发送连接请求
服务端接受连接请求后,拿到客户端的ip地址与接口,还有双向通道
客户端开始给服务端发送消息
服务端接收消息,并给客户端回消息
客户端接收到服务端回的消息后,断开与服务端的连接
服务端关闭双向通道,断开与服务端连接

------------------------------------

网络数据交互的一个雏形!!!!!!
很多代码和框架都是由这几行代码演变而来的!!!!!!!!
是所有牛逼的框架的底层的基础代码!!!!!!

.
.
.
.
.
.
.
.
.
.

代码优化


1.聊天内容自定义
	针对消息采用input获取

--------------------------------------

2.让聊天循环起来
	将聊天的部分用循环包起来

---------------------------------------

当客户端异常断开时,如何让服务端继续正常运行???windows系统服务端会直接报错,
苹果电脑会出现重复发空消息,好多次后会出现报错的现象!!!
怎么办?

应该要能自动识别出该报错信息,
并且应该要让代码能正常回退到accept那边等待新的对象连接请求!!!
当服务端接收到空消息后,说明此时客户端异常断开或者代码终止运行了,
或者客户端就什么都没发,直接按了一个回车了。


---------------------------------------

3.用户输入的消息不能为空!!!!用户的输入的为空,服务端sock.recv(1024)收不到消息后,
就会卡在这了不走了,这时候客户端与服务端两边代码都走到了recv这了,这个时候就尬住了!!!!

	本质其实是两边不能都是recv或者send!!!!!!
	一定是一方收一方发!!!!!!!!

---------------------------------------

4.苹果电脑:服务端多次重启可能会报错
	Address already in use 主要是mac电脑会报
	方式1:改端口号

	方式2:
from socket import SOL_SOCKET,SO_REUSEADDR
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加

---------------------------------------

5.当客户端异常断开的情况下 如何让服务端继续服务其他客人???
	windows服务端会直接报错
  mac服务端会有一段时间反复接收空消息延迟报错
	异常处理、空消息判断!!!!

---------------------------------------

.
.
.
.
.

没优化前代码


---------------------------------------------------
服务端
import socket
server = socket.socket()
# 2.通过服务端的地址链接服务端!!!!!!
server.bind(('127.0.0.1', 8080))
server.listen(5)
sock, addr = server.accept()
# 客户端发送连接请求,服务端接收连接请求,获取双向通道,并拿到客户端的ip地址与端口号
data = sock.recv(1024)
print(f'来自于客户端{addr}的消息>>>>:', data.decode('utf8'))
msg = input('请输入发送给客户端的而消息>>:').strip()
sock.send(msg.encode('utf8'))
sock.close()

----------------------------------------------------

客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8080))  # 连接的服务端地址!!!

msg = input('请输入您想要发送给服务端的消息>>>:').strip()
client.send(msg.encode('utf8'))
data = client.recv(1024)
print('来自于服务端发送过来的消息>>>:', data.decode('utf8'))

.
.
.
.
.
.
.
.

让聊天循环起来-------优化后代码


-----------------------------------------------------
服务端
import socket

# 1.产生一个socket对象并指定采用的通信版本和协议(TCP)
server = socket.socket()

# 2.通过服务端的地址链接服务端!!!!!!# 2.绑定要监控的界面((服务端ip地址,服务端的端口号))
server.bind(('127.0.0.1', 8080))
server.listen(5)   # 3.监听端口状态,设立半连接池
sock, addr = server.accept()  # 三次握手建立连接

# 客户端发送连接请求,服务端接收连接请求,获取双向通道,并拿到客户端的ip地址与端口号
while True:
    data = sock.recv(1024)  # 通过双向通道接收客户端发送过来的消息,如果客户端不发,代码就卡在这边,所以不会出现死循环!!
    print(f'来自于客户端{addr}的消息>>>>:', data.decode('utf8'))
    msg = input('请输入发送给客户端的而消息>>:').strip()
    sock.send(msg.encode('utf8'))  # 通过双向通道发送消息给客户端!!!

# sock.close()  # 这行不用写了,操作结束,操作系统回自动回收双向通道!!!

-------------------------------------
客户端
import socket

client = socket.socket()  # 1.生成socket对象指定类型和协议
client.connect(('127.0.0.1', 8080))  # 2.通过服务端的地址链接服务端!!!
# 服务端一旦接受,这边就拿到双向通道了!!就可以和服务端来回的聊天了!!!!!

while True:
    msg = input('请输入您想要发送给服务端的消息>>>:').strip()
    client.send(msg.encode('utf8'))  # 注意send发送的信息必须是bytes类型二进制数据!!!
    data = client.recv(1024)
    print('来自于服务端发送过来的消息>>>:', data.decode('utf8'))

# 当客户端终止程序运行后,或者当客户端异常断开时,服务端会报错!!!!
-------------------------------------

.
.
.
.
.
服务端最终优化后!!!!


import socket
from socket import SOL_SOCKET, SO_REUSEADDR      # 针对苹果电脑,服务端多次重启可能会报错的问题,要导入两个模块!!!!!!

# 1.产生一个socket对象并指定采用的通信版本和协议(TCP)
server = socket.socket()
# 2.通过服务端的地址链接服务端!!!!!!# 2.绑定要监控的界面((服务端ip地址,服务端的端口号))
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)    # 针对苹果电脑的,加这行代码!!!!!!

server.bind(('127.0.0.1', 8080))
server.listen(5)   # 3.监听端口状态,设立半连接池
while True:
    sock, addr = server.accept()  # 三次握手建立连接
    # 客户端发送连接请求,服务端接收连接请求,获取双向通道,并拿到客户端的ip地址与端口号
    while True:  # 聊天的过程可能出现报错的情况,所以加个异常捕获
        try:
            data = sock.recv(1024)  # 通过双向通道接收客户端发送过来的消息,
                                    # 如果客户端不发,代码就卡在这边,所以不会出现死循环!!
            if data == 0:
            # 如果监测到收到的消息为空后,跳出小循环,到大循环里面去,等待用户发送连接请求!!!
                break
            print(f'来自于客户端{addr}的消息>>>>:', data.decode('utf8'))
            msg = input('请输入发送给客户端的而消息(不能发空消息!!)>>:').strip()
            # 并且服务端发消息也不能为空不然还是会尬住了!!!
            # 但是服务端是一个固定的程序,不是一个人!!!
            sock.send(msg.encode('utf8'))  # 通过双向通道发送消息给客户端!!!
        except Exception:  # 一旦监测到报错信息,也break结束的掉小循环!!
            break

    # sock.close()  # 这行不用写了,操作结束,操作系统回自动回收双向通道!!!


# 微信两个人聊天,不是客户端与服务端交流,而是两个聊天的人都是客户端,
# 是1个人通过客户端把消息发给微信的服务端,然后服务端再把消息传给对应的另一个客户端!!!
# 微信的服务端作为一个中转站!!!


.
.
.
.
.
.
.
.


import socket

client = socket.socket()  # 1.生成socket对象指定类型和协议
client.connect(('127.0.0.1', 8080))  # 2.通过服务端的地址链接服务端!!!
# 服务端一旦接受,这边就拿到双向通道了!!就可以和服务端来回的聊天了!!!!!

while True:
    msg = input('请输入您想要发送给服务端的消息>>>:').strip()
    # 加个判断,这样就可以避免客户端发空消息的情况了!!!,两边就不会尬住了!!!
    # 并且服务端发消息也不能为空不然还是会尬住了!!!
    if not len(msg):
        print('不能发送空消息!!')
        continue
    client.send(msg.encode('utf8'))
    data = client.recv(1024)
    print('来自于服务端发送过来的消息>>>:', data.decode('utf8'))

.
.
.
.
.
.
.

半连接池的概念


server.listen(5)  # 半连接池
半连接池相当于建立了一个缓冲地带,除第一个外,还有5个客户端可以与服务端连接,
后面的客户端就连接不上了。

注意:另外五个虽然能连接,但是服务端一次只能服务一个客户端,所以另外5个人虽然能连接,
但并不能与服务端进行收发消息交流,只能在第一个客户端断开连接后,
下一个客户端才能与服务端进行信息的交流!!!!!!

当有多个客户端来链接的情况下 我们可以设置连接的客户端的等待数量(不考虑并发问题)
假设服务端只有一个人的情况下

在测试半连接池的时候 可以不用input获取消息 直接把消息写死即可

.
.
.
.
.

posted @   tengyifan  阅读(114)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示