3. 网络编程(UDP和TCP)

1. ip地址的作用

在网络中唯一标识一台主机,可以理解为"收货地址"。

2. 在windows中利用命令行查看网卡信息(ip地址)

ipconfig

3. 在Linux中查看网卡信息(ip地址)的指令

ifconfig

4. Linux在编写shell指令时,快速返回行首/行尾的快捷键

  • 快速返回行首:Ctrl + A
  • 快速返回行尾:Ctrl + E

5. 在Linux中 关闭/开启 某个网卡的指令

  1. 利用ifconfig查看网卡信息确认名称网卡,比如:ens40
  2. 关闭网卡 ifconfig ens40 down
  3. 开启网卡 ifconfig ens40 up

6. ip地址由______和_______组成,请简单说明这样组成的意义

IP地址由网络地址主机地址组成,简单的说,就是4个字节组成IP地址。同一网络下的网络地址是相同的,不同主机的主机地址部分不同:例如 192.168.1.1 和 192.168.1.2 隶属于同个网络,但是不同设备。而主机地址所占用的字节数为1~3个,决定了该网络地址下可容纳的 主机数量的上限。

7. 请尝试向你女朋友解释清楚网络通讯中的ip地址、端口号的概念

如家宾馆坐落在 山东省 192 市 168 县 192 大街 1号,
莫泰宾馆坐落在 山东省 192 市 168 县 192 大街 99号,
有一天,如家来了一个客人,名字叫 微信1 ,住进了 8000 号 房间
莫泰来了一个客人,名字叫 微信2,住进了 9000 号房间
两个人是笔友,经常写信沟通。邮递员小哥在两个宾馆间疯狂送信。
上述的两个宾馆就相当于网络中的两台主机,两个宾馆的地址就相当于主机的IP地址,是唯一标识两者位置的。微信1和微信2 就相当于两个运行在各自主机上的客户端应用,他们与外界进出(信息交换)必须通过各自房间的门,我们称之为 端口,而门牌号就是端口号。邮递员和其他派送服务共同组成了网络,他们写信的行为,我们称之为不同主机下的客户端应用(进程)在网络中的通信

8. 网络信息传输过程中包含的基本的5种与ip地址、端口号相关数据

  • dest_ip : 192.168.1.1 [目标ip地址]
  • source_ip : 192.168.1.2 [源ip地址]
  • dest_port : 7788 [目标端口号]
  • dest_ip :8080 [源端口号]
  • content : hello world [传输内容]

9. 请尝试向你女朋友解释清楚知名端口和动态端口的含义

端口号我们理解为 主机上的某个进程与其他主机进行数据交换进出的门牌号 ,这个门牌号是有范围限制的,为 0~65535 。而每个进程都要独自占用一个,就像我们申请QQ号,申请的早的,甚至能申请到5位的靓号,现在应该只能申请到 9位、10位甚至11位的。而知名端口号,就是那些大型知名企业的程序或者服务优先抢到了靓号,他们可以用 0 - 1023 之间号,而后来我们这种抢不到的,就只能使用动态端口号,意思是可变的号,但是在当前主机上是唯一的。范围在1024 - 65535 。

10. 知名端口和动态端口各自的可用区间

  • 知名端口:0 - 1023
  • 动态端口:1024 - 65535

11. 在Linux查看端口状态的指令

netstat -an

12. 什么是socket,其中文名称为?

socket是进程间通讯的一种方式,能够实现不同主机间的进程间通信。

13. socket模块中socket方法是做什么用的?有几个参数,分别是什么,参数常用的可选值分别是什么?

14. 如何确定当前主机和另外一个主机网络是否能够接通?

ping ip地址(xxxx.xxxx.xxxx.xxxx)

15. socket.sendto(data, dest_addr) 这个方法是做什么用的?其中的data参数有什么要求?dest_addr有什么要求?

socket.sendto(data, dest_addr)方法用于套接字发送数据,有两个参数:

  1. data:发送的内容,必须为 bytes 类型 ,在 python 中将字符串转换为 bytes 类型有两种方法:
    (1)直接在字符串前面加 b ,例如:b"string"
    (2)使用 encode(编码方式) 方法,例如 :"string".encode('utf-8') 常用的编码格式有 utf-8、gbk、ASCII
    dest_addr
  2. dest_addr: 目标地址 ,为 tuple 元组类型。 dest_addr = (str类型的IP地址,int类型的端口号)

16. Python中如何将字符串转换为 bytes(字节)类型的数据?

请参见问题15中的答案

17. 终端中运行的程序使用什么指令强制退出?

ctrl + c

18. 请编写代码实现功能如下:实现一个简易的udp管接字发送数据至指定IP地址和端口(利用网络调试助手接收查看信息)

import socket


def main():
    # 创建管接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    while True:
        # 定义发送数据的内容和数据接收地址
        data = input("请输入您要发送的内容:").encode('gbk')
        dest_addr = ("192.168.245.1", 8080)
        # 使用管接字收发数据
        udp_socket.sendto(data, dest_addr)
        if data == "exit":
            break
            
    # 关闭管接字
    udp_socket.close()

if __name__ == '__main__':
    main()

19. 编写代码实现使用udp套接字接收数据的一个基本流程

import socket


def main():
    # 1.新建udp类型套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 2.绑定端口号
    local_addr = ('', 7788)
    udp_socket.bind(local_addr)

    while True:
        # 3.接收信息
        recv = udp_socket.recvfrom(1024)
        # print(recv)
        # 4.打印接收到的数据
        content = recv[0].decode('gbk')
        source_addr = recv[1]
        print("ip地址:%s 端口号: %s --- %s" % (source_addr[0], source_addr[1], content))

    # 5.关闭套接字
    udp_socket.close()


if __name__ == '__main__':
    main()

20. 请使用语言描述使用udp协议的socket发送和接收数据的步骤分别是什么?

udp类型socket发送数据

  1. 新建udp类型的socket套接字
  2. 选择性绑定端口:因为发送发无需使用特定的端口去发送数据,当不绑定任何端口时,系统在运行程序时, 会分配一个随机端口供其使用。每个进程使用的端口号必然是唯一的
  3. 发送数据 sendto
  4. 关闭socket

udp类型socket接收数据

  1. 新建udp类型的socket套接字
  2. 强制性绑定端口:因为必须指定当前的socket去监听哪个端口,才可能接收到信息。需要注意的是,绑定端> 口时候使用bind方法,其中传入的参数为一个元组类型--(IP地址_str,端口号_int),因为默认都是监听当前> IP,故可以省略不写,默认为''
  3. 接收数据并打印显示 recvfrom
  4. 关闭socket

21. 什么是私有ip?

国际规定有一部分的IP地址仅用于我们各自的局域网中,不在公网中去使用,这些IP地址成为私网IP。


私有IP范围

22. 同一个套接字socket可以用来既发送数据又接收数据吗?

当然可以,socket是全双工的工作方式。

23. socket.recvfrom(bytes_limit) 方法接收数据时,如果一直收不到数据会怎么样?

会导致程序所在的进程堵塞。也就是说,但凡调用了socket.recvfrom方法后,socket会一直处于等待接收消息状态,直至接收到消息。

24. 请尝试向你女朋友解释清楚单工、半双工、全双工的含义

单工、半双工、全双工是通讯传输的术语。分别如何理解呢?

单工:可以理解成收音机--->只能用来接收频道信号然后播放
半双工:可以理解成对讲机--->可以进行双向通讯,但是不可以同时发送或者接收信号
全双工:可以理解成电话---> 可以进行双向通讯,并且可以同时进行发送或者接收

25. socket套接字是上述网络通讯工作方式中的哪种?

socket为全双工的通讯方式

26. 基于udp基础知识,请编写一个简易的半双工的聊天器。

import socket

target_ip = ''


def choose_ip():
    global target_ip
    target_ip = input("请输入您想要对话的ip地址:")


def send_msg(udp_socket):
    data = input("请输入您要发送的消息:")
    global target_ip
    target_addr = (target_ip, 8899)
    udp_socket.sendto(data.encode('gbk'), target_addr)


def recv_msg(udp_socket):
    msg = udp_socket.recvfrom(1024)
    recv_data = msg[0]
    from_addr = msg[1]
    print("您收到来自 %s  的消息:%s" % (from_addr[0], recv_data.decode('gbk')))


def main():
    # 新建套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 绑定端口
    udp_socket.bind(('', 8899))

    welcome = '''
    **************宝顺的简易聊天器*****************
    (1)输入0:更改聊天好友
    (2)输入1:发送消息功能
    (3)输入2:接收消息功能
    (4)输入9:退出程序
    **********************************************
    '''
    print(welcome)

    global target_ip
    target_ip = input("请输入您想要对话的ip地址:")

    while True:
        op = input("请输入操作功能号:")
        if op == '0':
            choose_ip()
        elif op == '1':
            # 发送数据
            send_msg(udp_socket)
        elif op == '2':
            # 接收数据
            recv_msg(udp_socket)
        elif op == '9':
            break
        else:
            print("您输入的功能号有误!")


if __name__ == '__main__':
    main()

27. 26中编写的聊天器,存在什么严重的漏洞?

用户可以一直发送信息给你,导致你底层的接收区域爆满至电脑卡死

28. TCP的英文全称是什么?总结一下TCP和UDP的两者区别和应用场景。

TCP: Transmission Control Protocal 传输控制协议

UDP模型-----看成写信:操作起来更简单一些,回忆一下我们编写过的基于udp的聊天小程序。每次发送数据时使用的 socket.sendto(data, dest_addr) 的方法都需要写上目标的ip地址和端口号信息。

TCP模型-----看成打电话:创建连接----传输数据-----终止连接。使用 socket.send(data) 方法在已经连接的前提下直接发送数据即可。因此说TCP是面向连接的,参见下述点1。

但是相比较起来,tcp更稳定和安全,TCP协议的两大特点:

  1. 面向连接:通信双方必须先建立连接才能进行数据的传输,双方间的数据传输都可以通过这一个连接进行, 完成数据交换后,双方必须断开此连接,以释放系统资源。这种连接是一对一的, 因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。
  2. 可靠传输:这是因为tcp协议有一系列来保障传输的机制,比如:发送应答机制、超时重传、错误校验、流量控制和阻塞管理。

至于两者的应用场景,可以参考知乎上的一个回答:


UDP和TCP的应用场景

29. TCP传输数据一定会区分客户端和服务器吗?

是的,使用TCP协议传输数据时一定会区分客户端(client)和服务器端(server)

30. 请书写TCP传输数据时客户端侧的代码(四步骤)。

import socket


def main():
    # 1.创建tcp类型套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.连接服务器
    server_ip = '192.168.245.1'
    server_port = 8899
    server_addr = (server_ip, server_port)
    tcp_socket.connect(server_addr)
    # 3.发送数据
    send_msg = input("请输入您要发送的消息:")
    tcp_socket.send(send_msg.encode('gbk'))
    # 4.断开连接
    tcp_socket.close()


if __name__ == '__main__':
    main()

31. python中拆包(unpack)的含义及应用场景。

a,b = (1,2) 这种形式即叫做拆包,即将元组拆分分别赋值给两个变量。前提是元组中元素的个数和等待辅助 的变量个数一致,否则就会触发异常。常见使用场景:

  1. 多个变量使用一个元组同时赋值:a,b = (1, 2)
  2. 多值参数 def function(*args, **kwargs) 也是拆包的一种

32. 请尝试向你女朋友解释清楚使用tcp协议创建服务器端程序时的步骤及含义。

使用TCP协议书写服务端程序时,一般按照如下步骤:

  1. 买个手机(创建一个TCP类型的tcp_server_socket)
  2. 给手机买个卡,即有自己运行的号码(绑定IP和工作端口号:bind)
  3. 将手机设置为响铃模式 (使用listen方法将socket更改为被动模式,因为新建的socket默认为主动连接模> 式)
  4. 等待接听 (使用accept方法)

这其中的过程也可以想象成如下场景:

联通公司客服部(TCP服务器端程序)里有个部长叫王大头(tcp_server_socket),王大头负责的是上海市区域(bind方法去绑定socket的工作ip和端口号)的客服工作,我们正常的座机都是能打出去电话能接到电话,而联通公司的电话比较特殊,上面有个按钮,不按它呢,默认只能打出去电话(socket默认新建之后是主动模式),而王大头因为是部长自然拥有高级权限,所以能按下按钮,电话默认只能接听(使用listen方法将socket更改为被动模式)。因此,王大头的工作于是乎很简单,就是等待电话打进来,一直等啊等,(socket的accept方法,会发生堵塞,在没有接收到时,一直处于等待状态),当有电话打进来的时候呢,王大头毕竟是部长,不需要承担客服balabala的工作,而是直接将来电转接给部下的某个人,比如手下小A,小A就会拿起自己的座机开始和来电方进行业务内容上的通话(accept方法的返回值为一个元组(一个新的socket连接用于和已连接的客户端通信,已连接的客户端的地址)),而王大头呢此时再等待下个人打进来就好,有人打进来再分配给小B、小C等。。。。而最后下班的时候,王大头专门交待过,一定要把电话线都拔掉,否则半夜电话还总是响(每个socket都需要去执行close方法,释放内存资源

33. 请书写代码:实现服务器端实现tcp协议通讯的一般程序代码

import socket


def main():
    # 1.买个手机:新建tcp类型的套接字(新建后默认为主动模式)
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 2.买个手机卡:为新建的tcp套接字绑定工作的ip和port
    server_addr = ('', 7890)
    tcp_server_socket.bind(server_addr)

    # 3.将手机调整为响铃模式:将新建的tcp套接字更改为被动模式
    tcp_server_socket.listen(128)

    # 4.等待接听:使用accept方法等待客户端连接(此方法会发生堵塞,即如没有连接则一直处于等待状态)
    # accept -->tuple(socket, socket_addr)
    # socket:自动分配一个新的socket连接用于和已连接客户端的后续通讯 | socket_addr:已连接的客户端地址
    new_client_socket, client_addr = tcp_server_socket.accept()

    # 5.使用新分配的socket用于后续通讯
    recv_msg = new_client_socket.recv(1024)
    print("收到来自ip: %s port: %s 的消息 :%s" % (client_addr[0], client_addr[1], recv_msg.decode('gbk')))
    new_client_socket.send("收到".encode('gbk'))

    # 6.关闭socket
    tcp_server_socket.close()
    new_client_socket.close()


if __name__ == '__main__':
    main()

34. 客户端socket的recv方法,在什么时候会堵塞?什么时候会解堵塞?根据这个特性,我们一般用来做什么操作?

recv_msg = socket.recv(1024)
一旦服务端的程序运行至此,就会发生堵塞:即一直等待直至客户端发来消息或关闭。
socket.recv() 解堵塞:

  1. 接收到客户端发来的消息(一定不为空)
  2. 检测到客户端关闭(此时返回值为空)

在实际项目中,我们通常会利用这个特性,即 if socket.recv(1024): ,来判断原本正在和服务器端通信的客户端是否已经下线。

35. if判断后面可以跟哪些类型的变量?分别返回什么值?

if 后不仅可以跟各种比较运算符连接的表达式,还可以跟各种变量

  1. if int/float : 仅为 0 时判断为False,其余所有数字均为True
  2. if string/tuple/list/dict : 这三个高级变量可以使用len()方法,即长度为0时判断为 False ,长度大于0 则判断为 True
  3. if None : 判断为False

36. 小案例:基于TCP编写服务端代码实现能够按次序服务多个客户端

import socket


def main():
    # 新建socket
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 绑定端口
    tcp_server_socket.bind(('', 8000))
    # 更改为被动模式
    tcp_server_socket.listen(128)

    while True:
        # 等待连接
        print('等待新用户连接...')
        client_socket, client_addr = tcp_server_socket.accept()
        print('新用户 %s 已连接!' % str(client_addr))
        while True:
            # 向客户端发送消息
            client_socket.send("请输入消息:".encode('gbk'))
            # 接收客户端消息
            recv_msg = client_socket.recv(1024).decode('gbk')
            # 判断接收到的消息是否为空:为空则说明客户端已下线
            if recv_msg:
                print("用户(ip: %s |port: %s): %s" % (client_addr[0], client_addr[1], recv_msg))
                continue
            else:
                client_socket.close()
                break

    # 关闭监听
    tcp_server_socket.close()


if __name__ == '__main__':
    main()

37. 操作文件的基础三步骤是什么?为了简化有了什么样的写法?

操作文件的最基本的三步走:

# 1.打开文件
file = open(file_path,'模式')
# 2. 读/写文件
file.read()/file.write()
# 3.关闭文件
file.close()

上述是最简单最基本的写法,可是我们为了放错处理,一般会使用try语句检测读写文件是否会发生异常:

# 1.打开文件
file = open(file_path)
try:
    # 2. 读/写文件
    file.read()/file.write()
except Exception as result:
    # 3.打印错误信息
    print(result)
finally:
    # 4.关闭文件
    file.close()

而文件操作又是非常常规和常见的操作,因此就有了with语法:

with open(file_path) as file:
    file.read()/file.write()

with的存在就是为了让文件操作变简单,不用考虑异常捕获,不用再去手动关闭文件

38. 使用udp或者tcp时绑定端口的操作?

对于udp而言

  • 因为不是面向连接的方式,所以没有客户端和服务器端一说,通常是发送发和接收方两者,因此,作为UDP的发送方,可以选择性的绑定端口(因为程序运行时会自动分配端口)
  • 作为接收方:必须绑定端口去接收,否则都无法知道去从哪个端口监听recv消息。
    对于tcp而言
  • 客户端:tcp客户端一般不绑定端口,因为是主动链接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机
  • 服务器:tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器

39. 请简述腾讯QQ使用udp为主的通讯框架是什么样的?

image.png

你可能感兴趣的:(3. 网络编程(UDP和TCP))