一、Socket简介
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
二、TCP简介
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据。
三、UDP简介
UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,UDP通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可
四、TCP与UDP区别
五、Python中的Socket
Python 提供了两个级别访问的网络服务。
六、Socket模块方法
(1)socket.socket(family = AF_INET,type = SOCK_STREAM,proto = 0,fileno = None):使用给定的地址系列,套接字类型和协议号创建一个新套接字
(2)socket.socketpair([ family [,type [,proto ] ] ] ):使用给定的地址系列,套接字类型和协议编号构建一对连接的套接字对象
(3)socket.create_connection(address [,timeout [,source_address ] ]):连接到侦听Internet 地址(2元组 )的TCP服务,然后返回套接字对象
(4)socket.fromfd(fd,family,type,proto = 0):复制文件描述符fd(由文件对象的fileno()方法返回的整数 ),并从结果中构建一个套接字对象
(5)socket.fromshare(data):从该socket.share() 方法获得的数据实例化一个套接字。假设套接字处于阻塞模式。
(6)socket.SocketType:这是表示套接字对象类型的Python类型对象。这是一样的type(socket(...))
(7)socket.getaddrinfo(host,port,family = 0,type = 0,proto = 0,flags = 0):将主机 / 端口参数转换为5元组序列,其中包含创建连接到该服务的套接字的所有必要参数
(8)socket.getfqdn([ name ]):为名称返回完全限定的域名。如果名称被省略或为空,则被解释为本地主机
(9)socket.gethostbyname(ostname):将主机名转换为IPv4地址格式。IPv4地址以字符串形式返回
(10)socket.gethostbyname_ex(hostname):将主机名转换为IPv4地址格式,扩展接口。返回一个triple ,其中主机名是响应给定ip_address的主要主机名,aliaslist是同一地址的备用主机名(可能为空)列表,ipaddrlist是同一主机上同一接口的IPv4地址列表经常但不总是一个地址)。不支持IPv6名称解析,应该用于IPv4 / v6双栈支持
(11)socket.gethostname():返回包含Python解释器当前正在执行的机器的主机名的字符串
(12)socket.gethostbyaddr(ip_address):返回一个triple ,其中hostname是响应给定ip_address的主要主机名,aliaslist是同一地址的备用主机名(可能为空)列表, ipaddrlist是同一个接口的IPv4 / v6地址列表主机(最有可能只包含一个地址)。要找到完全限定的域名,请使用该功能。支持IPv4和IPv6
(13)socket.getnameinfo(sockaddr,flags):将套接字地址sockaddr翻译成2元组。根据标志的设置,结果可以在主机中包含完全限定的域名或数字地址表示。同样,端口可以包含字符串端口名称或数字端口号。(host, port)
(14)socket.getprotobyname(protocolname):将Internet协议名称(例如,'icmp')转换为适合作为(可选)第三个参数传递给该socket() 函数的常量。这通常只需要以“原始”模式(SOCK_RAW)打开的套接字; 对于正常的套接字模式,如果协议被省略或为零,则自动选择正确的协议
(15)socket.getservbyname(servicename [,protocolname ]):将Internet服务名称和协议名称转换为该服务的端口号。可选的协议名称,如果有,应该是'tcp'或 'udp',否则任何协议将匹配
(16)socket.getservbyport(port [,protocolname ]):将Internet端口号和协议名称转换为该服务的服务名称。可选的协议名称,如果有,应该是'tcp'或 'udp',否则任何协议将匹配
(17)socket.ntohl(x):将32位正整数从网络转换为主机字节顺序。在主机字节顺序与网络字节顺序相同的机器上,这是无操作的; 否则,它会执行一个4字节的交换操作。
(18)socket.ntohs(x):将16位正整数从网络转换为主机字节顺序。在主机字节顺序与网络字节顺序相同的机器上,这是无操作的; 否则,它执行一个2字节的交换操作
(19)socket.htonl(x):将32位正整数从主机转换为网络字节顺序。在主机字节顺序与网络字节顺序相同的机器上,这是无操作的; 否则,它会执行一个4字节的交换操作。
(20)socket.htons(x):将16位正整数从主机转换为网络字节顺序。在主机字节顺序与网络字节顺序相同的机器上,这是无操作的; 否则,它执行一个2字节的交换操作
(21)socket.inet_aton(ip_string):将IPv4地址从点分四字符串格式(例如“123.45.67.89”)转换为32位打包二进制格式,作为长度为4个字符的字节对象。当与使用标准C库的程序进行交谈并且需要类型对象
(22)socket.getdefaulttimeout():返回新套接字对象的默认超时值(秒)(float)。值None表示新的套接字对象没有超时。首次导入套接字模块时,默认为None。
(23)socket.setdefaulttimeout(timeout):为新的套接字对象设置默认的超时值(秒)(float)。首次导入套接字模块时,默认为None。请参阅 settimeout()可能的值和它们各自的含义。
(24)socket.sethostname(name):将机器的主机名称设为名称。OSError如果你没有足够的权利,这将会提高 。
(25)socket.if_nameindex():返回网络接口信息列表(index int,name string)元组。 OSError如果系统调用失败。
(26)socket.if_nametoindex(if_name):返回接口名称对应的网络接口索引号。 OSError如果没有给定名称的接口存在。
(27)socket.if_indextoname(if_index):返回接口索引号对应的网络接口名称。 OSError如果没有给定索引的接口存在。
七、socket.socket对象方法
函数 | 描述 |
---|---|
s.bind() | 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。 |
s.listen() | 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。 |
s.accept() | 被动接受TCP客户端连接,(阻塞式)等待连接的到来 |
函数 | 描述 |
---|---|
s.connect() | 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 |
s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 |
函数 | 描述 |
---|---|
s.recv() | 接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。 |
s.send() | 发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。 |
s.sendall() | 完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 |
s.recvform() | 接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
s.sendto() | 发送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表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect()) |
s.gettimeout() | 返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。 |
s.fileno() | 返回套接字的文件描述符。 |
s.setblocking(flag) | 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。 |
s.makefile() | 创建一个与该套接字相关连的文件 |
八、Socket的创建
import socket # 创建TCP socket tcp_sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 创建UDP socket udp_sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
说明:
family为指定的地址族:
type为指定的套接字类型:
family和type参数是指定了一个协议,我们也可以使用proto第三个参数来直接指定使用的协议。我们也可以使用socket下的函数getprotobyname('tcp'),来代替IPPTOTO_XX变量,如下:
import socket socket.getprotobyname('tcp') socket.IPPROTO_TCP
九、Socket实例
9.1、TCP服务端
import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) HostPort = ('127.0.0.1',8898) s.bind(HostPort) #绑定地址端口 s.listen(5) #监听最多5个连接请求 while True: print('server socket waiting...') c,addr = s.accept() #阻塞等待链接,创建套接字c链接和地址信息addr while True: try: client_date = c.recv(1024) #接收客户端数据 if str(client_date,'utf8') == 'quit': c.close() break except Exception: break c.send(client_date) #发送数据给客户端 print('clientINFO:',str(client_date, 'utf8')) #打印数据,默认接收数据为bytes,需转换成str
9.2、Tcp客户端
import socket hostport = ('127.0.0.1',8898) s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建TCP socket s.connect(hostport) #链接套接字 while True: user_input = input('>>>:').strip() s.send(bytes(user_input,'utf8')) #发送数据到套接字 if not len(user_input):continue if user_input == 'quit': s.close() break server_reply = s.recv(1024) #接收套接字数据 print(str(server_reply, 'utf8')) #打印输出
9.3、UDP服务端
import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udp_server_client.bind(ip_port) while True: msg,addr=udp_server_client.recvfrom(BUFSIZE) print(msg,addr) udp_server_client.sendto(msg.upper(),addr)
9.4、UDP客户端
import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while True: msg=input('>>: ').strip() if not msg:continue udp_server_client.sendto(msg.encode('utf-8'),ip_port) back_msg,addr=udp_server_client.recvfrom(BUFSIZE) print(back_msg.decode('utf-8'),addr)
十、UDP广播
10.1、服务端
from socket import * from time import ctime HOST = '127.0.0.1' PORT = 21567 BUFSIZE = 1024 ADDR = (HOST, PORT) udpSerSock = socket(AF_INET, SOCK_DGRAM) udpSerSock.bind(('', PORT)) print 'wating for message...' while True: data, addr = udpSerSock.recvfrom(BUFSIZE) print('...received ->%s %s' % (addr, data)) udpSerSock.close()
10.2、客户端
from socket import * HOST = '' PORT = 21567 BUFSIZE = 1024 ADDR = (HOST, PORT) udpCliSock = socket(AF_INET, SOCK_DGRAM) udpCliSock.bind(('', 0)) udpCliSock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) while True: data = input('>') if not data: break print "sending -> %s" % data udpCliSock.sendto(data.encode("utf-8"), ADDR) ## data,ADDR = udpCliSock.recvfrom(BUFSIZE) ## if not data: ## break ## print data udpCliSock.close()
十一、echo服务器
#coding=utf-8 from socket import * #1. 创建套接字 udpSocket = socket(AF_INET, SOCK_DGRAM) #2. 绑定本地的相关信息 bindAddr = ('', 7788) # ip地址和端口号,ip一般不用写,表示本机的任何一个ip udpSocket.bind(bindAddr) num = 1 while True: #3. 等待接收对方发送的数据 recvData = udpSocket.recvfrom(1024) # 1024表示本次接收的最大字节数 #4. 将接收到的数据再发送给对方 udpSocket.sendto(recvData[0], recvData[1]) #5. 统计信息 print('已经将接收到的第%d个数据返回给对方,内容为:%s'%(num,recvData[0])) num+=1 #5. 关闭套接字 udpSocket.close()