Python3的socket和socketserver网络编程(聊天和文件传输功能)


前言

接触了Python黑帽子一本书,看到了网络编程socket这一章,觉得挺有意思的,花了一点时间去学了一下,写了一份代码,主要是实现同一ip和端口下用cmd进行聊天和传输文件,很多大神写的没有注释看起来有点吃力,我在代码中已经详细给出了注释。


提示:以下是本篇文章正文内容,下面案例可供参考

一、TCP以及UDP的选择

TCP的可靠保证,是它的三次握手双向机制,这一机制保证校验了数据,保证了他的可靠性。而UDP就没有了,udp信息发出后,不验证是否到达对方,所以不可靠。

所以我写了一个TCP服务器和客户端。都是可以直接拿下来用的。因为只是测试版本还有很多地方没有优化,仅供参考。

二、socket以及socketserver

Python提供了两个基本的socket模块。一个是socket,它提供了标准的BSD Socket API;另一个是socketServer,它提供了服务器中心类,可以简化网络服务器的开发。我的TCP这两个都用到了,可以给你们一个参考。他们的具体区别和使用可以参考:https://www.cnblogs.com/panwenbin-logs/p/5655130.html

三、使用步骤

1.打开cmd输入python total_sever.py,创建服务器

代码如下(示例):

import socket
import threading 													# 导入多线程模块
import socketserver,struct,os

Hostport=('127.0.0.1',12303)
hostport=('127.0.0.1',12304)
true = True

#聊天功能
def chatsend():
	global true
	print("Watiing to be connected....")
	global Hostport
	s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)			# 创建socket实例
	s.bind(Hostport)
	s.listen(1)
	conn,addr = s.accept()
	addr = str(addr)
	print("Connecting by: "+addr)
	def Receve(conn):												# 将接收定义成一个函数
		global true 												# 声明全局变量,当接收到的消息为quit时,则触发全局变量 true = False,则会将socket关闭
		while true:
			
			data = conn.recv(1024).decode('utf8')
			if data == "quit":										# 当接收的值为'quit'时,退出接收线程,否则,循环接收并打印
				true = False
			print("You have receve: "+data)
	thrd=threading.Thread(target=Receve,args=(conn,))				# 线程实例化,target为方法,args为方法的参数
	thrd.start()													# 启动线程
	while true:
		uesr_input = input("请输入>>> \n")
		conn.send(uesr_input.encode('utf8'))						# 循环发送消息
		if uesr_input == 'quit':                                    # 当发送为‘quit’时,关闭socket
			true = False
		elif uesr_input == "文件发送":
			filesend()												# 输入为文件发送时调用filesend()


# 文件传输功能
def filesend():
	try:
		class MyTCP(socketserver.BaseRequestHandler):
			def handle(self):
				try:
					print("已成功连接上: ",self.client_address)
					file_define_size = struct.calcsize('128sl')									# 定义文件大小信息,128s表示文件名为128bytes长,l表示一个int或log文件类型,在此为文件大小
					self.file=self.request.recv(file_define_size)								# 接受信息并且将文件信息大小代入
					global true
					while true:
						if self.file:															# 如果文件大小存在
							self.file_name,self.file_size=struct.unpack('128sl',self.file)		# 根据128sl解包信息,与client端的打包规则相同,开始解包。
							self.file_name=self.file_name.decode('utf8').strip('\00')			# 使用strip()删除打包时附加的多余空字符
							'''网络通信的一个数据包中,包含了文件类型、文件名、文件长度三项其中文件名默认是32字,用struct进行解包
							虽然文件名输出出来是File,但实际上剩下的28个字用"\0"进行补全'''
							print('文件内容大小: ',self.file_size,'文件名字: ',self.file_name)
							self.file_new_name = os.path.join("d:\\",self.file_name)#文件路径
							print('文件存储的路径为',self.file_new_name)
							recvd_filesize = 0 													# 定义了接收的文件大小
							files = open(self.file_new_name,"wb")								# 写入文件
							print("开始接收文件...")
							while not recvd_filesize == self.file_size:
								if self.file_size==0:
									break
								elif self.file_size - recvd_filesize > 10:
									rdata = self.request.recv(10)								# 这里设置为10,方便cmd看文件过大如何进行增加size的
									recvd_filesize += len(rdata)
									print(str(recvd_filesize)+"被传输")
								else:
									rdata = self.request.recv(self.file_size - recvd_filesize)	# 这里将剩下的内容补上
									recvd_filesize = self.file_size 							# 退出while循环
									print(str(recvd_filesize)+"补全")
									tcpSever.shutdown()											# 相当于结束线程
								files.write(rdata)
							files.close()														# 关闭open
							print("接收完毕")
							chatsend()
				except:
					pass
		tcpSever = socketserver.ThreadingTCPServer(hostport,MyTCP)								# TCPServer是接收到请求后执行handle方法,这里是相当于建立新线程的方法运行handle
		tcpSever.serve_forever()																# 相当于循环启动线程
	except:
		pass

if __name__ == '__main__':
	print('''
请选择功能:
	1.聊天(附带文件发送功能。输入文件发送)
		''')
	sda=True
	while sda:
		keyboard_input=input()
		if keyboard_input=="1":
			chatsend()
			sda=False
		else:
			print("输入选择项错误请重新输入")
			continue

2.打开cmd输入python total_client.py,创建客户端

代码如下(示例):


# -*- coding: UTF-8 -*-
import socket,os,struct
import threading
import time
true=True
#聊天
def chatsend():
	global true
	hostport=('127.0.0.1',12303)										# 这是本地的可以采用cmd的netstat查看IP和端口,端口数字往后移动一位
	s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)				# 创建socket实例
	s.connect(hostport)													# 连接IP和端口
	def Receve(s):
		global true
		while true:
			data=s.recv(1024).decode("utf8")
			if data == "quit":
				true = False
			elif data == "文件发送":
				sendfile()
			print('receve news: '+data)
	thrd=threading.Thread(target=Receve,args=(s,))						# 线程实例化,target为方法,args为方法的参数
	thrd.start()														# 启动线程
	while true:
		uesr_input=input("请输入: \n")
		s.send(uesr_input.encode("utf8"))
		if uesr_input == "quit":										# 当发送为‘quit’时,关闭socket
			true = False



#发送文件
def sendfile():
	s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
	s.connect(('127.0.0.1',12304))								
	true = True
	def xunhuanchuanshu(s):
		filepath = input("对方请求发送文件,请输入文件的绝对路径:\r\n")												# 换行符为各系统默认的换行符(\n, \r, or \r\n, )
		if os.path.isfile(filepath):
			file_pack =struct.pack('128sl',os.path.basename(filepath).encode('utf8'),os.stat(filepath).st_size) # pack需要定义文件头信息,包含文件名和文件大小
			s.send(file_pack)																					# 发送包
			print('客户端传输文件绝对路径: ', filepath)
			open_file = open(filepath,'rb')																		# 读取成二进制格式数据
			while True:
				file_data = open_file.read(1024)
				if not file_data:																				# 判断文件是否存在,并且进行传输
					break
				s.send(file_data)																				# 发送数据
			open_file.close()
			print('传输完成!')
	thrd=threading.Thread(target=xunhuanchuanshu,args=(s,))														# 线程实例化,target为方法,args为方法的参数
	thrd.start()



if __name__ == '__main__':
	print('''
请选择功能:
	1.聊天(附带文件发送功能。输入文件发送)
		''')
	sda=True
	while sda:
		keyboard_input=input()
		if keyboard_input=="1":
			chatsend()
			sda=False
		else:
			print("输入选择项错误请重新输入")
			continue

四、使用截图

1.先要打开cmd运行total_sever.py文件打开服务器等待连接。在这里插入图片描述
2.然后打开cmd运行total_client.py文件连接服务器。在这里插入图片描述
3.实现聊天在这里插入图片描述在这里插入图片描述
4.实现聊天中用total_client实现请求文件,total_client发送并且不中断聊天(如需双方都发送文件功能需要自己加写代码)
在这里插入图片描述
在这里插入图片描述

五、参考

https://blog.csdn.net/Dyy8686/article/details/79356189?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param

https://blog.csdn.net/weixin_37579123/article/details/85841251

总结

这份代码是我自己写的一个测试版本,很多功能和需要优化的地方还没有进行优化,需要用的同学可以自己参考然后进行修改。
经过测试可以实现两台电脑一起聊天和发送文件,前提是对方电脑能够在cmd里面ping的通你的ip,ip和端口可以使用cmd然后输入netstat进行查看,如果是192.168.12.3:2020,那么你需要在代码写为192.168.12.3:2021。
127.0.0.1是本地ip的意思,本地测试可以使用。

猜你喜欢

转载自blog.csdn.net/qq_41866988/article/details/108536305