网络 #TCP #UDP #文件下载器 #网络通信

网络基础

  • 网络把多方连接在一起,然后可以进行数据传递
  • 所谓的网络编程就是:在不同的电脑上的软件能够进行数据传递,即进程之间的通信

1)IP地址

  • 如何将数据精确地传递到目标电脑⇒ 通过IP地址
  • 如果我们把一栋栋大房子比作一台台电脑,IP地址就类似这个大房子的位置。
  • IP地址的作用是:在网络上标记唯一一台电脑

1>查看网卡信息

  • Win中:打开终端,输入ipconfig
    -
  • Linux:打开终端,输入ifconfig
    -
  • 每一个缩进的块就是一个网卡,前面的就是网卡名
  • lo表示本地环回,测试用的
  • ifconfig不仅可以查看网卡信息,还能关闭/打开网卡
sudo ifconfig 网卡名 down  # 关闭网卡
sudo ifconfig 网卡名 up  # 打开网卡
  • 小tips: Linux中,Ctrl A在终端中可以让指针快速回到行首;Ctrl E可以回到行尾。

2>IP地址分类

  • IPv4: xxx.xxx.xxx.xxx IP 地址的第四种版本,以四个分隔的数字组成,每个数字最大值为255。
  • IPv6: ipv4已经被瓜分完了,枯竭了。未来的趋势,遥远的未来。号称可以标记每一粒沙。

192.168.88.xxx

  • 前三位标记网络,前三位相同可以通信。后面一位标记主机。前三位叫网络号,后一位叫主机号。主机号为0,还有255不能用,所以一个网内最多可以有254个主机。这种就被称为C类IP。
  • 主机太多了,就可以以后两位为主机号,前两位为网络号。
    在这里插入图片描述

3>私有IP

  • 有一部分的IP地址是属于我们的局域网使用,也就是私网IP。它们的范围是:

10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255

  • IP地址127.0.0.1~127.255.255.255用于回路测试。

如127.0.0.1可以测试本机中配置的web服务器

2)端口

  • 如果把电脑比作一栋栋大房子,应用程序就是每一个住户,电脑为这些住户分配门牌号。有了这些门牌号就不会串错门,这个门牌号就叫做端口号,它是用来唯一标记应用程序的。

1>端口的分类(部分)

1.知名端口

  • 知名端口是众所周知的端口号,范围从0到1023

80端口分配给HTTP服务
21端口分配给FTP服务

  • 在一般情况下,一个程序需要使用知名端口时,需要有root权限

2.动态端口

  • 动态端口的范围是从1024到65535
  • 它一般不固定分配某种服务,而是动态分配。
  • 当一个系统程序或应用程序需要网络通信时,它向主机申请一个端口,主机从可用的端口号中分配一个供它使用。
  • 当这个程序关闭时,这个占用的端口号随之释放。

2>查看端口号

netstat -an  # 查看端口状态
lsof -i [tcp/udp]:端口号  # 知道该端口被哪个应用占用

→具体参考这个博客,写得很全很好←

3)socket

  • socket 插口、插排
  • socket是完成网络通信的必备东西
  • 不同电脑的进程间如何通信?
  • 首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!
  • 在1台电脑上可以通过进程号(PID)来唯一标识一个进程,但是在网络中这是行不通的。
  • 其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用进程(进程)。
  • 这样利用ip地址,协议,端口就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互

创建socket

  • python 中,使用socket模块即可:
import socket
socket.socket(AddressFamily, Type)  # 第一个参数是协议、族,第二个写UDP或tcp
  • Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
  • Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
  • 创建一个tcp socket(tcp套接字)
import socket

# 创建tcp的套接字(s即套接字对象)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

# TODO 这里使用套接字功能

# 关闭套接字
s.close()
  • 创建一个udp socket(udp套接字)
import socket

# 创建udp的套接字(s即套接字对象)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# TODO 这里使用套接字功能

# 关闭套接字
s.close()

4)UDP

  • 传输控制报协议,类似于写信
  • 只有位于同一个网络才能进行通信

1>发送UDP

  • 在Linux中编写代码:
    在这里插入图片描述
  • Windows搜索网络调试助手,下载
    在这里插入图片描述
  • 运行py文件,与该程序进行对话
    在这里插入图片描述
    (这里乱码了,编码改成gbk即可)

2>增加循环和跳出

  • 先备份一下文件:

:w udp发送1.py
在这里插入图片描述

  • 循环跳出:
    在这里插入图片描述
  • 发送数据验证一下:
    在这里插入图片描述

3>接收udp数据

  • 接收udp数据,首先需要绑定 一个端口。

在这里插入图片描述

这里分配了一个8090的端口
必须绑定自己电脑的IP和端口,其它的不行

  • 其次,等待接收对方发送的数据,并打印出来
    在这里插入图片描述

可以试一下打印recv_data,发现是一个元组:(内容,(对方IP, 对方端口)

  • 互相发送数据:
    在这里插入图片描述
  • 完整代码如下:
    在这里插入图片描述
  • 一个套接字可以即发又收

4>单工、半双工、全双工

网络通信的概念

  • 单工:信息指向是单向的,比如收音机。
  • 半双工:信息指向双向,但不能同时收发,比如对讲机
  • 全双工:手机之类的。socket套接字是全双工的。

5>创建一个UDP聊天小程序

  • 先完成主体(完成之后备份):
  • 封装在函数内,添加一个选择功能
    在这里插入图片描述
    在这里插入图片描述

用python3打开,先不选择功能。拿网络调试助手给其发送三条消息。然后终端中选择接收功能,发现一次接收一条消息。当第四次接收时,发现程序一直在等待页面,没有数据就一直在那里卡着。这说明:

操作系统,把接收到的数据入栈了。套接字对象每一次调用接收打印,操作系统就查看是否有该端口的数据,有就弹出一条数据。

如果知道别人的端口,就可以一直发数据,操作系统会一直存着。当发送的数据足够大足够多时,对方电脑就会卡到崩溃。

  • 效果展示:

在这里插入图片描述

5)TCP

  • 传输控制协议(Transmission Control Protocol)是一种面向连接的可靠的,基于字节流的传输层协议,由ETF的RFC 793定义
  • TCP通信需要经过创建连接、数据传递、终止连接 三个步骤
  • TCP通信模型中,在通信开始之前,一定要先建立相关的连接,才能发送数据,类似打电话
  • TCP更稳定和可靠(丢包或是包损坏,会要求计算机重新发一份)

1>TCP特点

1.面向连接

  • 通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。
  • 双方间的数据传输都可以通过这一个连接进行。
  • 完成数据交换后,双方必须断开此连接,以释放系统资源。
  • 这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。

2.可靠传输

  1. TCP采用发送应答机制
    TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功
  2. 超时重传
    发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。
    TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
  3. 错误校验
    TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
  4. 流量控制和阻塞管理
    流量控制用来避免主机发送得过快而使接收方来不及完全收下。

3.为什么常用TCP而不是UDP

  • 面向连接(确认有三方交握,连接创建后才做传输)
  • 有序数据传输
  • 重发丢失的数据包
  • 舍弃重复的数据包
  • 无差值的数据传输
  • 阻塞/流量控制

2>tcp客户端

  • TCP严格区分服务器端和客户端
  • 服务器端,就是提供服务的一方。客户端,就是需要被服务的一方
  • 参照下方的代码:(让程序当客户端,网络调试助手当服务器)
    在这里插入图片描述

send参数只有一个要发送的内容;sendto参数有要发送的内容和地址

  • 接收用的网络调试工具,选择TCP服务器选项,然后开启
    在这里插入图片描述

3>tcp服务器

1.tcp服务器1.0

  • 创建服务器的流程很固定:
  1. socket创建一个套接字
  2. listen使套接字可以变为被动链接
  3. accept等待客户端的链接
  4. accept等待客户端的链接
  5. recv/send接收发送数据
  • 参照下方代码,注意阅读注释:
    在这里插入图片描述
    在这里插入图片描述
  • 服务器的监听套接字打开,客户端C1和C2先连接监听套接字,然后服务器为其分配新套接字,客户端与新套接字建立链接,与监听套接字断开链接。监听套接字保持监听,服务套接字负责服务。
  • 服务器先运行。
  • 服务器先收后发。

2.tcp服务器2.0(接待多个客户端)

  • 加个循环:
    在这里插入图片描述
  • 打开多个网络助手向其发送数据(也可以使用之前写好的客户端程序)
    在这里插入图片描述
  • 强行退出的话,再次打开会发现端口被占用(其实等待一段时间就好,不想等的话,看下面的解决办法)
    在这里插入图片描述
  • 可以:
  1. net -anp | grep 端口号 # 查看该端口是否被占用
  2. fuser -vn tcp 端口号 # 查看占用该端口的进程
  3. kill -s 9 查出来的PID # 杀死该进程,PID就是进程号
  4. ps # 查看所有进程号,以检查是否被真正杀死

3.tcp服务器3.0(断开链接,保持链接)

  • 现在在接待客户端时有个问题,每个客户端执行了一次操作后,就需要再次链接了。(加循环,辅助条件判断退出)
  • 因为上面在循环,所以监听套接字不可能退出(辅助条件判断退出)。
  • 因为两个需求都要辅助条件判断退出,所以定义一个方法。
  • 看下列代码:
    在这里插入图片描述
    在这里插入图片描述
  • 效果:
    在这里插入图片描述
    在这里插入图片描述

4.tcp服务器

  • 以下是在2.0的基础上进行操作的,请复制2.0版本到4.0,然后编辑
  • 每次都要输入判断,麻不麻烦?有没有办法判断客户端的状态,如果客户端主动断开连接,就退出为整个客户端的服务。还真有:
  • 如果recv解堵塞,那么有两种可能:
  1. 客户端数据已经发送过来了
  2. 客户端调用了close()断开连接
  • 由于不能发送空数据,所以只要判断是否传来数据(没有传来会为None),就可以进行分支判断了。
  • 代码如下:
    在这里插入图片描述
    在这里插入图片描述
  • 结果如下:
  • 在这里插入图片描述
  • 服务器在这里告一段落,仍然有需要解决的问题:比如,如何同时接入多个客户端,同时工作?后面多任务再进行补充。

4>tcp文件下载器

5>其它注意事项

  • listen(128) 中的参数是指同一时刻最大达访问数,这个数字填多少其实没太大关系,取决于操作系统处理的逻辑(不然高并发只要改个数字?)。这也是为什么Linux做服务器非常好的原因。
  • tcp&udp处理逻辑如下:
tcp_client tcp_server udp
socket socket socket
conect bind bind(若是客户端可省)
send/recv listen sendto/recvfrom
close accept close
recv/send
close
  • 为了保证客户端不因多开出问题,不绑定

腾讯QQ采取半UDP半TCP,当登录QQ时,会获得一个动态端口。当要和他人聊天时,发送信息,首先到腾讯服务器中(服务器有固定端口),然后服务器就知道其此时此刻的端口号。然后另一方的QQ登录时,也获得一个动态端口。于是腾讯服务器就知道两方端口,起到一个桥梁的作用,传递数据。但是,雁过拔毛,腾讯服务器会存储聊天信息。国家规定,至少保存六个月。像腾讯这种体量的大公司,根本不删的。

  • 关闭listen,新的客户端不能再接入。但已经服务的客户端依旧会服务完。
发布了48 篇原创文章 · 获赞 30 · 访问量 4654

猜你喜欢

转载自blog.csdn.net/weixin_44925501/article/details/103391941
今日推荐