python网络编程Socket
-班级:自动化5班
-姓名:刘廷威
-学号:3118001021
-邮箱:52642202@qq.com
参考资料:《Python核心编程(第三版)》
学习内容:
- 套接字相关知识(前导)
- socket模块中的函数、属性和套接字对象内置的方法、属性
- socket实现TCP服务器/客户端的数据传输
- socket实现UDP服务器/客户端的数据传输
一、套接字
(一)介绍和分类
套接字(Socket)是计算机网络数据结构,体现了“通信端点”的概念。在任何类型的通信开始前,网络应用程序都必须创建套接字。
套接字最初是为主机上两个进程通信而创建。
套接字的类型有两种:基于文件和面向网络。
(1)基于文件的套接字
UNIX套接字是套接字里的一个家族,并且有“家族名字”:AF_UNIX(又名AF_LOCAL)。其中AF代表地址家族(Address Family),除了地址家族外还有域家族(Domain Family)和协议家族(Protocol Family)。
基于文件的套接字用于两个进程运行在同一台计算机上的通信。意味着文件系统支持它们的底层结构,这是因为文件系统是一个运行在同一主机的多个进程之间的共享常量。
(2)基于网络的套接字
其家族名字为AF_INET,另一个地址家族AF_INET6用于因特网协议(IPv6)寻址。
除此之外还有AF_NETLINK家族(无连接);
AF_TIPC家族:支持透明的进程间通信(TIPC)协议,TIPC允许计算机集群之中的机器相互通信,而无须使用基于IP的寻址方式。
总的来说,Python只支持AF_UNIX、AF_NETLINK、AF_TIPC、AF_INET家族,而网络编程重点讨论AF_INET。
(二)套接字地址:主机-端口对
1、简单来说,一个套接字就像一个电话插孔,那么主机名和端口号则像区号和电话号码的组合,在拨打电话前必须有其他人在另一端监听。
2、一个网络地址由主机名和端口号对组成。
3、计算机有效端口范围0~65535(小于1024的端口预留给了系统)
(三)面向连接的套接字和无连接的套接字
无论采用哪种地址家族,都有两种不同风格的套接字连接方式。
(一)面向连接的套接字
1、这意味着在进行通信之前必须先建立一个连接。
2、这种面向连接的通信提供了序列化的、可靠的、不重复的数据交付,并且没有记录边界(指数据包的分割没有固定边界大小)
3、实现这种连接类型的主要协议是传输控制协议(TCP)。为创建TCP套接字,必须使用SOCK_STREAM(基于流的套接字的其中一种表示)作为套接字类型。
4、在监听到连接并且成功连接后,会以新的Socket来进行下面的数据传输(就像总接线员会将电话转接给合适的人来处理)。
【注意】由于AF_INET这种套接字使用因特网协议(IP)来搜寻网络中的主机,所以整个系统通常结合这两种协议(TCP/IP)。
当然,也可以使用TCP和本地套接字(非网络的AF_LOCAL/AF_UNIX)结合,这里很明显并不需要IP。
(二)无连接的套接字
1、意味着通信开始前并不需要建立连接。
2、意味着数据传输过程中无法保证它的顺序性、可靠性、重复性。
3、这种类型的套接字指的是数据报类型的套接字。然而,数据报确实保存了记录边界,但也意味着消息是以整体发送的,并非首先分成多个片段。
4、实现这种连接类型的主要协议是用户数据报协议(UDP)。为创建UDP套接字,必须使用SOCK_DGRAM(datagram数据报)作为套接字类型。
二、Socket模块
(一)socket模块中的函数socket()
import socket
该模块使用socket.socket()函数来创建套接字,一般语法如下:
socket(socket_family,socket_type,protocol=0)
---参数: socket_family指地址家族,如AF_INET、AF_UNIX等等
socket_type指连接类型,如面向连接的SOCK_STREAM或无连接的SOCK_DGRAM
protocol通常省略,默认为0
例如,创建TCP/IP套接字,可以用下面的方式:
tcpSock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
创建UDP/IP套接字,可以用下面的语句:
udpSock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
【补充】 1、该socket()函数将返回一个套接字对象
2、因为有很多socket模块属性和方法,所以也可以考虑使用from socket import *这种方式导入。
(二)套接字对象(内置)方法
在利用完socket()函数创建并返回一个套接字对象后,就可以使用socket模块中用于套接字对象的方法和属性,常见的如下:
名称 | 描述 |
---|---|
服务器套接字方法 | |
s.bind(ADDR) | 将地址(主机名、端口对)绑定到套接字上。通常地址参数ADDR是以(host,port)形式的元组 |
s.listen(backlog) | 设置并启动TCP监听。bakclog参数至少为1,用于指定在拒绝连接前,操作系统可以挂起的最大连接数量,通常设为5 |
s.accept() | 被动接受TCP客户端连接,(阻塞式地)等待连接到来。当连接成功后返回(NewSock,ADDR),其中NewSock为连接成功后新的套接字,用以接下来的数据传输。ADDR为客户端的地址参数 |
客户端套接字方法 | |
s.connect(ADDR) | 向TCP服务端发起连接。通常地址参数ADDR是以(host,port)形式地元组 |
s.connect_ex(ADDR) | connet()的扩展版本,此时会以错误码的形式返回,而不是抛出一个异常 |
普通的套接字方法 | |
s.recv(bufsize[,flag]) | 接受TCP消息,数据以字符串形式返回。bufsize指定要接收的最大数据,单位为B。flag提供消息有关的信息,通常忽略 |
s.recv_into() | 接收TCP消息到指定的缓冲区 |
s.send(string) | 发送TCP消息,将string中的数据发送到连接的套接字中。返回值是要发送的字节数量。 |
s.sendall(string) | 接收UDP消息,与TCP的recv()类似。但返回值是(data,ADDR),其中data指接受到的字符串数据,ADDR是发送方的套接字地址 |
s.recvfrom_into(bufsize[,flag]) | 接收UDP消息到指定的缓冲区 |
s.sendto(string[,flag],ADDR) | 发送UDP消息。ADDR为远程地址(host,port)。返回值为发送的字节数 |
s.getpeername() | 返回连接套接字(TCP)的远程地址,为(host,port) |
s.getsockname() | 返回套接字自己的地址(host,port) |
s.getsockopt() | 返回给定套接字选项的值 |
s.setsockopt() | 设定给定套接字选项的值。默认的socket选项不够用时,使用setsockopt来调整 |
s.shutdown() | 关闭连接 |
s.close() | 关闭套接字 |
s.detach() | 在未关闭文件描述符的情况下关闭套接字,返回文件描述符 |
s.ioctl() | 控制套接字的模式(仅支持Windows) |
面向阻塞的套接字方法 | |
s.setblocking(flag) | 设置套接字的阻塞或非阻塞模式。flag默认值为1,若为0,则是将套接字设为非阻塞模式 |
s.settimeout(timeout) | 设置阻塞套接字操作的超时时间。timeout为浮点数,单位为秒,值为None时表示没有超时时间 |
s.gettimeout() | 获取阻塞套接字操作的超时时间 |
面向文件的套接字方法 | |
s.fileno() | 返回套接字的文件描述符 |
s.makefile() | 创建与套接字关联的文件对象 |
数据属性 | |
s.family | 返回套接字家族 |
s.type | 返回套接字类型 |
s.proto | 返回套接字协议 |
三、思路
TCP服务器创建:(伪代码)
ss=socket() #创建服务器套接字
ss.bind() #套接字与地址绑定
ss.listen() #监听连接
inf_loop: #服务器无限循环
cs=ss.accept() #接受客户端连接、
comm_loop: #通信循环
cs.revc()/cs.send() #对话(接收/发送)
cs.close() #关闭客户端套接字
ss.close #关闭服务器套接字(可选)
TCP客户端创建:(伪代码)
cs=socket() #创建客户端套接字
cs.connect() #尝试连接服务器
comm_loop: #通信循环
cs.send()/cs.recv() #对话(发送/接收)
cs.close() #关闭客户端套接字
UDP服务器创建:(伪代码)
ss=socket() #创建服务器套接字
ss.bind() #绑定服务器套接字
inf_loop: #服务器无限循环
cs=ss.recvfrom()/ss.sendto() #关闭(接收/发送)
ss.close() #关闭服务器套接字
UDP客户端创建:(伪代码)
cs=socket() #创建客户端套接字
comm_loop: #通信循环
cs.sendto()/cs.recvfrom() #对话(发送/接收)
cs.close() #关闭客户端套接字
四、实例
(一)TCP实例
服务器:(SerTcp.py)
import socket #导入socket模块
HOST='localhost' #服务器主机名/IP
PORT=21567 #服务器通信端口
BUFSIZ=1024 #缓冲区大小
ADDR=(HOST,PORT) #创建套接字地址元组
tcpSerSock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建TCP类型的服务器套接字
tcpSerSock.bind(ADDR) #套接字绑定地址元组
tcpSerSock.listen(5) #监听连接,设置可挂起最大数为5
while True:
print("waiting for connection...")
tcpCliSock,addr=tcpSerSock.accept() #阻塞式地等待连接,若成功连接则返回与客户端通信的新套接字和客户端地址元组
print("...connected from {ADDR}".format(ADDR=addr))
while True:
data1=tcpCliSock.recv(BUFSIZ) #接收客户端的数据
if not data1:
break
data2='nihao!'
data2=bytes(data2,'utf-8') #TCP以字节形式分片传输,所以将数据提前编码
#上个语句也可以换成data2=data2.encode(encoding='UTF-8')
tcpCliSock.send(data2) #向客户端发送数据
tcpCliSock.close() #关闭与某个客户端的套接字,继续回到外层循环进行监听
tcpSerSock.close() #当服务器不再监听时,关闭服务器套接字
客户端:(CliTcp.py)
import socket #导入socket模块
HOST='localhost' #需要访问的服务器主机名/IP
PORT=21567 #需要访问的服务器通信端口
BUFSIZ=1024 #缓冲区大小
ADDR=(HOST,PORT) #创建套接字地址元组
tcpCliSock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #创建TCP类型的客户端套接字
tcpCliSock.connect(ADDR) #连接地址元组为ADDR的套接字
while True:
data1=input(">") #输入需要发送的数据
if not data1:
break
data1=data1.encode(encoding='UTF-8') #TCP以字节形式分片传输,所以将数据提前编码
tcpCliSock.send(data1) #向服务器发送数据
data2=tcpCliSock.recv(BUFSIZ) #接收服务器的数据
if not data2:
break
#将收到的服务器数据解码并打印
data2=data2.decode(encoding='UTF-8')
print(data2)
tcpCliSock.close() #关闭客户端套接字
(一)UDP实例
服务器:(SerUdp.py)
import socket #导入socket模块
HOST='localhost' #服务器主机名/IP
PORT=21567 #服务器通信端口
BUFSIZ=1024 #缓冲区大小
ADDR=(HOST,PORT) #创建套接字地址元组
udpSerSock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #创建UDP类型的服务器套接字
udpSerSock.bind(ADDR) #套接字绑定地址元组
#UDP与TCP不同,这里不需要设置监听
while True:
print('waiting for message...') #与TCP不同,这里等待的是message,而不是connection
#与TCP不同,用来发送和接收数据的套接字为服务器的套接字
data1,addr=udpSerSock.recvfrom(BUFSIZ) #当接收到某客户端的消息时,返回数据和客户端地址元组
data2='nihao!'
data2=bytes(data2,'utf-8') #TCP以字节形式分片传输,所以将数据提前编码
udpSerSock.sendto(data2,addr) #与TCP不同,由于UDP无连接,所以发送数据时需要加上客户端的地址元组
print('...received from and returned to:',addr) #打印客户端的地址元组
udpSerSock.close() #关闭服务器套接字
客户端:(CliUdp.py)
import socket #导入socket模块
HOST='localhost' #服务器主机名/IP
PORT=21567 #服务器通信端口
BUFSIZ=1024 #缓冲区大小
ADDR=(HOST,PORT) #创建套接字地址元组
udpSerSock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #创建UDP类型的服务器套接字
udpSerSock.bind(ADDR) #套接字绑定地址元组
#UDP与TCP不同,这里不需要设置监听
while True:
print('waiting for message...') #与TCP不同,这里等待的是message,而不是connection
#与TCP不同,用来发送和接收数据的套接字为服务器的套接字
data1,addr=udpSerSock.recvfrom(BUFSIZ) #当接收到某客户端的消息时,返回数据和客户端地址元组
data2='nihao!'
data2=bytes(data2,'utf-8') #TCP以字节形式分片传输,所以将数据提前编码
udpSerSock.sendto(data2,addr) #与TCP不同,由于UDP无连接,所以发送数据时需要加上客户端的地址元组
print('...received from and returned to:',addr) #打印客户端的地址元组
udpSerSock.close() #关闭服务器套接字