TCP是流协议,而UDP是数据报协议。换句话说,TCP在客户机和服务器之间建立持续的开放连接,在该连接的生命期内,字节可以通过该连接写出(并且保证顺序正确)。然而,通过 TCP 写出的字节没有内置的结构,所以需要高层协议在被传输的字节流内部分隔数据记录和字段。
UDP是数据报协议,不需要在客户机和服务器之间建立连接,它只是在地址之间传输报文。UDP的一个很好特性在于它的包是自分隔的(self-delimiting),也就是一个数据报都准确地指出它的开始和结束位置。然而,UDP的一个可能的缺点在于,它不保证包将会按顺序到达,甚至根本就不保证。不过,UDP有一个很大的有点就是效率高。
TCP与UDP直接的对比就好似手机通信和邮寄信件通信。TCP犹如手机通信机制,当呼叫者通过手机拨打接受者手机,只有接受者按下接听键,两方才算建立起了连接,且只要没人挂断,连接就一直是存活的,也只有在连接存活情况下两者才能通话。UDP就如有邮局系统,只有有人来寄信,邮局就回帮他寄,但是不会去管收件人是否存在、也不管收件人什么时候能收到信,如果寄件人陆续寄出多封信,收件人收的的信先后顺序是混乱的,如果有的信没有送达,那这封信就此消失在历史的尘埃中。
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。每一个socket都用一个半相关描述{协议、本地地址、本地端口}来表示;一个完整的套接字则用一个相关描述{协议、本地地址、本地端口、远程地址、远程端口}来表示。socket也有一个类似于打开文件的函数调用,该函数返回一个整型的socket描述符,随后的连接建立、数据传输等 *** 作都是通过socket描述符来实现的。以TCP协议中socket建立连接的“三次握手”为例,其过程如下:
如图所示,当客户端通过socket调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用socket的accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。
两个socket通过“网络”交互数据进行数据交互时只负责两件事:建立连接,传递数据;同时socket在收发数据时遵循的原则:有发就有收,收发必相等!
4、socket基本使用
1)socket函数
功能:使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。
格式:socket.socket([family[,type[,proto]]])
参数:
family : AF_INET (默认ipv4),AF_INET6(ipv6) , AF_UNIX(Unix系统进程间通信).
type : SOCK_STREAM (TCP),SOCK_DGRAM(UDP) .
protocol : 一般为0或者默认
备注:如果socket创建失败会抛出一个socket.error异常
2)服务器端函数
a)bind函数
格式:s.bind(address)
功能:将地址address绑定到套接字,地址以元组(host,port)的形式表示。
参数:
address为元组(host,port)
host: ip地址,为一个字符串
post: 自定义主机号,为整型
b)Listen函数
格式:s.Listen(backlog)
功能:使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求。如果客户端有连接请求,端口就会接受这个连接。
参数:backlog : *** 作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了
c)accept函数
格式:s.accept()
功能:接受远程计算机的连接请求,建立起与客户机之间的通信连接。服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求。
返回值:返回一个数组(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址
3)客户端函数
a)connect函数
格式:s.connect(address)
功能:用来请求连接远程服务器
参数:address为远程服务器地址, 格式为元组(hostname,port),如果连接出错,返回socket.error错误
b)connect_ex函数
格式:s.connect_ex(address)
备注:connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
4)通用函数
a)recv函数
格式:s.recv(bufsize[,flag])
功能:接收远端主机传来的数据
参数:
bufsize : 指定要接收的数据大小
flag : 提供有关消息的其他信息,通常可以忽略
返回值:返回值为数据以字符串形式
b)send函数
格式:s.send(string[,flag])
功能:发送数据给指定的远端主机
参数:
string : 要发送的字符串数据
flag : 提供有关消息的其他信息,通常可以忽略
返回值:返回值是要发送的字节数量,该数量可能小于string的字节大小。
c)sendall函数
格式:s.sendall(string[,flag])
功能:内部调用了send函数,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。
参数:同send函数
返回值 : 成功返回None,失败则抛出异常。
d)close函数
格式:s.close()
功能:关闭套接字
e)recvfrom函数
格式:s.recvfrom(bufsize[.flag])
功能:与recv()类似,区别是返回值不同
返回值:返回一个数组(data,address),其中data是包含接收数据的字符串,address是发送数据的套接字地址。
f)sendto函数
格式:s.sendto(string[,flag],address)
功能:将数据发送到套接字
参数:
string : 要发送的字符串数据
flag : 提供有关消息的其他信息,通常可以忽略
address是形式为(ipaddr,port)的元组,指定远程地址
返回值:返回值是要发送的字节数量
备注:该函数主要用于UDP协议。
g)settimeout函数
格式:s.settimeout(timeout)
功能:设置套接字 *** 作的超时期
参数:timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的 *** 作(如 clIEnt 连接最多等待5s )
h)getpeername函数
格式:s.getpeername()
功能:获取连接套接字的远程地址
返回值:返回值通常是元组(ipaddr,port)。
i)getsockname函数
格式:s.getsockname()
功能:获取套接字自己的地址
返回值:通常是一个元组(ipaddr,port)
socket中常用的函数就上面这些了。先用上面这些函数尝试TCP协议下的socket通信。
服务器端代码如下:
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8088))
sk.Listen(5)
print('正在等待linux公社客户端连接……')
conn,addr = sk.accept()
print('linux公社客户端已连接到服务器……')
mes_from_clIEnt = conn.recv(1024).decode('utf-8')
print(mes_from_clIEnt)
mes_to_server = '你好,linux公社www.linuxIDc.com客户端,已收到您的信息!'.encode('utf-8')#发送的数据必须是byte类型
conn.send(mes_to_server)
conn.close()
sk.close()
客户端代码:
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8088))
mes_to_server = '你好,linux公社www.linuxIDc.com服务器!'.encode('utf-8')#发送的数据必须是byte类型
sk.send(mes_to_server)
mes_from_server = sk.recv(1024).decode('utf-8')
print(mes_from_server)
sk.close()
注意:上述两代码块必须放在两不同的文件中,且必须先运行服务器代码,然后在开启客户端。开启服务器后,首先输出“正在等待客户端连接……”,然后进程会阻塞在accept函数中,下面的代码不会被执行,知道有客户端连接过来。开启客户端后,服务器端会先收到客户端发来的信息,然后客户端也会受到服务器发来的信息。
上面的例子中,服务器和客户端都是收发了一条信息后socket关闭,如果要保持连接进行长时间通信呢?那么,我们可以把收发函数放入一个“while True”循环中:
服务器端代码:
import socket
BUF_SIZE = 1024 #设置缓冲区大小
server_addr = ('127.0.0.1',8089) #IP和端口构成表示地址
server = socket.socket(socket.AF_INET,socket.soCK_STREAM) #生成一个新的socket对象
server.bind(server_addr) #绑定地址
print("socket与地址绑定完成……")
server.Listen(5) #监听,最大监听数为5
print("socket监听开始……")
clIEnt,clIEnt_addr = server.accept() #接收TCP连接,并返回新的套接字和地址,阻塞函数
print("报告:有客户端请求连接,正在连接……")
print('客户端地址为:{}'.format( clIEnt_addr))
while True :
mes_from_clIEnt = clIEnt.recv(BUF_SIZE) #从客户端接收数据
mes = mes_from_clIEnt.decode('utf-8')
print('客户端说:{}'.format(mes))
mes = input('回复客户端的信息>')
mes_to_clIEnt = mes.encode('utf-8')
clIEnt.sendall(mes_to_clIEnt) #发送数据到客户端
server.close()
客户端代码:
import socket
BUF_SIZE = 1024 #设置缓冲区的大小
server_addr = ('127.0.0.1',8089) #IP和端口构成的服务器地址
clIEnt = socket.socket(socket.AF_INET,socket.soCK_STREAM) #返回新的socket对象
clIEnt.connect(server_addr) #连接服务器
while True:
mes = input("发送给服务器的信息> ")
mes_to_server = mes.encode('utf-8')
clIEnt.sendall(mes_to_server) #发送数据到服务器
mes_from_server = clIEnt.recv(BUF_SIZE) #从服务器端接收数据
mes = mes_from_server.decode('utf-8')
print(mes)
clIEnt.close()
运行上述代码后,客户端和服务器可以长时间维持通信。不过,使用socket时一定要注意,有发才有收,收发必相等,否则,就回出现异常。如果需要求换其他客户端与当前服务器进行通信,必须先断开当前客户端的连接。
再来尝试UDP协议下socket通信:
服务器端代码:
import socket
sk = socket.socket(type=socket.soCK_DGRAM)
sk.bind(('127.0.0.1',8090))
print('等待客户端发来消息……')
msg,addr = sk.recvfrom(1024) # 此处会阻塞
print(msg.decode('utf-8'))
mes_to_server = '你好,linux公社www.linuxIDc.com客户端,已收到你的信息!'.encode('utf-8')#发送的数据必须是byte类型
sk.sendto(mes_to_server,addr)
sk.close()
客户端代码:
import socket
sk = socket.socket(type=socket.soCK_DGRAM)
ip_port = ('127.0.0.1',8090)
mes_to_server = '你好,linux公社www.linuxIDc.com服务器!'.encode('utf-8')#发送的数据必须是byte类型
sk.sendto(mes_to_server,ip_port)
ret,addr = sk.recvfrom(1024)
print(ret.decode('utf-8'))
sk.close()
如果需要不停收发消息,代码更改如下:
服务器端代码:
import socket
sk = socket.socket(type=socket.soCK_DGRAM)
sk.bind(('127.0.0.1',8090))
print('等待linux公社linuxIDc.com客户端发来消息……')
while True:
msg,addr = sk.recvfrom(1024) # 此处会阻塞
print('linux公社收到{}发来的信息,内容是:{}'.format(addr,msg.decode('utf-8')))
mes_to_server = input('>>>').encode('utf-8')#发送的数据必须是byte类型
sk.sendto(mes_to_server,8090)
while True:
mes_to_server = input('>>>').encode('utf-8')#发送的数据必须是byte类型
sk.sendto(mes_to_server,addr = sk.recvfrom(1024)
print(ret.decode('utf-8'))
sk.close()
进群:960410445 即可获取数十套pdf!
使用socket进行UDP协议下通信时,可以多个客户端与服务器通信,也就是说,上面客户端代码你可以另开一个进程与服务器通信,且不需要关闭当前客户端,这是TCP协议与UDP协议下socket通信的一个不同之处。
5、总结
本篇初步总结了Python网络编程中socket模块的使用,事实上只是大致总结了基本函数的用法
总结以上是内存溢出为你收集整理的Python网络编程之socket模块基础实例!全部内容,希望文章能够帮你解决Python网络编程之socket模块基础实例!所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)