Python3 --- Socket

一、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区别

  • TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
  • TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保   证可靠交付
  • TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
  • 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
  • TCP首部开销20字节;UDP的首部开销小,只有8个字节
  • TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道


五、Python中的Socket

Python 提供了两个级别访问的网络服务。

  • 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法。
  • 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。


六、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对象方法

7.1、服务器端套接字

函数 描述
s.bind() 绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。
s.listen() 开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
s.accept() 被动接受TCP客户端连接,(阻塞式)等待连接的到来

7.2、客户端套接字

函数 描述
s.connect() 主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

7.3、公共用途的套接字函数

函数 描述
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为指定的地址族:

  • socket.AF_UNIX :只能够用于单一的Unix系统进程间通信
  • socket.AF_INET :服务器之间的网络通信(ipv4协议的TCP和UDP)ipv4,默认为这个
  • socket.AF_INET6 :服务器之间的网络通信ipv6

type为指定的套接字类型:

  • socket.SOCK_STREAM :面向连接的TCP,默认为这个
  • socket.SOCK_DGRAM :面向非连接的UDP

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 = '<broadcast>'
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()


猜你喜欢

转载自blog.csdn.net/ka_ka314/article/details/80537872