第五十三节,socket模块介绍,socket单线程通讯
socket单线程通讯,只能单线程通讯,不能并发
socket是基于(TCP、UDP、IP)的通讯、也叫做套接字
通讯过程由服务端的socket处理信息发送,由客户端的socket处理信息接收。
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
socket和file的区别:
file模块是针对某个指定文件进行【打开】【读写】【关闭】
socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】
socket通讯过程由服务端的socket处理信息发送,由客户端的socket处理信息接收。
举例:
模拟一个服务端的socket WEB服务应用
#!/usr/bin/env python
# -*- coding:utf8 -*-
import socket def handle_request(client):
buf = client.recv(1024)
client.sendall(bytes("HTTP/1.1 200 OK\r\n\r\n", encoding='utf-8'))
client.sendall(bytes("Hello, World", encoding='utf-8')) def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost',8080))
sock.listen(5) while True:
connection, address = sock.accept()
handle_request(connection)
connection.close() if __name__ == '__main__':
main()
启动服务端的socket后,我们利用浏览器的客户端socket来访问这个 WEB服务应用 (当然客户端的socket也可以自己写的)
打开浏览器输入 http://127.0.0.1:8080/ 就可以接收到服务端socket输出的 Hello, World
socket通讯有基于TCP和UDP两个通讯的,TCP是需要客户端和服务端相互连接后进行通讯,UDP是不需要相互连接直接由单方面发起的,使用最多的还是TCP
socket通讯过程
socket通讯由服务端和客户端双方完成通讯,服务端启动着等待客户端来连接,客户端从服务端的IP和指定端口进行连接通讯
创建服务端
socket.socket()创建socket对象,有三个可选参数
socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)
参数一:地址簇
socket.AF_INET IPv4(默认)
socket.AF_INET6 IPv6
socket.AF_UNIX 只能够用于单一的Unix系统进程间通信
参数二:类型
socket.SOCK_STREAM 流式socket , for TCP (默认)
socket.SOCK_DGRAM 数据报式socket , for UDP
socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
socket.SOCK_SEQPACKET 可靠的连续数据包服务
参数三:协议
0 (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议
使用方法:对象变量 = socket.socket()
格式:a = socket.socket()
bind()在服务端设置服务端ip和端口
使用方法:对象变量.bind(元祖类型的服务端IP和端口)
格式:a.bind(('127.0.0.1',9999))
listen()监听IP和端口,设置一个参数,表示最多连接排队数量
使用方法:对象变量.listen(排队数)
格式:a.listen(5)
accept()等待接收客户端的请求,一旦有客户端请求连接,就会返回两个值,一个是连接对象,一个是客户端的地址信息,所以需要两个变量来接收
注意:accept()是阻塞的,意思就是程序执行带这里就是等待状态,在没有客户端请求连接的情况下,下面的代码将不会执行,只有客户端请求连接时下面的代码才会被执行
accept()被客户端连接一次后就会被中断,可以写个while循环让它永远等待客户端连接
使用方法:定义连接变量,定义客户端地址信息变量 = 对象变量.accept()
格式:b, c = a.accept()
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建服务端"""
import socket #导入模块
a = socket.socket() #创建socket对象
a.bind(('127.0.0.1', 9999,)) #绑定服务端ip和端口
a.listen(5) #监听IP和端口,设置一个参数,表示最多连接排队数量
while True: #accept()被客户端连接一次后就会被中断,写个while循环让它永远等待客户端连接
b, c = a.accept() #等待接收客户端的请求,一旦有客户端请求连接,就会返回两个值,一个是连接,一个是客户端的地址信息,所以需要两个变量来接收
print(b, c) #打印出客户端连接的,连接,和客服端地址信息
根据以上就创建了一个等待客户端连接的socket服务端
创建客户端
connect()连接服务端,在客户端绑定服务端IP和端口
使用方法:对象变量.socket(元祖类型的服务端IP和端口)
格式:z.connect(('127.0.0.1', 9999,))
close()在客户端关闭连接
使用方法:对象变量.close()
格式:z.close()
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建客户端"""
import socket #导入模块
z = socket.socket() #创建socket对象
z.connect(('127.0.0.1', 9999,))#连接服务端,在客户端绑定服务端IP和端口
z.close() #在客户端关闭连接
客户端每次连接服务端后,服务端的accept()就能获取到来自客户端的一个连接对象,和客户端地址信息,两个元素
客户端执行代码
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建客户端"""
import socket #导入模块
z = socket.socket() #创建socket对象
z.connect(('127.0.0.1', 9999,))#连接服务端,在客户端绑定服务端IP和端口
z.close() #在客户端关闭连接
等待接收客户端连接的服务端代码
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建服务端"""
import socket #导入模块
a = socket.socket() #创建socket对象
a.bind(('127.0.0.1', 9999,)) #绑定服务端ip和端口
a.listen(5) #监听IP和端口,设置一个参数,表示最多连接排队数量
while True: #accept()被客户端连接一次后就会被中断,写个while循环让它永远等待客户端连接
b, c = a.accept() #等待接收客户端的请求,一旦有客户端请求连接,就会返回两个值,一个是连接,一个是客户端的地址信息,所以需要两个变量来接收
print(b, c) #打印出客户端连接的,连接,和客服端地址信息 #显示客户端的连接信息
# <socket.socket fd=264, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 58272)> ('127.0.0.1', 58272)
客户端访问服务端流程图
sendall()【推荐】服务端向客户端发信息,或者客户端向服务端发信息
服务端:通过accept()接收到的客户端对象信息变量,客户端对象信息变量.sendall(字节方式要发送的字符串)
客户端:通过客户端的socket对象.sendall(字节方式要发送的字符串)
格式:b.sendall(bytes("欢迎你",encoding='utf-8'))
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建服务端"""
import socket #导入模块
a = socket.socket() #创建socket对象
a.bind(('127.0.0.1', 9999,)) #绑定服务端ip和端口
a.listen(5) #监听IP和端口,设置一个参数,表示最多连接排队数量
while True: #accept()被客户端连接一次后就会被中断,写个while循环让它永远等待客户端连接
b, c = a.accept() #等待接收客户端的请求,一旦有客户端请求连接,就会返回两个值,一个是连接,一个是客户端的地址信息,所以需要两个变量来接收
b.sendall(bytes("你好欢迎你",encoding='utf-8')) #根据accept()接收到客户端连接对象信息,向客户端发送信息
recv()【推荐】服务端接收客户端发来的信息,或者客户端接收服务端发来的信息
注意:recv()也是阻塞的,也就是说等待接收信息,如果recv()没接收到信息,下面的代码就不会执行,只有接收到信息后下面的代码才会被执行
服务端:通过accept()接收到的客户端对象信息变量,客户端对象信息变量.recv()
客户端:通过客户端的socket对象.recv()
格式:f = z.recv(1024)
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建客户端"""
import socket #导入模块
z = socket.socket() #创建socket对象
z.connect(('127.0.0.1', 9999,))#连接服务端,在客户端绑定服务端IP和端口
f = z.recv(1024) #客户端接收服务端sendall()发来的信息,1024表示最大接收1024字节
f2 = str(f, encoding='utf-8') #将接收到的服务端字节信息转换成字符串
print(f2) #打印出服务端发来的信息
z.close() #在客户端关闭连接
# 输出
# 你好欢迎你
客户端连接服务端,服务端向客户端发送一条信息原理图
客户端与服务端进行交互信息
服务端代码
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建服务端"""
import socket #导入模块
a = socket.socket() #创建socket对象
a.bind(('127.0.0.1', 9999,)) #绑定服务端ip和端口
a.listen(5) #监听IP和端口,设置一个参数,表示最多连接排队数量
while True: #accept()被客户端连接一次后就会被中断,写个while循环让它永远等待客户端连接
b, c = a.accept() #等待接收客户端的请求,一旦有客户端请求连接,就会返回两个值,一个是连接,一个是客户端的地址信息,所以需要两个变量来接收
b.sendall(bytes("你好欢迎你",encoding='utf-8')) #根据accept()接收到客户端连接对象信息,向客户端发送信息
while True: #当客户端连接成功后,进入循环,保持与客户端的通讯
j = b.recv(1024)#接收客户端发来的信息
j2 = str(j, encoding='utf-8') #将接收到的客户端信息转换成字符串
print(j2)
if j2 == "q": #判断客户端输入q,表示不再与服务端通讯,跳出循环,不在保持客户端的通讯
break
b.sendall(bytes(j2+"好",encoding='utf-8')) #将接收到客户端的信息加上一个好字,在发送给客户端
客户端代码
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建客户端"""
import socket #导入模块
z = socket.socket() #创建socket对象
z.connect(('127.0.0.1', 9999,))#连接服务端,在客户端绑定服务端IP和端口
f = z.recv(1024) #客户端接收服务端sendall()发来的信息,1024表示最大接收1024字节
f2 = str(f, encoding='utf-8') #将接收到的服务端字节信息转换成字符串
print(f2) #打印出服务端发来的信息
while True: #当连接服务端成功后进入循环,保持与服务端的通讯
a = input("请输入信息")
z.sendall(bytes(a,encoding='utf-8')) #向服务端发送信息
if a == "q": #判断客户端输入 q表示,不再以服务端通讯跳出循环
break
js = z.recv(1024) #接收服务端发来的信息
js2 = str(js, encoding='utf-8') #将服务端发来的信息转换成字符串
print(js2)
z.close() #在客户端关闭连接
客户端与服务端进行交互信息原理图
UDP通讯方式
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""UDP通讯"""
#服务端
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
sk.bind(ip_port)
while True:
data = sk.recv(1024)
print(data)
#客户端
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
while True:
inp = input('数据:').strip()
if inp == 'exit':
break
sk.sendto(inp,ip_port)
sk.close()
setblocking()设置accept()和recv()是否阻塞,参数为1表示阻塞【默认】,参数为0表示不阻塞,那么accept和recv时一旦无数据,则报错
使用方法:对象变量.setblocking(0)
格式:a.setblocking(0)
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建服务端"""
import socket #导入模块
a = socket.socket() #创建socket对象
a.bind(('127.0.0.1', 9999,)) #绑定服务端ip和端口
a.listen(5) #监听IP和端口,设置一个参数,表示最多连接排队数量
a.setblocking(0) #设置为不阻塞
b, c = a.accept() #等待接收客户端的请求,一旦有客户端请求连接,就会返回两个值,一个是连接,一个是客户端的地址信息,所以需要两个变量来接收
connect_ex()客户端连接服务端,与connect()相同,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061
使用方法:对象变量.connect_ex(元祖类型IP和端口)
格式:b = z.connect_ex(('127.0.0.1', 9999,))
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建客户端"""
import socket #导入模块
z = socket.socket() #创建socket对象
b = z.connect_ex(('127.0.0.1', 9999,))#连接服务端,在客户端绑定服务端IP和端口
print(b)
recvfrom()接收数据信息,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建客户端"""
import socket #导入模块
z = socket.socket() #创建socket对象
b = z.connect(('127.0.0.1', 9999,))#连接服务端,在客户端绑定服务端IP和端口
f,s = z.recvfrom(1024) #客户端接收服务端sendall()发来的信息,1024表示最大接收1024字节
f = str(f, encoding='utf-8') #将接收到的服务端字节信息转换成字符串
print(f,s) #打印出服务端发来的信息
send()发数据,sendall底层也是调用的它,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
sendto(string[,flag],address) 发送数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。
# 服务端
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
sk.bind(ip_port) while True:
data,(host,port) = sk.recvfrom(1024)
print(data,host,port)
sk.sendto(bytes('ok', encoding='utf-8'), (host,port)) #客户端
import socket
ip_port = ('127.0.0.1',9999) sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
while True:
inp = input('数据:').strip()
if inp == 'exit':
break
sk.sendto(bytes(inp, encoding='utf-8'),ip_port)
data = sk.recvfrom(1024)
print(data) sk.close()
settimeout(timeout)设置连接超时时间,设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )
getpeername()客户端获取服务端信息或者服务端获取客户端信息如IP,返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)
getsockname()客户端获取服务端信息或者服务端获取客户端信息如IP,返回套接字自己的地址。通常是一个元组(ipaddr,port)
fileno()通讯信号检测,套接字的文件描述符
UDP通讯举例
# 服务端
import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
sk.bind(ip_port) while True:
data,(host,port) = sk.recvfrom(1024)
print(data,host,port)
sk.sendto(bytes('ok', encoding='utf-8'), (host,port)) #客户端
import socket
ip_port = ('127.0.0.1',9999) sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
while True:
inp = input('数据:').strip()
if inp == 'exit':
break
sk.sendto(bytes(inp, encoding='utf-8'),ip_port)
data = sk.recvfrom(1024)
print(data) sk.close()
基于socket实现文件上传
列如:客户端向服务端上传文件
1.客户端连接上服务端
2.客户端检测要传给服务端的文件大小,并且通讯告诉服务端,
3.服务端接收客户端发来的文件大小信息,接收文件大小信息后通讯客户端告诉客户端文件大小信息收到(解决粘包问题)
4.客户端接收服务端是否收到文件大小的信息,没收到服务端信息阻塞,收到继续下面执行
5.客户端已字节方式打开要传输的文件,for循环打开的文件,每循环一行向服务端传输一行的数据
6.服务端设置一个文件接收了多少的判断变量默认等于0
7.服务端打开文件等待写入接收到的客户端文件数据
8.服务端while 循环接收客户端发的文件数据并且写入文件,每循环写入一次检查一下服务端,当次接收到客户端多少字节文件数据,将当次接收到的文件大小字节累计加到,文件接收判断变量里
9.if判断,当文件接收判断变量等于文件总大小时,说明文件已经接收完成,跳出循环,关闭打开的文件
服务端代码
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建服务端"""
import socket #导入模块
a = socket.socket() #创建socket对象
a.bind(('127.0.0.1', 9999,)) #绑定服务端ip和端口
a.listen(5) #监听IP和端口,设置一个参数,表示最多连接排队数量
while True: #accept()被客户端连接一次后就会被中断,写个while循环让它永远等待客户端连接
b, c = a.accept() #等待接收客户端的请求,一旦有客户端请求连接,就会返回两个值,一个是连接,一个是客户端的地址信息,所以需要两个变量来接收
# 接收客户端发来的文件大小 dx = b.recv(1024) #接受客户端发来的文件大小
dx2 = str(dx, encoding="utf-8") #将文件大小转换成字符串
dx3 = int(dx2) #将文件大小字符串转换成数字 b.sendall(bytes("文件大小接收完成", encoding="utf-8")) #接收到文件大小后,告诉客户端
#设置接收判断 pd = 0 #设置一个接受了文件的判断大小
#服务端打开文件接收 f = open("456.png", "wb")
while True: #循环写入接收到客户端发的文件内容
if pd == dx3: #判断,判断大小等于总大小的时候跳出循环
break
j = b.recv(1024) #接收到的文件内容
f.write(j) #将接收到的内容写入文件
pd += len(j) #每循环写入一次就将写入的大小加给判断
f.close()#关闭打开的文件
客户端代码
#!/usr/bin/env python
# -*- coding:utf8 -*-
"""创建客户端"""
import os
import socket #导入模块
z = socket.socket() #创建socket对象
b = z.connect(('127.0.0.1', 9999,))#连接服务端,在客户端绑定服务端IP和端口
#检查发送文件大小 dx = os.stat("32.png").st_size #检测要发送文件的大小
z.sendall(bytes(str(dx), encoding="utf-8")) #将文件大小发送给服务端 bao = z.recv(1024) #接收服务端文件大小,接收成功后的返回信息
bao2 = str(bao, encoding="utf-8")
print(bao2)
#打开文件循环发送文件 with open("32.png", "rb") as f:
for i in f: #循环文件
z.sendall(i) #将每次循环到文件内容发送给服务端
z.close() #关闭通讯连接
重点:注意传输数据时的粘包问题
粘包就是一端发了两次信息或数据,第一次发的有可能计算机缓冲区还没发出去,第二次的信息就发到了缓冲区,这样两次的信息粘在一起发出去了,为了避免粘包问题,在第一次发送后接收一下另外一端是否接收到第一次发的信息,如果接收到才发第二次,如果没接收到就不发用recv()阻塞,注意上面的列子
注意:socket模块不能处理并发,也就是不能实现多线路通讯,只能一条线路,第二个进来第一个占线着,要第一个退出后第二个才能进来
第五十三节,socket模块介绍,socket单线程通讯的更多相关文章
- 第三百五十三节,Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy的暂停与重启
第三百五十三节,Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy的暂停与重启 scrapy的每一个爬虫,暂停时可以记录暂停状态以及爬取了哪些url,重启时可以从暂停状态开始爬取过的UR ...
- socket编程--socket模块介绍
socket也称作'套接字,用于描述IP地址和端口,是一个通信的终点. socket起源于Unix,而Unix/Linux基本哲学之一就是"一切皆文件",对于文件用[打开][读写] ...
- python学习第五十天shutil模块的用法
什么shutil模块,就是对高级的文件,文件夹,压缩包进行处理的模块,下面简单讲述其用法. 文件和文件夹的操作 拷贝文件内容 import shutil shutil.copyfileobj(open ...
- 五十 常用第三方模块 PIL
PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了.PIL功能非常强大,但API却非常简单易用. 由于PIL仅支持到Python 2.7,加上年久失修 ...
- 第一百五十三节,封装库--JavaScript,表单验证--备注字数验证
封装库--JavaScript,表单验证--备注字数验证 效果图 html <div id="reg"> <h2 class="tuo"> ...
- Python套接字编程(1)——socket模块与套接字编程
在Python网络编程系列,我们主要学习以下内容: 1. socket模块与基本套接字编程 2. socket模块的其他网络编程功能 3. SocketServer模块与简单并发服务器 4. 异步编程 ...
- Python 进阶 之 socket模块
Python Socket 编程详细介绍 转载地址:https://gist.github.com/kevinkindom/108ffd675cb9253f8f71?signup=true Pytho ...
- day28 网络协议OSI 及socket模块
反射都是操作对象中的__dict__的内容 class Student: def study(self): print("正在学习....") stu = Student() if ...
- socket()函数介绍
socket()函数介绍 socket函数介绍 函数原型 domain type protocol errno 示例 函数原型 socket()函数的原型如下,这个函数建立一个协议族为domain.协 ...
随机推荐
- Unity在安卓的一些路径
APK安装之后找不到路径 公司的测试机(安卓)基本都是不带SD卡的. APK在安卓手机上安装之后,使用手机助手类的软件打开文件管理,打开 内置SDK卡/Android/data/ 在这个目录下却发现 ...
- Docker集群实验环境布署--swarm【3 注册服务监控与自动发现组件--consul】
参考官网集群配置方式 https://hub.docker.com/r/progrium/consul/ 集群中需要manager与node能通信consul的发现服务,不然,管理节点选举不了,无 ...
- 数据结构之Heap (Java)
Heap简介 Heap译为“堆”,是一种特殊的树形数据结构,它满足所有堆的特性:父节点的值大于等于子节点的值(max heap),或者小于等于子节点的值(min heap).对于max heap 根节 ...
- Mutex的使用方法以及封装的AutoLock介绍(转载)
Mutex-互斥类 互斥类-MutexMutex是互斥类,用于多线程访问同一个资源的时候,保证一次只有一个线程能访问该资源.在<Windows核心编程>①一书中,对于这种互斥访问有一个很形 ...
- js学习笔记1
就是调用对象是一个Array,对Array类型增加了一个原型写法的函数,一般写一些扩展时经常用.比如判断一个元素是否在数组中之类的 Array.prototype.inArray=function(v ...
- iOS ARC与MRC混编的一些解决方法
1. ARC & MRC 混合开发 在项目开发中,遇到使用MRC开发的第三方库怎么办? 例如:ASI 1> 尝试使用Xcode的转换工具(失败率比较高) 2> 在编译选项中,为MR ...
- 多表查询 INNER JOIN ON WHERE
SELECT *FROM STUDENT_INFO siINNER JOIN CLASS_INFO ci on si.CLASS_INFO_ID = ci.ID INNER JOIN TEACHER_ ...
- Hibernate——脏检查和缓存清理机制
Session到底是如何进行脏检查的呢? 当一个Customer对象被加入到Session缓存中时,Session会为Customer对象的值类型的属性复制一份快照.当Session清理缓存时,会先进 ...
- String与StringBuild、StringBuffer的区别
String与StringBuild.StringBuffer的区别相信困扰了好多新入门的JAVA程序员,而这也是笔试和面试的一道常见题型,如何全面的回答该问题,变得尤为重要. 首先我们需要清楚一点, ...
- R语言数据结构
5. 数据结构 5.1 数据结构简介 (1)向量 一个向量的所有元素必须有相同的类型(模式) (2)列表 列表可以非同质的 列表可按位置索引:lst[[2]] 抽取子列表:lst[c(2,5)] 列表 ...