Python网络编程Socket的介绍
socket 的原意是“插座”,在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。网络套接字是计算机网络连接的端点。今天,大多数计算机之间的通信是基于Internet协议的;因此,大多数网络套接字都是Internet套接字。更准确地说,套接字是一个句柄(抽象引用),本地程序可以将其传递给网络应用程序编程接口(API)来使用连接,例如“在此套接字上发送此数据”。
例如,要通过TCP将“Hello, world!”发送到地址为1.2.3.4的主机的80端口,可以获得一个套接字,将其连接到远程主机,发送字符串,然后关闭套接字。
Python中网络编程Socket的使用
如何更好的理解socket网络编程,通过一些例子,你可能会知道更多。首先跟着鳄鱼君创建一个服务器端(server.py)和一个创建客户端(client.py),我们先来写一个简单的发送,接收试试。
server.py服务器端
import socket
#服务器端
server=socket.socket() #这里不写的话,默认 family = AF_INET,type = SOCK_STREAM
server.bind(('localhost',8899)) #绑定要监听的端口
server.listen() #监听
print('我要开始等电话了')
conn,addr=server.accept() #等电话打进来,这里会返回两个值,第一个连接的标记,第二个是对方的地址
print(conn,addr) #conn就是客户端连接过来,然后在服务器端为其生成的一个连接实例
data=conn.recv(1024) #接收客户端发过来的消息
print('recv:',data)
conn.send(data.upper()) #转换为大写返回给客户端
server.close()
通过上面服务器端的代码,我们可以大致归结出TCP通信的基本步骤:服务器首先创建一个socket对象—>服务器端socket将自己绑定到指定端口和IP地址—>服务器端socket调用listen()监听网络—>程序可以采用循环,不断调用socket的accept()方法来接受客户端的连接。
client.py客户端
import socket
#客户端
client=socket.socket() #声明socket类型,同时生成socket连接对象
client.connect(('localhost',8899))
client.send(b'Hello World') #Python3里面必须发送字节类型
data=client.recv(1024) #接收服务器端返回的消息
print('recv:',data)
client.close()
那么TCP通信客户端编程按照上面的代码,可以归纳出:客户端先创建一个socket对象—->客户端socket()调用connect()方法远程连接服务器。
上面的代码就实现客户端发送,服务器端接收的功能,很显然我们只是发送了一个字节类型的Hello World,上面也提到循环发送,我一个客户端可以发送多次
server.py
import socket
server=socket.socket()
server.bind(('localhost',9999))
server.listen()
print('我要开始等电话了')
while True: #最外层的循环就是可以接受多个电话,一个关掉就换另一个客户端
conn,addr=server.accept() #在Windows上不起作用
print('电话来了',conn)
while True:
data=conn.recv(1024)
print('recv:',data)
if len(data)==0: #在Windows中没有用,断开客户端和服务器端就会报错
print('客户端已断开连接')
break
conn.send(data)
server.close()
client.py
import socket
client=socket.socket()
client.connect(('localhost',9999))
while True:
input_content=input('请输入需要发送的消息:').strip()
if len(input_content)==0:continue #为空就接着输入
client.send(input_content.encode())
data=client.recv(1024)
print('recv:',data.decode())
client.close()
上面的代码在Linux中完全没有问题,我们可以打开多个客户端连接服务器端,同时跟服务器端通信的只有一个,其他的都是在阻塞着,只要当前客户端已断开,那么服务器端就会接受下一个客户端,但是在pycharm(Windows)中使用是有问题的,在server.py中,我们问什么要判断data的长度的呢?因为如果客户端如果断开的话,服务器端收到的就是空,那么在Linux上就会出现循环接受空值,在Windows上,只要客户端已断开就会报错,这里自己去尝试,看百遍不如自己敲一遍。
上面的服务器端,我们在外层套一个while循环,就是为了接受多个客户端,就是指代多个用户打电话,经过测试,不管你打开多少个客户端连接服务器,只要一个断开,就会报ConnectionResetError的错误,呢我们可以捕获一下异常,这样就可以实现多个客户端通信了:
import socket
server = socket.socket()
server.bind(('localhost', 9999))
server.listen()
print('我要开始等电话了')
while True: # 最外层的循环就是可以接受多个电话,一个关掉就换另一个客户端
conn, addr = server.accept()
print('电话来了', conn)
while True:
try:
data = conn.recv(1024)
print('recv:', data)
conn.send(data)
except ConnectionResetError as e:
print('当前客户端已经断开连接,准备接听下一个用户')
break
server.close()
Socket常用函数方法 参数介绍
最后我们来对上面的代码做一个总结,可以看到每次都会创建下面的代码,这通常是创建一个socket对象:
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
我们通常可以这样创建socket对象:socket.socket(),因为默认的参数已经够我们使用了,但是不妨碍我们了解其他的参数:
Socket family(地址簇)
socket.AF_UNIX unix #本机进程间通信
socket.AF_INET #IPV4
socket.AF_INET6 #IPV6
这些常量表示地址(和协议)族,用于socket()的第一个参数。如果没有定义AF_UNIX常量,则不支持此协议。根据系统的不同,可以使用更多的常量
Socket type
socket.SOCK_STREAM #for tcp
socket.SOCK_DGRAM #for udp
socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
这些常量表示套接字类型,用于套接字()的第二个参数。根据系统的不同,可以使用更多的常量。(只有SOCK_STREAM和SOCK_DGRAM通常有用。)前面介绍的那么多内容就是便于我们理解socket的参数。
使用给定的地址族、套接字类型和协议号创建一个新的套接字。地址家族应该是AF_INET(默认)、AF_INET6、AF_UNIX、AF_CAN或AF_RDS。套接字类型应该是beSOCK_STREAM(默认)、SOCK_DGRAM、SOCK_RAW或其他SOCK_常量之一。协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应该是CAN_RAW或CAN_BCM之一。如果指定了fileno,则忽略其他参数,从而导致具有指定文件描述符的套接字返回。与socket.fromfd()不同,fileno将返回相同的socket,而不是一个副本。这可能有助于使用socket.close()关闭分离的套接字。
socket.socketpair([family[, type[, proto]]])
使用给定的地址族、套接字类型和协议号构建一对连接的套接字对象。地址族、套接字类型和协议编号与上面的套接字()函数相同。如果在平台上定义,则默认系列是AF_UNIX;否则,默认是AF_INET。
socket.create_connection(address[, timeout[, source_address]])
连接到监听Internet地址的TCP服务(一个2元组(主机、端口)),并返回套接字对象。这是一个比socket.connect()更高级的函数:如果host是一个非数字的主机名,它会尝试为AF_INET和AF_INET6解析它,然后依次尝试连接到所有可能的地址,直到连接成功。这使得编写兼容IPv4和IPv6的客户端变得很容易。
传递可选的超时参数将在尝试连接之前设置套接字实例的超时。如果没有提供超时,则使用getdefaulttimeout()返回的全局缺省超时设置。
如果提供,source_address必须是一个2元组(主机、端口),以便套接字在连接之前绑定到它的源地址。如果主机或端口分别为“或0”,则将使用OS默认行为。
socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0) #获取要连接的对端主机地址
Socket 对象(内建)方法
函数
描述
服务器端套接字
bind()
绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址
listen()
开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
accept()
被动接受TCP客户端连接,(阻塞式)等待连接的到来
客户端套接字
connect()
主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
connect_ex()
connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
recv()
接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
send()
发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
sendall()
完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
recvfrom()
接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
sendto()
发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
close()
关闭套接字
getpeername()
返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
getsockname()
返回套接字自己的地址。通常是一个元组(ipaddr,port)
setsockopt(level,optname,value)
设置给定套接字选项的值。
getsockopt(level,optname[.buflen])
返回套接字选项的值。
settimeout(timeout)
设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
gettimeout()
返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
fileno()
返回套接字的文件描述符。
setblocking(flag)
如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
makefile()
创建一个与该套接字相关连的文件
未经允许不得转载:作者:鳄鱼君,
转载或复制请以 超链接形式 并注明出处 鳄鱼君。