文章目录
提要
网络编程简单提要:
IP:
1.用于识别网络上一台独立的计算机(主机)
2.IP地址 = 网络地址 + 主机地址
3.特殊IP地址:127.0.0.1(表示本机)
端口号:
1.用于识别程序进程,每个进程都有自己的端口号
传输协议:
1.UDP:用户数据报协议(UDP,User Datagram Protocol)
1.传输速率快,效率高
2.传输数据时,传输端不必和目的端产生连接,即:传出数据不一定会被接收
3.限制传输数据大小8字节(64K)
2.TCP:传输控制协议(TCP,Transmission Control Protocol)
1.传输速率慢,效率低
2.传输数据前,传输端需要与目的端产生连接
3.可传输大量数据
4.遵循三握手协议:
传输端:我要给你发信息了
目的端:我知道你要给我发信息了
传输端:我知道你知道我要给你发信息了
一、socket模块
1.socket简述
socket:通过IP等进程,在网络上识别某个独立计算机后,可以利用socket模块使双方建立通讯
1.socket内常用方法:
sk = socket.socket() #建立一个socket对象
address=('127.0.0.1',9999)
sk.bind(address) #向socket对象导入IP地址以及端口号
sk.listen(3) #限制连接对象与排队人数为3
conn,address = sk.accept() #接收客户端配置信息,此时由conn与address分别接收
#conn:接收客户端应用配置信息,是在服务端内的客户端的套接字对象,用于与客户端连接以及传输数据
#address:接收客户端地址
服务端:
# conn:客户端的端口信息
conn.recv(1024) #接收客户端传来的信息,限制一次性传来大小为1024字节,可更改大小
conn.send(<信息>) #向客户端传输指定信息,可能未将指定内容全部发送
conn.sendall() #向客户端传输指定信息,尝试传输所有内容,传输失败返回False
客户端:
# sk:客户端的端口信息
sk.recv(1024)
sk.send(<信息>)
sk.sendall()
!!!
注意:send以及recv只能传输或接收bytes类型数据
!!!
2.代码实例(以下皆以本机作为客户端以及服务端,模拟两台主机间的交互):
1.字符交互(代码案例):
服务端:
import socket
sk = socket.socket()
address = ('127.0.0.1',9999)
sk.bind(address)
sk.listen(3)
print('waiting.....')
while True:
conn, ret = sk.accept()
print(ret,end='') #标识连接的客户端IP
print('已连接....')
while True:
try: #捕捉客户端的异常断开
message = conn.recv(1024) #接收客户端信息
except Exception as e:
print('客户端异常退出...\n')
break
if not message: #通过判断客户端传输信息是否存在,决定是否关闭连接端口
print('客户端已退出...')
print('等待连接...')
conn.close()
break
else:
print(str(message, 'utf8'))
conn.send(bytes(input('>>>'), 'utf8')) #向客户端传递信息
客户端:
import socket
sk = socket.socket()
address = ('127.0.0.1',9999)
sk.connect(address)
while True:
data = input('>>>')
if data == 'exit':
break
sk.send(bytes(data, 'utf8'))
message = sk.recv(1024)
print(str(message, 'utf8'))
sk.close()
输出举例:
服务端:
waiting.....
('127.0.0.1', 51570)已连接....
HI
>>>HI
客户端已退出...
等待连接...
客户端:
>>>HI
HI
>>>exit
2.客户端通过服务端执行系统操作指令(代码案例)
服务端:
import socket
import subprocess
sk = socket.socket()
address = ('127.0.0.1',9999)
sk.bind(address)
sk.listen(3)
print('waiting.....')
while True:
conn, ret = sk.accept()
print(ret,end='')
print('已连接....')
while True:
message = conn.recv(1024)
cmd_set = subprocess.Popen(str(message,'utf8'),shell=True,stdout=subprocess.PIPE)
#str(message,'utf8'):指定操作命令
#shell=True参数会让subprocess接受字符串类型的变量作为命令
#stout = subprocess.PIPE -> 将Popen调入主进程,否则将会在其额外进程将其打印在当前屏幕
cmd_result = cmd_set.stdout.read() #接收命令结果,cmd_set.stdout.read()返回值为:bytes类型
cmd_len = len(cmd_result) #传回命令结果长度,方便客户端操作,使其能够完整接收传出结果
conn.sendall(bytes(str(cmd_len),'utf8')) #bytes不能转换int
conn.recv(1024) #解决粘包问题,有时因为前一个sendall传出数据太短,将和后一个sendall同时传出,此时recv起到一个暂停作用
conn.sendall(cmd_result) #传出结果
客户端:
import socket
sk = socket.socket()
address = ('127.0.0.1',9999)
sk.connect(address)
while True:
data = input('>>>')
sk.send(bytes(data, 'utf8')) #发送想要执行的命令
cmd_lens = int(str(sk.recv(1024),'utf8')) #接收命令结果的长度
sk.sendall('') #解决粘包问题
print(cmd_lens) #显示命令结果长度
message = bytes() #建立一个空的buytes类型对象,方便下方统计长度,及时退出循环
while len(message) != cmd_lens: #因为一次只能接收1024字节,所以此时需要使用循环来帮助接收完整的结果
result_cmd = sk.recv(1024)
message+=result_cmd
print(str(message,'GBK'))
sk.close()
输出案例:
服务端:
waiting.....
('127.0.0.1', 52364)已连接....
客户端:
>>>cd
44
D:\compileby\python fullstack\net_function
3.传输一个文件的代码案例(将一个文件从当前脚本目录传输到当前脚本目录下的phtone_home文件夹下)
服务端:
import os
import socket
root_path = os.path.dirname(os.path.abspath(__file__)) #查找脚本文件夹路径
sk = socket.socket()
address = ('127.0.0.1',9999)
sk.bind(address)
sk.listen(3)
while True:
conn, ret = sk.accept()
while True:
data = conn.recv(1024)
cmd_name,file_name,file_size = str(data,'utf8').split('|')
#客户端传来的三个信息:命令,图片名称,图片大小
path = os.path.join(root_path,'phtone_home',file_name)
#图片传输目的地目录
file_size = int(file_size)
f = open(path,'wb')
recv_size = 0
while recv_size != file_size:
file_recv = conn.recv(1024)
f.write(file_recv)
recv_size += len(file_recv)
f.close()
客户端:
import socket
import os
sk = socket.socket()
address = ('127.0.0.1',9999)
sk.connect(address)
root_path = os.path.dirname(os.path.abspath(__file__))
while True:
data = input('请输入命令以及文件名称以‘|’分隔:').strip() #删除结尾换行符
cmd_name,data_name = data.split('|') #将一次性输入的命令与文件名分开
file_path = os.path.join(root_path,data_name) #文件路径
file_name = os.path.basename(file_path) #文件名
file_size = os.stat(file_path).st_size #查找文件大小
file_info = "post|%s|%s"%(file_name,file_size) #将上述得到的命令信息打包
sk.sendall(bytes(file_info,'utf8'))
f = open(file_path,'rb') #rb:将数据以bytes类型读出
num_size = 0
while num_size != file_size:
datas = f.read(1024)
sk.sendall(datas)
num_size+=len(datas)
f.close()
print('上传成功...')
服务端输出:
客户端输出:
请输入命令以及文件名称以‘|’分隔:post|11.jpg
上传成功...
运行前:
运行后: