Python 之socket的应用
本节主要讲解socket编程的有关知识点,顺便也会讲解一些其它的关联性知识:
一、概述(socket、socketserver):
python对于socket编程,提供了两个模块,分别是socket和socketserver,它们之间最大的区别在于,socketserver能轻松的实现并发访问,关于这个后面都会介绍的。
先说socket的编程思路:
#客户端创建socket并连接服务器
s = socket.socket()
ip_port = ('127.0.0.1',8089)
s.connect(ip_port) ----------------------------------
#服务端创建socket并等待客户端的连接
s = socket.socket()
ip_port = ('127.0.0.1',8089)
s.bind(ip_port)
s.listen(5)
conn,client_address = s.accept()
下面根据上面的代码做一个简单的介绍:
i. 首先我们先看客户端代码的意思(前提是先在第一行的上面导入socket模块,import socket),第一行创建一个socket对象,对于客户端来说,只要知道服务端的IP和 端口,就可以直接连接了,也就是调用connect()方法就可以了。
ii. 那么我们再来看服务端的代码,第一行也是先生成一个socket对象(在这里插入一句以前讲过的知识点,那就是方法只能通过对象去调用),然后服务端需要绑定一个IP和端口(调用bind方法实现),供客户端去连接,然后调用listen
方法,listen方法的作用是定义坚挺客户端的数量;调用accept方法才是允许客户端连接的额最后一步,它会返回一个连接的对象conn,还会返回客户端的IP地址。
iii. 这样我们就建立了客户端到服务端的初步连接,下面我们要谈的,就是连接是建立起来了,那怎么样传输数据呢?我们通过下面这个实例来进一步的讲解:
#客户端发送字符串的代码
str = 'hello'
s.send(bytes(str,encoding='utf8')) ---------------------------------------- #服务端接收字符串的代码
conn,client_address = s.accept()
recv_data = conn.recv(1024)
print(str(recv_data,encoding='utf8'))
print(recv_data.decode()) ----------------------------------------- #输出结果
hello
hello
解释说明:
1,客户端使用send方法发送一个字符串hello,服务端使用recv方法接受字符串。
2,重点说明的是python2.7版本可以直接发送字符串,但是在python3.0以上的版本都已经不支持这种功能了,改成了只发送和接受字节的形式,所以在你使用python3.0以上版本的时候,要先把字符串转换成字节,然后再发送。
3,不管是把字节转换成字符串,还是把字符串转换成字节,使用的编码都是utf8。
这就是整个发送和接收的流程,其实并不难,因为这就是一个发送和一个接受的过程,如果多的话,就显得有点乱了。
二,并发的实现:
如果你的代码跑在真正的生产环境中,那么就不可能只有一个用户去连接,其它用户都在等待的状态,显然,这是不现实的,那么怎么样才能做到并发处理呢?其实就是用我们上面提到的socketserver模块来实现并发处理的,下面看具体的事例:
client端:
import socket s = socket.socket()
ip_port = ('127.0.0.1',8089)
s.connect(ip_port)
str = 'hello'
s.send(bytes(str,encoding='utf8'))
server端:
import socketserver class Myclass(socketserver.BaseRequestHandler):
def handle(self):
recv_data = self.request.recv(1024)
print(recv_data.decode()) s = socketserver.ThreadingTCPServer(('127.0.0.1',8089),Myclass)
s.serve_forever() #输出结果hello
解释说明:
客户端上的代码是一样的,我们只需要看服务端的代码就可以了;首先导入socketserver模块,然后定义一个类叫Myclass,后面括号里面的意思就是继承BaseRequestHandler这个父类,其实在这里定义类的时候,应该注意两个点:
i. 必须继承上面说的那个父类(没有为啥,只是规定)。
ii. 在类里面定义一个handle方法,这个方法是python内部定义好的,在接受客户端发送的字符串的时候,也是调用的这个方法。
然后是调用这个socketserver模块的另一个类来定义调用的自己的类,和要连接的ip和端口;最后执行s.serve_forver()这个方法(其实这个模块中的类和方法所实现的原理就是判断accept客户端的时候,是否有新的链接连接进来;
如果有新的链接进来的话,socket内部会发生变化,python就是根据这种变化,来建立一个新的连接;使用for循环的方式去接收多用户的连接。)
iii. 其实并发处理用到了两个知识点,一个是IO多路复用,一个是多线程或者多线程。
在这里额外的说一下多进程和多线程,通俗点讲就是:(应用程序---》进程---》线程),其实就是这么的一层关系,但是Python中针对不同的程序用多进程或者多线程是不一样的,比如一个程序不需要大量的运算,那么就不需要
消耗太多的CPU,那么用多线程就比较合适了;如果是计算量比较大,那么在Python中应该用多进程。
三、通过搭建一个简单的ftp服务器来深入得了解socket和其它的一些小知识:
废话不多说,直接上代码,简单明了:
client端:
while True:
ftp_user = input('Input Your ftp user:')
ftp_pass = input('Input Your ftp pass:')
s.send(bytes(ftp_user,encoding='utf8'))
status_code = s.recv(1024)
status_code = str(status_code,encoding='utf8')
if status_code == '':
s.send(bytes(ftp_pass,encoding='utf8'))
status_code = s.recv(2014)
status_code = str(status_code,encoding='utf8')
if status_code == '':
pass
else:
print(status_code)
continue
else:
print(status_code)
continue
while True:
ftp_action = input('请输入你要执行的ftp操作>>>:').strip()
com_dict = {'help/?':'使用帮助','ls':'查看当前用户下的文件或目录','cd':'切换目录',
'put':'上传文件','get':'下载文件','mkdir':'创建目录'}
if ftp_action == 'q':
sys.exit()
if len(ftp_action) == 0:continue
cmd_list = ftp_action.split()
#如果输入put 文件名,就执行put
if cmd_list[0] == 'put':
abs_filepath = cmd_list[1]
if os.path.isfile(abs_filepath):
file_size = os.stat(abs_filepath).st_size
filename = abs_filepath.split('\\')[-1]
print("file:%s,size:%s" %(abs_filepath,file_size))
msg_data = {"action":"put","filename":filename,"file_size":file_size,"ftp_user":ftp_user}
s.send(bytes(json.dumps(msg_data),encoding='utf8'))
recv_data = s.recv(1024)
status_code = json.loads(str(recv_data,encoding='utf8'))
print(status_code)
if status_code['status'] == 200:
print('start send file:',filename)
f = open(abs_filepath,'rb')
for line in f:
s.send(line)
print('send file done')
else:
print('The file is not exist!!')
server端:
user_dict = json.load(open(os.path.join(settings.user_dir,'user_info')))
class MyServer(socketserver.BaseRequestHandler): def handle(self):
#服务端验证客户端输入的用户名和密码
while True:
ftp_user = self.request.recv(1024)
if ftp_user.decode() in user_dict:
self.request.send(bytes('',encoding='utf8'))
ftp_pass = self.request.recv(1024)
str_ftp_pass = ftp_pass.decode()
md = hashlib.md5(str_ftp_pass.encode()).hexdigest()
if md == user_dict[ftp_user.decode()].get('passwd'):
self.request.send(bytes('',encoding='utf8'))
current_dir = {'current_dir':'',}
break
else:
self.request.sendall(bytes('你输入的密码不正确!!',encoding='utf8'))
continue
else:
self.request.sendall(bytes('你输入的用户名不存在!!',encoding='utf8'))
continue
#循环的目的是接收客户端发送的字典信息,然后判断调用不同的方法
while True:
data = self.request.recv(1024)
if len(data) == 0:break task_data = json.loads(data.decode())
task_action = task_data.get('action')
if hasattr(self,"task_%s"%task_action):
func = getattr(self,"task_%s"%task_action)
func(task_data,current_dir)
#如果用户输入的是put命令,就调用此方法
def task_put(self,*args,**kwargs):
filename = args[0].get('filename')
file_size = args[0].get('file_size')
user_dic = args[0].get('ftp_user')
server_response = {"status":200}
self.request.send(bytes(json.dumps(server_response),encoding='utf8'))
f = open(user_dic+'\\'+filename,'wb')
recv_size = 0
while recv_size < file_size:
data = self.request.recv(1024)
f.write(data)
recv_size += len(data)
print('file recv sucess')
f.close()
我想当大家看这段代码的时候,感觉有点乱,那我就跟大家梳理一下思路,以及传输的流程:
1、首先我先给你们说下整个程序大概的一个流程,首先客户端在执行ftp命令之前,先输入用户密码,然后把用户和加密过的密码发送到服务端,让服务端的程序去判断,客户输入的用户名或密码是否正确,然后给出相应的输出;
现在客户端可以执行ftp命令了(在这里只能执行put命令),如果客户端输入的格式正确,就把文件名、文件大小和用户名传到服务端(重点说明:把文件名传过去是想要服务器知道要传的文件,文件大小的作用是为了防止文件
粘包,粘包的结果就是传输不完整(在本实例中,为了粘包是根据客户端提供的文件大小,跟接收的文件大小做判断,只要接收的文件大小小于实际大小,就会一直传输),下面我再详细的讲解粘包的概念;传输用户名的目的是让
该用户只能访问它自己的家目录)
2、我感觉你不明白的可能有两个地方,一个是在server端定义的current_dir字典,在我所列出的代码中,没有体现出来这个字典的真正用图,其实定义它的目的是为了存放每个用户的进入子目录所存的一个标示,存在字典里,然后
用户再执行ls或者mkdir命令的时候,就会从这个字典里取出用户进入的子目录做一列的操作。
3、另外一个可能是hasattr和getattr这两个内置的函数了,hasattr(self,name):这个的意思是说,self对象里面是否包含name方法,如果有,则返回True,如果没有就返回False;
func = get(self,name): 这个和上面那个相结合,就是如果存在就把这个方法或者函数赋值给func,然后由它其传参等操作。
四、附加小知识:
1、在socket客户端和服务端互传数据的时候,如果传输一串字符串很长,那么如果还是用send的话,就可能会出现传输不完全的情况,结果这一个问题的方法是把send换成sendall就OK了!!
Python 之socket的应用的更多相关文章
- 进击的Python【第十章】:Python的socket高级应用(多进程,协程与异步)
Python的socket高级应用(多进程,协程与异步)
- Python底层socket库
Python底层socket库将Unix关于网络通信的系统调用对象化处理,是底层函数的高级封装,socket()函数返回一个套接字,它的方法实现了各种套接字系统调用.read与write与Python ...
- 转:Python 的 Socket 编程教程
这是用来快速学习 Python Socket 套接字编程的指南和教程.Python 的 Socket 编程跟 C 语言很像. Python 官方关于 Socket 的函数请看 http://docs. ...
- Python Udp Socket
socket(套接字),传输层通信的端点,由IP和端口号组成(IP,Port),可以通过socket精确地找到服务器上的进程并与之通信 python2.6实现,基于AF_INET(网络套接字) 类型S ...
- Python Tcp Socket
socket(套接字),传输层通信的端点,由IP和端口号组成(IP,Port),可以通过socket精确地找到服务器上的进程并与之通信 python2.6实现,基于AF_INET(网络套接字) 类型S ...
- python tcp socket 多线程
不多说,直接上代码 client.py #!/usr/bin/python import socket,sys,string host="localhost" port=8000 ...
- 老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具
老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具 poptest是业内唯一的测试开发工程师培训机构,测试开发工程师主要是为测试服务开发测试工具,在工作中要求你做网络级别的安全 ...
- 操作系统底层原理与Python中socket解读
目录 操作系统底层原理 网络通信原理 网络基础架构 局域网与交换机/网络常见术语 OSI七层协议 TCP/IP五层模型讲解 Python中Socket模块解读 TCP协议和UDP协议 操作系统底层原理 ...
- python中socket模块详解
socket模块简介 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.socket通常被叫做"套接字",用于描述IP地址和端口,是一个通信 ...
随机推荐
- WordPress插件制作教程(二): 编写一个简单的插件
上一篇说到了如何创建一个插件,我想大家看了之后一定会有所收获,这一篇简单给大家写一个插件样例,让大家有一个基本的印象.这个插件的样例就是当你激活这个插件后会在你的每篇文章中插入一段自己定义好的内容,比 ...
- excel筛选两列值是否相同,如果相同返回第三列值
见图:
- AFNetworking 使用方法(2.0)
AFNetworking 使用方法(2.0) 分类: IOS2014-11-12 09:17 2018人阅读 评论(0) 收藏 举报 目录(?)[+] 本文介绍的是AFNetworking-2 ...
- wordpress显示多个分类的文章
显示多个分类下的文章可以这样提取: <?php query_posts(array('posts_per_page'=>10,'orderby'=>'rand','category_ ...
- android 显示特殊符号
http://hsx9566.iteye.com/blog/1305052 在android上使用ASCII显示特殊符号 在xml中表示如下: <string name="symbol ...
- cf446C DZY Loves Fibonacci Numbers
C. DZY Loves Fibonacci Numbers time limit per test 4 seconds memory limit per test 256 megabytes inp ...
- Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6629298 在前面一篇文章浅谈Android系 ...
- java public protect default private
(1)对于public修饰符,它具有最大的访问权限,可以访问任何一个在CLASSPATH下的类.接口.异常等.它往往用于对外的情况,也就是对象或类对外的一种接口的形式. (2)对于protected修 ...
- 图片轮播插件 Slides-SlidesJS-3
图片轮播插件 Slides-SlidesJS-3 demo document 地址: http://slidesjs.com/
- js控制html5 audio的暂停、播放、停止
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <meta name ...