1. ip地址的作用
在网络中唯一标识一台主机,可以理解为"收货地址"。
2. 在windows中利用命令行查看网卡信息(ip地址)
ipconfig
3. 在Linux中查看网卡信息(ip地址)的指令
ifconfig
4. Linux在编写shell指令时,快速返回行首/行尾的快捷键
- 快速返回行首:
Ctrl + A
- 快速返回行尾:
Ctrl + E
5. 在Linux中 关闭/开启 某个网卡的指令
- 利用ifconfig查看网卡信息确认名称网卡,比如:ens40
- 关闭网卡
ifconfig ens40 down
- 开启网卡
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)
方法用于套接字发送数据,有两个参数:
- data:发送的内容,必须为 bytes 类型 ,在 python 中将字符串转换为 bytes 类型有两种方法:
(1)直接在字符串前面加 b ,例如:b"string"
(2)使用 encode(编码方式) 方法,例如 :"string".encode('utf-8') 常用的编码格式有 utf-8、gbk、ASCII
dest_addr- 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发送数据:
- 新建udp类型的socket套接字
- 选择性绑定端口:因为发送发无需使用特定的端口去发送数据,当不绑定任何端口时,系统在运行程序时, 会分配一个随机端口供其使用。每个进程使用的端口号必然是唯一的
- 发送数据 sendto
- 关闭socket
udp类型socket接收数据:
- 新建udp类型的socket套接字
- 强制性绑定端口:因为必须指定当前的socket去监听哪个端口,才可能接收到信息。需要注意的是,绑定端> 口时候使用bind方法,其中传入的参数为一个元组类型--(IP地址_str,端口号_int),因为默认都是监听当前> IP,故可以省略不写,默认为''
- 接收数据并打印显示 recvfrom
- 关闭socket
21. 什么是私有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协议的两大特点:
- 面向连接:通信双方必须先建立连接才能进行数据的传输,双方间的数据传输都可以通过这一个连接进行, 完成数据交换后,双方必须断开此连接,以释放系统资源。这种连接是一对一的, 因此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) 这种形式即叫做拆包,即将元组拆分分别赋值给两个变量。前提是元组中元素的个数和等待辅助 的变量个数一致,否则就会触发异常。常见使用场景:
- 多个变量使用一个元组同时赋值:a,b = (1, 2)
- 多值参数 def function(*args, **kwargs) 也是拆包的一种
32. 请尝试向你女朋友解释清楚使用tcp协议创建服务器端程序时的步骤及含义。
使用TCP协议书写服务端程序时,一般按照如下步骤:
- 买个手机(创建一个TCP类型的tcp_server_socket)
- 给手机买个卡,即有自己运行的号码(绑定IP和工作端口号:bind)
- 将手机设置为响铃模式 (使用listen方法将socket更改为被动模式,因为新建的socket默认为主动连接模> 式)
- 等待接听 (使用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() 解堵塞:
- 接收到客户端发来的消息(一定不为空)
- 检测到客户端关闭(此时返回值为空)
在实际项目中,我们通常会利用这个特性,即
if socket.recv(1024):
,来判断原本正在和服务器端通信的客户端是否已经下线。
35. if判断后面可以跟哪些类型的变量?分别返回什么值?
if 后不仅可以跟各种比较运算符连接的表达式,还可以跟各种变量:
- if int/float : 仅为 0 时判断为False,其余所有数字均为True
- if string/tuple/list/dict : 这三个高级变量可以使用len()方法,即长度为0时判断为 False ,长度大于0 则判断为 True
- 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服务器一般情况下都需要绑定,否则客户端找不到这个服务器