python关于Socket编程方法
1、创建
socket.socket(family=AF_INET, type=SOCK_STREAM, protocal=0)
-
socket families(地址簇):
AF_UNIX :unix本机之间进行通信
AF_INET :使用IPv4
AF_INET6 :使用IPv6 -
socket types:
SOCK_STREAM :TCP套接字类型
SOCK_DGRAM :UDP套接字类型
SOCK_RAW :原始套接字类型,这个套接字比较强大,创建这种套接字可以监听网卡上的所有数据帧
SOCK_RDM :是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
注:这些常量都是套接字类型,应用于
socket()
函数中的第二个参数中.根据系统的不同,可能有更多的常数。(只有SOCK_STREAM
和SOCK_DGRAM
似乎通常很有用。)
-
protocal:
协议号通常为零,可以省略,或者在地址族为AF_CAN
的情况下,协议应为CAN_RAW
或CAN_BCM
。如果指定fileno,则忽略其他参数,从而导致具有指定文件描述器的套接字返回。
与socket.fromfd()
不同,fileno将返回相同的套接字,而不是重复。这可能有助于使用socket.close()
关闭分离的套接字,一般情况下后两个参数忽略即可。
#创建 TCP/IP 套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#创建 UDP/IP 套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
2. TCP Socket通信
如下图所示,TCP通信的基本步骤如下:
服务端:socket---bind---listen---while(True){---accept----recv---send---}---close
客户端:socket----------------------------------------connect---send---recv-------close
TCP Socket通信
- 创建
socket.socket([family[, type[, proto]]])
family : AF_INET (默认ipv4), AF_INET6(ipv6) or AF_UNIX(Unix系统进程间通信)
type : SOCK_STREAM (TCP), SOCK_DGRAM(UDP) .
protocol : 一般为0或者默认
如果socket创建失败会抛出一个socket.error异常</code>
- 服务器函数
1). bind函数
使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求。如果客户端有连接请求,端口就会接受这个连接。
s.bind(address)
s为socket.socket()返回的套接字对象
address为元组(host,port)
host: ip地址, 为一个字符串
post: 自定义主机号, 为整型
2). listen函数
使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求。如果客户端有连接请求,端口就会接受这个连接。
s.listen(backlog)
s为socket.socket()返回的套接字对象
backlog : 操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了
3). accept函数
接受远程计算机的连接请求,建立起与客户机之间的通信连接。服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求。
s.accept()
s为socket.socket()返回的套接字对象
返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址
- 客户端函数
1). connect函数
s.connect(address)
s为socket.socket()返回的套接字对象
address : 格式为元组(hostname,port),如果连接出错,返回socket.error错误
- 通用函数
1). recv函数
#接收远端主机传来的数据
s.recv(bufsize[,flag])
s为socket.socket()返回的套接字对象
bufsize : 指定要接收的数据大小
flag : 提供有关消息的其他信息,通常可以忽略
返回值为数据以字符串形式
2). send函数
#发送数据给指定的远端主机
s.send(string[,flag])
s为socket.socket()返回的套接字对象
string : 要发送的字符串数据
flag : 提供有关消息的其他信息,通常可以忽略
返回值是要发送的字节数量,该数量可能小于string的字节大小。
s.sendall(string[,flag])
#完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。
返回值 : 成功返回None,失败则抛出异常。
3). close函数
#关闭套接字
s.close()
s为socket.socket()返回的套接字对象
- 带异常处理的客户端服务端TCP连接
在进行网络编程时, 最好使用大量的错误处理, 能够尽量的发现错误, 也能够使代码显得更加严谨。
1). 服务器端代码
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
phone.bind(('127.0.0.1',8080)) #插电话卡
phone.listen(5) #开机,backlog
print('starting....')
while True:
conn,addr = phone.accept() #接电话
print('接到来自%s的电话' %addr[0])
while True:
client_msg=conn.recv(1024) #收消息
if len(client_msg) == 0:break #如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生
print('client msg: %s' %client_msg)
conn.send(client_msg.upper()) #发消息
conn.close()
phone.close()
2). 客户端代码
import socket
ip_port = ('127.0.0.1',8081) #电话卡
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
s.bind(ip_port) #手机插卡
s.listen(5) #手机待机
while True: #新增接收链接循环,可以不停的接电话
conn,addr=s.accept() #手机接电话
print('接到来自%s的电话' %addr[0])
while True: ##新增通信循环,可以不断的通信,收发消息
msg=conn.recv(BUFSIZE) #听消息,听话
if len(msg) == 0:break #如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生
print(msg,type(msg))
conn.send(msg.upper()) #发消息,说话
conn.close() #挂电话
s.close() #手机关机
3. TCP UDP通信
UDP通信流程图如下:
服务端:socket---bind---recvfrom---sendto---close
客户端:socket------------sendto---recvfrom---close
UDP通信
- 创建
socket.socket([family[, type[, proto]]])
family : AF_INET (默认ipv4), AF_INET6(ipv6) or AF_UNIX(Unix系统进程间通信)
type : SOCK_STREAM (TCP), SOCK_DGRAM(UDP) .
protocol : 一般为0或者默认
如果socket创建失败会抛出一个socket.error异常</code>
- 常用方法
1). sendto函数
#发送UDP数据, 将数据发送到套接字
s.sendto(string[,flag],address)
s为socket.socket()返回的套接字对象
address : 指定远程地址, 形式为(ipaddr,port)的元组
flag : 提供有关消息的其他信息,通常可以忽略
返回值 : 发送的字节数。
2). recvfrom函数
#接受UDP套接字的数据, 与recv()类似
s.recvfrom(bufsize[.flag])
返回值 : (data,address)元组, 其中data是包含接收数据的字符串,address是发送数据的套接字地址
bufsize : 指定要接收的数据大小
flag : 提供有关消息的其他信息,通常可以忽略
3). close函数
#关闭套接字
s.close()
s为socket.socket()返回的套接字对象
- 简单的客户端服务器UDP连接
1). 服务器端代码
import socket
ip_port=('127.0.0.1',8081)
udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #买手机
udp_server_sock.bind(ip_port)
while True:
qq_msg,addr=udp_server_sock.recvfrom(1024)
print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))
back_msg=input('回复消息: ').strip()
udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
2). 客户端代码
客户端1
import socket
BUFSIZE=1024
udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
qq_name_dic={
'TOM':('127.0.0.1',8081),
'JACK':('127.0.0.1',8081),
'一棵树':('127.0.0.1',8081),
'武大郎':('127.0.0.1',8081),
}
while True:
qq_name=input('请选择聊天对象: ').strip()
while True:
msg=input('请输入消息,回车发送: ').strip()
if msg == 'quit':break
if not msg or not qq_name or qq_name not in qq_name_dic:continue
udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
udp_client_socket.close()
客户端2
import socket
BUFSIZE=1024
udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
qq_name_dic={
'TOM':('127.0.0.1',8081),
'JACK':('127.0.0.1',8081),
'一棵树':('127.0.0.1',8081),
'武大郎':('127.0.0.1',8081),
}
while True:
qq_name=input('请选择聊天对象: ').strip()
while True:
msg=input('请输入消息,回车发送: ').strip()
if msg == 'quit':break
if not msg or not qq_name or qq_name not in qq_name_dic:continue
udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
udp_client_socket.close()
附
常见的套接字对象方法和属性
名 称 描 述 | 服务器套接字方法 |
---|---|
s.bind() | 将地址(主机名、端口号对)绑定到套接字上 |
s.listen() | 设置并启动 TCP 监听器 |
s.accept() | 被动接受 TCP 客户端连接,一直等待直到连接到达(阻塞) |
客户端套接字方法 | |
s.connect() | 主动发起 TCP 服务器连接 |
s.connect_ex() | connect()的扩展版本,此时会以错误码的形式返回问题,而不是抛出一个异常 |
普通的套接字方法 | |
s.recv() | 接收 TCP 消息 |
s.recv_into() | 接收 TCP 消息到指定的缓冲区 |
s.send() | 发送 TCP 消息 |
s.sendall() | 完整地发送 TCP 消息 |
s.recvfrom() | 接收 UDP 消息 |
s.recvfrom_into() | 接收 UDP 消息到指定的缓冲区 |
s.sendto() | 发送 UDP 消息 |
s.getpeername() | 连接到套接字( TCP)的远程地址 |
s.getsockname() | 当前套接字的地址 |
s.getsockopt() | 返回给定套接字选项的值 |
s.setsockopt() | 设置给定套接字选项的值 |
s.shutdown() | 关闭连接 |
s.close() | 关闭套接字 |
s.detach() | 在未关闭文件描述符的情况下关闭套接字,返回文件描述符 |
s.ioctl() | 控制套接字的模式(仅支持 Windows) |
面向阻塞的套接字方法 | |
s.setblocking() | 设置套接字的阻塞或非阻塞模式 |
s.settimeout() | 设置阻塞套接字操作的超时时间 |
s.gettimeout() | 获取阻塞套接字操作的超时时间 |
面向文件的套接字方法 | |
s.fileno() | 套接字的文件描述符 |
s.makefile() | 创建与套接字关联的文件对象 |
数据属性 | |
s.family | 套接字家族 |
s.type | 套接字类型 |
s.proto | 套接字协议 |