打开文件
open(name[.mode[.buffering]])
mode参数
‘r’ | ‘w’ | ‘a’ | ‘b’ | ‘+’ |
---|---|---|---|---|
读模式 | 写模式 | 追加模式 | 二进制模式(可添加到其他模式中使用) | 读/写模式(可添加到其他模式中使用) |
缓冲区
文件读取
filename = '/file'
#######################
# VER 1
#######################
f = open(filename)
f.read() # 文件内容的str对象
f.close()
#######################
# VER 2
#######################
try:
f = open(filename)
print(f.read())
finally:
if(f):
f.close()
#######################
# VER 3
#######################
with open(filename) as f:
print(f.read())
#######################
# VER 4
#######################
with open(filename) as f:
for line in f.readlines():
print(line.strip())
文件写入
filename = '/file'
str = 'write content'
#######################
# VER 1
#######################
f = open(filename, 'w')
f.write(str)
f.close()
#######################
# VER 2
#######################
with open(filename, 'w') as f:
f.write(str)
操作 | 作用 |
---|---|
os.getcwd() | 获得当前Python脚本工作的目录路径 |
os.listdir() | 返回指定目录下的所有文件和目录名 |
os.remove(path) | 删除一个文件 |
os.removedirs(dir) | 删除多个空目录 |
os.path.isfile(filepath) | 检验给出的路径是否是一个文件 |
os.path.isdir(dirpath) | 检验给出的路径是否是一个目录 |
os.isabs() | 判断是否是绝对路径 |
os.path.exists() | 检验路径是否真的存在 |
os.path.split() | 分离一个路径的目录名和文件名 |
os.path.splitext() | 分离扩展名 |
os.path.dirname(filepath) | 获取路径名 |
os.path.basename(filepath) | 获取文件名 |
os.getenv(), os.putenv() | 读取和设置环境变量 |
os.linesep | 给出当前平台的行终止符(Windows:\r\n,Linux:‘\n’,Mac:’r’) |
os.name | 指示正在使用的平台(Windows:‘nt’,Linux/Unix:‘posix’) |
os.rename(old, new) | 重命名文件或目录 |
os.makedirs(dir) | 创建多级目录 |
os.mkdir(dir) | 创建单个目录 |
os.stat(file) | 获取文件属性 |
os.chmod(file) | 修改文件权限与时间戳 |
os.path.getsize(filename) | 获取文件大小 |
shutil.copytree(olddir, newdir) | 复制文件夹,两个参数都必须是目录,且newdir必须不存在 |
shutil.copyfile(oldfile, newfile), shutil.copy(oldfile, newfile) | 复制文件,copyfile()中两个参数都必须是文件;copy()中,oldfile只能是文件,而newfile可以是文件也可以是目标目录 |
shutil.move(oldpos, newpos) | 移动文件或目录 |
os.rmdir(dir), shutil.rmtree(dir) | 删除目录,os.rmdir()只能删除空目录;shutil.rmtree()可以删除空目录或有内容的目录 |
把内存中的变量编程可存储或可传输的过程,就是序列化
将内存中的变量序列化后,可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上,实现程序状态的保存和共享。读取这些数据的过程,称为反序列化。
在Python中提供了两个模块来实现序列化:cPickle和pickle,前者是使用C语言编写的,效率比后者高很多
一般编写程序时,会先导入cPickle模块,如果不存在该模块,再导入pickle模块:
try:
import cPickle as pickle
expect ImportError:
import pickle
dumps()方法可以将任意对象序列化成一个str,loads()方法可以读取这样的str并将其转换为对象
dump()方法可以将任意对象序列化并写入文件中,load()方法可以读取这样的文件并将其转换为对象
try:
import cPickle as pickle
expect ImportError:
import pickle
data = '要序列化的任意格式数据'
filename = './file'
#######################
# dumps
#######################
pickle.dumps(data)
#######################
# dump
#######################
f = open(filename, 'wb')
pickle.dump(data, f)
f.close()
#######################
# loads
#######################
loads(str) # 经过dumps得到的str
#######################
# dump
#######################
f = open(filename, 'rb')
data = pickle.load(f)
f.close()
fork方法是调用一次,返回两次。fork方法会在当前进程中复制出一份几乎完全相同的子进程
子进程永远返回0,父进程返回子进程的ID
getpid方法用于获取当前进程的ID,getppid方法用户获取父进程的ID
import os
if __name__ == '__main__':
print('current Process (%s) start ...'%(os.getpid()))
pid = os.fork()
if(pid < 0):
print('error in fork')
elif(pid == 0):
print('I am child process (%s) and my parent process is (%s)'%(os.getpid(), os.getppid()))
else:
print('I(%s) created a child process (%s).'%(os.getpid(), pid))
# 输出:
# current Process (70923) start ...
# I(70923) created a child process (70924).
# I am child process (70924) and my parent process is (70923)
创建子进程时,只需要传入一个执行函数和函数的参数
用start()方法启动进程
用join()方法实现进程间的同步
import os
from multiprocessing import Process
def run_proc(name):
print('Child process %s (%s) Running...' % (name, os.getpid()))
if __name__ == '__main__':
print('Parent process %s.' % os.getpid())
for i in range(5):
p = Process(target=run_proc, args=(str(i), ))
print('Process will start')
p.start()
p.join()
print('Process end')
# 输出:
# Parent process 128285.
# Process will start
# Process will start
# Process will start
# Process will start
# Process will start
# Child process 1 (128287) Running...
# Child process 0 (128286) Running...
# Child process 4 (128290) Running...
# Process end
# Child process 3 (128289) Running...
# Child process 2 (128288) Running...
Pool可以提供指定数量的进程供用户调用,默认大小是CPU的核数
当有新的请求到来时:
apply_async()方法可以向进程池请求一个进程
Pool对象调用join()方法会等待所有子程序执行完毕
调用join()方法之前必须先调用close()方法,调用close()之后就不能继续添加新的Process了
from multiprocessing import Pool
import os, time, random
def run_task(name):
print('Task %s (pid = %s) is running...' % (name, os.getpid()))
time.sleep(random.random()*3)
print('Task %s end.' %name)
if __name__ == '__main__':
print('Current process %s.' % os.getpid())
p = Pool(processes=3) # 设置进程池的最大进程数为3
for i in range(5):
p.apply_async(run_task, args=(i, ))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
# 输出:
# Current process 128323.
# Waiting for all subprocesses done...
# Task 0 (pid = 128324) is running...
# Task 1 (pid = 128326) is running...
# Task 2 (pid = 128325) is running...
# Task 2 end.
# Task 3 (pid = 128325) is running...
# Task 1 end.
# Task 4 (pid = 128326) is running...
# Task 4 end.
# Task 0 end.
# Task 3 end.
# All subprocesses done.
Python创建了多种进程间通信的方式,如Queue,Pipe,Value+Array等。本书主要讲解前两种方式。
Queue和Pipe的区别在于Pipe常用来在两个进程间通信,而Queue用来在多个进程间实现通信
Queue:
from multiprocessing import Process, Queue
import os, time, random
# 写数据进程执行的代码:
def proc_write(q,urls):
print('Process(%s) is writing...' % os.getpid())
for url in urls:
q.put(url)
print('Put %s to queue...' % url)
time.sleep(random.random())
# 读数据进程执行的代码:
def proc_read(q):
print('Process(%s) is reading...' % os.getpid())
while True:
url = q.get(True)
print('Get %s from queue.' % url)
if __name__=='__main__':
# 父进程创建Queue,并传给各个子进程:
q = Queue()
proc_writer1 = Process(target=proc_write, args=(q,['url_1', 'url_2', 'url_3']))
proc_writer2 = Process(target=proc_write, args=(q,['url_4','url_5','url_6']))
proc_reader = Process(target=proc_read, args=(q,))
# 启动子进程proc_writer,写入:
proc_writer1.start()
proc_writer2.start()
# 启动子进程proc_reader,读取:
proc_reader.start()
# 等待proc_writer结束:
proc_writer1.join()
proc_writer2.join()
# proc_reader进程里是死循环,无法等待其结束,只能强行终止:
proc_reader.terminate()
# 输出:
# Process(128409) is writing...
# Put url_1 to queue...
# Process(128410) is writing...
# Put url_4 to queue...
# Process(128411) is reading...
# Get url_1 from queue.
# Get url_4 from queue.
# Put url_5 to queue...
# Get url_5 from queue.
# Put url_2 to queue...
# Get url_2 from queue.
# Put url_3 to queue...
# Get url_3 from queue.
# Put url_6 to queue...
# Get url_6 from queue.
Pipe:
import multiprocessing
import random
import time,os
def proc_send(pipe,urls):
for url in urls:
print("Process(%s) send: %s" %(os.getpid(),url))
pipe.send(url)
time.sleep(random.random())
def proc_recv(pipe):
while True:
print("Process(%s) rev:%s" %(os.getpid(),pipe.recv()))
time.sleep(random.random())
if __name__ == '__main__':
pipe = multiprocessing.Pipe()
p1 = multiprocessing.Process(target=proc_send, args=(pipe[0], ['url_'+str(i) for i in range(10)]))
p2 = multiprocessing.Process(target=proc_recv, args=(pipe[1], ))
p1.start()
p2.start()
p1.join()
p2.terminate()
# 输出:
# Process(128467) send: url_0
# Process(128468) rev:url_0
# Process(128467) send: url_1
# Process(128468) rev:url_1
# Process(128467) send: url_2
# Process(128468) rev:url_2
# Process(128467) send: url_3
# Process(128468) rev:url_3
# Process(128467) send: url_4
# Process(128467) send: url_5
# Process(128468) rev:url_4
# Process(128467) send: url_6
# Process(128468) rev:url_5
# Process(128467) send: url_7
# Process(128467) send: url_8
# Process(128468) rev:url_6
# Process(128468) rev:url_7
# Process(128468) rev:url_8
# Process(128467) send: url_9
# Process(128468) rev:url_9
import random
import time, threading
# 新线程执行的代码:
def thread_run(urls):
print('Current %s is running...' % threading.current_thread().name)
for url in urls:
print('%s ---->>> %s' % (threading.current_thread().name,url))
time.sleep(random.random())
print('%s ended.' % threading.current_thread().name)
print('%s is running...' % threading.current_thread().name)
t1 = threading.Thread(target=thread_run, name='Thread_1',args=(['url_1','url_2','url_3'],))
t2 = threading.Thread(target=thread_run, name='Thread_2',args=(['url_4','url_5','url_6'],))
t1.start()
t2.start()
t1.join()
t2.join()
print('%s ended.' % threading.current_thread().name)
# 输出:
# MainThread is running...
# Current Thread_1 is running...
# Thread_1 ---->>> url_1
# Current Thread_2 is running...
# Thread_2 ---->>> url_4
# Thread_2 ---->>> url_5
# Thread_1 ---->>> url_2
# Thread_2 ---->>> url_6
# Thread_1 ---->>> url_3
# Thread_2 ended.
# Thread_1 ended.
# MainThread ended.
import random
import threading
import time
class myThread(threading.Thread):
def __init__(self,name,urls):
threading.Thread.__init__(self,name=name)
self.urls = urls
def run(self):
print('Current %s is running...' % threading.current_thread().name)
for url in self.urls:
print('%s ---->>> %s' % (threading.current_thread().name,url))
time.sleep(random.random())
print('%s ended.' % threading.current_thread().name)
print('%s is running...' % threading.current_thread().name)
t1 = myThread(name='Thread_1',urls=['url_1','url_2','url_3'])
t2 = myThread(name='Thread_2',urls=['url_4','url_5','url_6'])
t1.start()
t2.start()
t1.join()
t2.join()
print('%s ended.' % threading.current_thread().name)
# 输出:
# MainThread is running...
# Current Thread_1 is running...
# Thread_1 ---->>> url_1
# Current Thread_2 is running...
# Thread_2 ---->>> url_4
# Thread_2 ---->>> url_5
# Thread_1 ---->>> url_2
# Thread_2 ---->>> url_6
# Thread_1 ---->>> url_3
# Thread_1 ended.
# Thread_2 ended.
# MainThread ended.
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步
使用Thread对象的Lock和RLock可实现简单的线程同步
对于Lock对象,如果一个线程连续两次进行acquire操作,由于第一次acquire没有release,第二次acquire将挂起线程,导致永远都不会release,形成死锁
对于RLock对象,允许一个线程多次进行acquire操作,其内部有一个counter维护acquire的次数,而且每次acquire必须对应于一个release,在完成所有的release操作后,别的线程才能申请RLock对象
import threading
mylock = threading.RLock()
num=0
class myThread(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self,name=name)
def run(self):
global num
while True:
mylock.acquire()
print('%s locked, Number: %d'%(threading.current_thread().name, num))
if num>=4:
mylock.release()
print('%s released, Number: %d'%(threading.current_thread().name, num))
break
num+=1
print('%s released, Number: %d'%(threading.current_thread().name, num))
mylock.release()
if __name__== '__main__':
thread1 = myThread('Thread_1')
thread2 = myThread('Thread_2')
thread1.start()
thread2.start()
# 输出:
# Thread_1 locked, Number: 0
# Thread_1 released, Number: 1
# Thread_1 locked, Number: 1
# Thread_1 released, Number: 2
# Thread_1 locked, Number: 2
# Thread_1 released, Number: 3
# Thread_1 locked, Number: 3
# Thread_1 released, Number: 4
# Thread_1 locked, Number: 4
# Thread_1 released, Number: 4
# Thread_2 locked, Number: 4
# Thread_2 released, Number: 4
协程,又称微线程,纤程,是一种用户级的轻量级线程
协程拥有自己的寄存器上下文和栈
协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
在并发编程中,协程与线程类似,每个协程表示一个执行单元,有自己的本地数据,与其他协程共享全局数据和其他资源
协程需要用户自己来编写调度逻辑。对于CPU来说,协程其实是单线程,所以CPU不用处理调度、切换上下文,这就省去了CPU的切换开销
Python通过yield提供了对协程的基本支持,但是不完全。而使用第三方gevent库是更好的选择
gevent是一个基于协程的Python网络函数库,使用greenlet在libev事件循环顶部提供了以一个有高级别并发性的API
gevent对协程的支持,本质上是greenlet在实现切换工作
greenlet工作流程:如果进行访问网络的IO操作时出现阻塞,greenlet就显式切换到另一端没有被阻塞的代码段执行,直到原先的阻塞状况消失以后,再自动切换回原来的代码段继续处理
因此,greenlet是一种合理安排的串行方式
有了gevent自动切换协程,就保证总有greenlet在运行,而不是等待IO,这就是协程比一般多线程效率高的原因
spawn()方法可以用来形成协程
joinall()方法可以添加这些协程任务并启动运行
从运行结果来看,3个网络操作是并发执行的,而且结束顺序不同,但其实只有一个线程
from gevent import monkey; monkey.patch_all()
import gevent
import urllib.request
def run_task(url):
print('Visit --> %s' % url)
try:
response = urllib.request.urlopen(url)
data = response.read()
print('%d bytes received from %s.' % (len(data), url))
except Exception as e:
print(e)
if __name__=='__main__':
urls = ['https://github.com/','https://www.python.org/','http://www.cnblogs.com/']
greenlets = [gevent.spawn(run_task, url) for url in urls ]
gevent.joinall(greenlets)
# 输出:
# Visit --> https://github.com/
# Visit --> https://www.python.org/
# Visit --> http://www.cnblogs.com/
# 71025 bytes received from http://www.cnblogs.com/.
# 219611 bytes received from https://github.com/.
# 49982 bytes received from https://www.python.org/.
gevent中还提供了对池的支持
当拥有动态数量的greenlet需要进行并发管理时,就可以使用池
from gevent import monkey
monkey.patch_all()
import urllib.request
from gevent.pool import Pool
def run_task(url):
print('Visit --> %s' % url)
try:
response = urllib.request.urlopen(url)
data = response.read()
print('%d bytes received from %s.' % (len(data), url))
except Exception as e:
print(e)
return 'url:%s --->finish' % url
if __name__=='__main__':
pool = Pool(2)
urls = ['https://github.com/','https://www.python.org/','http://www.cnblogs.com/']
results = pool.map(run_task,urls)
print(results)
# 输出:
# Visit --> https://github.com/
# Visit --> https://www.python.org/
# 219605 bytes received from https://github.com/.
# Visit --> http://www.cnblogs.com/
# 71065 bytes received from http://www.cnblogs.com/.
# 49982 bytes received from https://www.python.org/.
# ['url:https://github.com/ --->finish', 'url:https://www.python.org/ --->finish', 'url:http://www.cnblogs.com/ --->finish']
分布式进程指的是将Process进程分布到多台机器上,充分利用多台机器的性能完成复杂的任务
multiprocessing的managers子模块支持把多进程分布到多台机器上
通过一个服务进程作为调度者,将任务分布到其他多个进程中,依靠网络通信进行管理
举个例子:要爬取一个网站上所有的图片
实现上面的例子,创建分布式进程(服务进程)需要分为六个步骤
创建任务进程需要以下步骤:
服务进程样例(Linux版):
import random,time
import queue as Queue
from multiprocessing.managers import BaseManager
#实现第一步:建立task_queue和result_queue,用来存放任务和结果
task_queue=Queue.Queue()
result_queue=Queue.Queue()
class Queuemanager(BaseManager):
pass
#实现第二步:把创建的两个队列注册在网络上,利用register方法,callable参数关联了Queue对象,
# 将Queue对象在网络中暴露
Queuemanager.register('get_task_queue',callable=lambda:task_queue)
Queuemanager.register('get_result_queue',callable=lambda:result_queue)
#实现第三步:绑定端口8001,设置验证口令‘qiye’。这个相当于对象的初始化
manager=Queuemanager(address=('',8001),authkey=bytes('qiye', encoding='utf-8'))
#实现第四步:启动管理,监听信息通道
manager.start()
#实现第五步:通过管理实例的方法获得通过网络访问的Queue对象
task=manager.get_task_queue()
result=manager.get_result_queue()
#实现第六步:添加任务
for url in ["ImageUrl_"+str(i) for i in range(10)]:
print('put task %s ...' %url)
task.put(url)
#获取返回结果
print('try get result...')
for i in range(10):
print('result is %s' %result.get(timeout=10))
#关闭管理
manager.shutdown()
# 输出:
# put task ImageUrl_0 ...
# put task ImageUrl_1 ...
# put task ImageUrl_2 ...
# put task ImageUrl_3 ...
# put task ImageUrl_4 ...
# put task ImageUrl_5 ...
# put task ImageUrl_6 ...
# put task ImageUrl_7 ...
# put task ImageUrl_8 ...
# put task ImageUrl_9 ...
# try get result...
# result is ImageUrl_0--->success
# result is ImageUrl_1--->success
# result is ImageUrl_2--->success
# result is ImageUrl_3--->success
# result is ImageUrl_4--->success
# result is ImageUrl_5--->success
# result is ImageUrl_6--->success
# result is ImageUrl_7--->success
# result is ImageUrl_8--->success
# result is ImageUrl_9--->success
服务进程样例(Windows版):
# taskManager.py for windows
import queue as Queue
from multiprocessing.managers import BaseManager
from multiprocessing import freeze_support
#任务个数
task_number = 10
#定义收发队列
task_queue = Queue.Queue(task_number)
result_queue = Queue.Queue(task_number)
def get_task():
return task_queue
def get_result():
return result_queue
# 创建类似的QueueManager:
class QueueManager(BaseManager):
pass
def win_run():
#windows下绑定调用接口不能使用lambda,所以只能先定义函数再绑定
QueueManager.register('get_task_queue',callable = get_task)
QueueManager.register('get_result_queue',callable = get_result)
#绑定端口并设置验证口令,windows下需要填写ip地址,linux下不填默认为本地
manager = QueueManager(address = ('127.0.0.1',8001),authkey = b'qiye')
#启动
manager.start()
try:
#通过网络获取任务队列和结果队列
task = manager.get_task_queue()
result = manager.get_result_queue()
#添加任务
for url in ["ImageUrl_"+str(i) for i in range(10)]:
print('put task %s ...' %url)
task.put(url)
print('try get result...')
for i in range(10):
print('result is %s' %result.get(timeout=10))
except:
print('Manager error')
finally:
#一定要关闭,否则会爆管道未关闭的错误
manager.shutdown()
if __name__ == '__main__':
#windows下多进程可能会有问题,添加这句可以缓解
freeze_support()
win_run()
任务进程样例:
#coding:utf-8
import time
from multiprocessing.managers import BaseManager
# 创建类似的QueueManager:
class QueueManager(BaseManager):
pass
# 实现第一步:使用QueueManager注册获取Queue的方法名称
QueueManager.register('get_task_queue')
QueueManager.register('get_result_queue')
# 实现第二步:连接到服务器:
server_addr = '127.0.0.1'
print('Connect to server %s...' % server_addr)
# 端口和验证口令注意保持与服务进程设置的完全一致:
m = QueueManager(address=(server_addr, 8001), authkey=bytes('qiye', encoding='utf-8'))
# 从网络连接:
m.connect()
# 实现第三步:获取Queue的对象:
task = m.get_task_queue()
result = m.get_result_queue()
# 实现第四步:从task队列取任务,并把结果写入result队列:
while(not task.empty()):
image_url = task.get(True,timeout=5)
print('run task download %s...' % image_url)
time.sleep(1)
result.put('%s--->success'%image_url)
# 处理结束:
print('worker exit.')
# 输出:
# Connect to server 127.0.0.1...
# run task download ImageUrl_0...
# run task download ImageUrl_1...
# run task download ImageUrl_2...
# run task download ImageUrl_3...
# run task download ImageUrl_4...
# run task download ImageUrl_5...
# run task download ImageUrl_6...
# run task download ImageUrl_7...
# run task download ImageUrl_8...
# run task download ImageUrl_9...
# worker exit.
Socket(套接字)是网络编程的一个抽象概念,用于表示“打开了一个网络连接”
建立一个socket链接需要知道目标IP地址和端口号,以及指定协议类型
Python提供了两个基本的socket模块:
套接字格式为:socket(family, type[, protocal]),使用给定的地址族,套接字类型,协议编号(默认为0)来创建套接字
套接字类型:
Socket类型 | 描述 |
---|---|
socket.AF_UNIX | 只能用于单一的Unix系统进程间通信 |
socket.AF_INET | 服务器之间网络通信 |
socket.AF_INET6 | IPv6 |
socket.SOCK_STREAM | 流式socket,用于TCP |
socket.SOCK_DGRAM | 数据报式socket,用于UDP |
socket.SOCK_RAW | 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而原始套接字可以。其次,SOCK_RAW可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头 |
socket.SOCK_SEQPACKET | 可靠的连续数据包服务 |
创建TCP Socket | s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
创建UDP Socket | s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
Socket函数:
Socket函数 | 描述 |
---|---|
服务端Socket函数 | |
s.bind(address) | 将套接字绑定到地址,在AF_INET下,以元组(host, port)的形式表示地址 |
s.listen(backlog) | 开始监听TCP传入连接。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量,至少为1,大部分应用程序设为5就可以了 |
s.accept() | 接受TCP连接并返回(conn, address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址 |
客户端Socket函数 | |
s.connect(address) | 连接到address的套接字。一般address的格式为元组(hostname, port),如果连接出错,返回socket.error错误 |
s.connect_ex(address) | 功能与s.connect相同,但成功返回0,失败返回errno的值 |
公共Socket函数 | |
s.recv(bufsize[,flag]) | 接受TCP套接字的数据。以字符串形式返回,bufsize指定要接收的最大数据量,flag提供有关消息的其他信息,通常可以忽略 |
s.send(string[,flag]) | 发送TCP数据。将string中的数据发送到连接的套接字。返回值是要发送的字节数量,可能小于string的字节大小 |
s.sendall(string[,flag]) | 完整发送TCP数据。将string中的数据发送到连接的套接字, 但在返回前会尝试发送所有数据,成功返回None,失败则抛出异常 |
s.recvfrom(bufsize[,flag]) | 接受UDP套接字的数据。与s.recv()类似,但返回值是(data, address),data是包含接收数据的字符串,address是发送数据的套接字地址 |
s.sendto(string[,flag], address) | 发送UDP数据,将数据发送到套接字。address格式为(ipaddr, port)的元组,指定远程地址。返回发送的字节数 |
s.close() | 关闭套接字 |
s.getpeername() | 返回连接套接字的远程地址。通常是元组(ipaddr, port) |
s.getsockname() | 返回套接字自己的地址。通常是元组(ipaddr, port) |
s.setsockopt(level, optname, value) | 设置给定套接字选项的值 |
s.getsockopt(level, optname[, buflen]) | 返回套接字选项的值 |
s.settimeout(timeout) | 设置套接字操作的超时期,timeout是一个浮点数,单位是秒,为None时表示没有超时期。一般应在刚创建套接字时设置,因为可能会用于连接操作。 |
s.setblocking(flag) | 如果flag非0,则将套接字设为非阻塞模式,否则设为阻塞模式(默认)。非阻塞模式下,send无法发送数据,或recv无法接收数据,将引起socket.error异常 |
TCP是一种面向连接的通信方式,主动发起的连接叫客户端,被动响应连接的叫服务端
服务端创建和运行TCP连接需要以下步骤:
#coding:utf-8
import socket
import threading
import time
def dealClient(sock, addr):
#第四步:接收传来的数据,并发送给对方数据
print('Accept new connection from %s:%s...' % addr)
sock.send(b'Hello,I am server!')
while True:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'exit':
break
print('-->>%s!' % data.decode('utf-8'))
sock.send(('Loop_Msg: %s!' % data.decode('utf-8')).encode('utf-8'))
#第五步:关闭套接字
sock.close()
print('Connection from %s:%s closed.' % addr)
if __name__=="__main__":
#第一步:创建一个基于IPv4和TCP协议的Socket
# 套接字绑定的IP(127.0.0.1为本机ip)与端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 9999))
#第二步:监听连接
s.listen(5)
print('Waiting for connection...')
while True:
# 第三步:接受一个新连接:
sock, addr = s.accept()
# 创建新线程来处理TCP连接:
t = threading.Thread(target=dealClient, args=(sock, addr))
t.start()
# 输出:
# Waiting for connection...
# Accept new connection from 127.0.0.1:37092...
# -->>Hello,I am a client!
# Connection from 127.0.0.1:37092 closed.
客户端创建和运行TCP连接需要以下步骤:
#coding:utf-8
import socket
#初始化Socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#连接目标的ip和端口
s.connect(('127.0.0.1', 9999))
# 接收消息
print('-->>'+s.recv(1024).decode('utf-8'))
# 发送消息
s.send(b'Hello,I am a client')
print('-->>'+s.recv(1024).decode('utf-8'))
s.send(b'exit')
#关闭套接字
s.close()
# 输出:
# -->>Hello,I am server!
# -->>Loop_Msg: Hello,I am a client!
服务器端输出:
客户端输出
TCP是面向连接的协议,需要建立连接,以流的形式发送数据
UDP无连接的协议,但发送数据后无法确保数据能够到达目的端
UDP具有不可靠性,但速度比TCP快得多
服务端创建和运行UDP需要以下步骤:
#coding:utf-8
import socket
#创建Socket,绑定指定的ip和端口
#SOCK_DGRAM指定了这个Socket的类型是UDP。绑定端口和TCP一样。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('127.0.0.1', 9999))
print('Bind UDP on 9999...')
while True:
# 直接发送数据和接收数据
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' % addr)
s.sendto(b'Hello, %s!' % data, addr)
# 输出:
# Bind UDP on 9999...
# Received from 127.0.0.1:59835.
# Received from 127.0.0.1:59835.
客户端创建和运行UDP需要以下步骤:
#coding:utf-8
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Hello', b'World']:
# 发送数据:
s.sendto(data, ('127.0.0.1', 9999))
# 接收数据:
print(s.recv(1024).decode('utf-8'))
s.close()
# 输出:
# Hello, Hello!
# Hello, World!
服务器端输出:
客户端输出: