Socket介绍
Socket是计算机之间进行网络通信的一套程序接口,相当于在发送端和接收端之间建立了一个通信管道。在实际应用中,一些远程管理软件和网络安全软件大多依赖于Socket来实现特定功能。本篇主要讲解TCP(传输控制协议——在网络编程中应用的比较频繁)方式在网络编程中的具体应用实例以及代码详解!
socket.socket(family = AF_INET,type = SOCK_STREAM,proto = 0,fileno = None
family(协议族): AF_UNIX 用于单一的Unix系统进程间通信,要用一个绝对路径名作为地址 AF_INET 默认IPV4 AF_INET6 IPV6 tpye(类型): SOCK_STREAM 流式socket,TCP连接方式,默认参数 SOCK_DGRAM 数据包式,不可靠UDP SOCK_RAW 处理特殊的IPv4报文 IGMP ICMP SOCK_RDM 可靠UDP形式 SOCK_SEQPACKET 可靠的连续数据包服务 proto(协议): 默认为0,表示会自动选择type类型对应的默认协议 IPPROTO_TCP TCP传输协议 IPPROTO_UDP UDP传输协议 IPPROTO_STCP STCP传输协议 IPPROTO_TIPC TIPC传输协议
socket常用函数
服务器端 | ||
函数名称 | 作用 | 备注 |
bind((ip,port)) | 绑定地址 | 以元组方式(地址,端口) |
listen(number) | 开始监听 | number表示操作糸统可以挂起的最大连接数量,默认为1 |
server.accept() | 接受连接 | 返回两个值,需要用两个变量来接收(conn, addr)每进来一个链接,服务端都会生成一个实例 conn用来接收这个实例,addr是链接的地址 |
客户端 | ||
connect(address) | 连接远程计算机 | address的格式为元组:(ip,port) |
connect_ex(address) | connect相似远程连接 | 只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061 |
通用函数 | ||
send(bytes[, flags]) | 发送数据 | 发送的为bytes字节模式,英文字符串前加b--> b'hello',中文要加encode---->"中文字符串".encode("utf-8"), flags默认为空 |
sendall(bytes[,flag]) | 连续发送数据 | 直到发送完所有数据或发生错误异常,并且无法确定成功发送了多少数据 |
sendto(bytes, flags, address) | 指定发送地址 | address为元组:(ip,port),指接收数据的套接字的远程地址 |
recv(bufsize[,flag]) | 接收数据 | 返回值是一个字节对象,bufsize指定要接收的最大数据量,不可起过8193 |
recvfrom(bufsize[, flags]) | 接收数据 | 返回值是一对(bytes,addr) ,其中bytes是表示接收到的数据的字节对象,addr是发送数据的地址 |
close() | 关闭 | |
socket.detach() | 关闭套接字 | 在未关闭文件描述符的情况下关闭,并返回文件描述符 |
dup() | 复制套接字 | |
fileno() | 套接字的文件描述符 | 返回socket的文件描述符(一个小整数),失败时返回 -1 |
get_inheritable() | 获取套接字的文件描述 | 获取套接字的文件描述符或套接字句柄的可继承标志:如果套接字可以在子进程中继承,如果不能。True False |
getpeername() | 返回套接字连接到的远程地址 | 例如,这对于找出远程 IPv4/v6 套接字的端口号很有用。 |
getsockopt(level, optname[, buflen]) | 返回给定套接字选项的值 | 如果buflen不存在,则假定为整数选项,并且函数返回其整数值。如果buflen存在,它指定用于接收选项的缓冲区的最大长度,并且此缓冲区作为字节对象返回。 |
ioctl(control, option) | 控制套接字的模式 | (仅支持 Windows) |
makefile(mode='r', buffering=None, *, encoding=None, errors=None, newline=None) | 返回与套接字关联的文件对象 | 确切的返回类型取决于给 的参数。这些参数的解释方式与内置函数的解释方式相同 |
recvmsg(bufsize[, ancbufsize[, flags]]) | 从socket接收普通数据 | 最大bufsize字节)和辅助数据。ancbufsize参数设置用于接收辅助数据的内部缓冲区的大小(以字节为单位);它默认为 0,表示不会接收任何辅助数据。可以使用 或计算辅助数据的适当缓冲区大小,不适合缓冲区的项目可能会被截断或丢弃。 |
recvfrom_into(buffer[, nbytes[, flags]]) | 接收 UDP 消息到指定的缓冲区 | 从套接字接收数据,将其写入缓冲区而不是创建新的字节串。返回值是一对,其中nbytes是接收到的字节数,address是发送数据的套接字地址。 |
recv_into(buffer[, nbytes[, flags]]) | 接收 TCP 消息到指定的缓冲区 | 从socket接收最多nbytes字节,将数据存储到缓冲区而不是创建新的字节串。如果未指定nbytes (或 0),则接收最大为给定缓冲区中可用的大小 |
阻塞 面向阻塞的套接字方法 | ||
setblocking() | 设置套接字的阻塞或非阻塞模式 |
这相当于检查. |
settimeout() | 设置阻塞套接字操作的超时时间 | 返回与套接字操作关联的以秒为单位的超时(浮点数),或者None 如果没有设置超时。 |
gettimeout() | 获取阻塞套接字操作的超时时间 | |
文件方法 面向文件的套接字方法 | ||
fileno() | 套接字的文件描述符 | |
makefile() | 创建与套接字关联的文件对象 |
简单通信TCP服务器
import socket
server = socket.socket()
server.bind(('localhost',6968)) #绑定地址
server.listen(5) #开始监听,等待客户连接 监听5个
print("等响应")
while True:
conn,addr = server.accept() #响应客户端的一个请求 conn,addr用来接收accept返回的两个值,分别是对方的链接和地址
print("客户端响应",addr)
while True:
data = conn.recv(1024)
print("接收来自客户端发来的数据:",data.decode()) #接收客户端发来的数据,因为数据为bytes类型,所以decode()
if not data :
break
input_0 = input(">>:").strip()
conn.send(input_0.encode("utf-8")) #向客户端发送数据
server.close()
简单通信TCP客户端
import socket
client = socket.socket() ##声明socket类型,同时生成socket连接对象
client.connect(('localhost',6968)) #连接远程计算机
while True:
input_0 = input(">>:").strip()
if len(input_0) == 0:continue
client.send(input_0.encode("utf-8")) #发送给服务端的数据
data = client.recv(1024) #接收数据1024字节
print("接收服务端发来的数据:",data.decode())
client.close() #关闭
struct----将字节解释为打包的二进制数据
该模块定义了以下异常和函数:
exception struct.error
在各种场合引发的异常;argument 是描述错误的字符串。
struct.pack(format, v1, v2, ...)
返回一个字节对象,第一个参数format格式字符串,v1、v2 、…打包的值 。参数必须与格式所需的值完全匹配。
struct.pack_into(format, buffer, offset, v1, v2, ...)
根据格式字符串format打包值v1,v2 ,...,并将打包的字节写入从位置offset开始的可写缓冲区buffer中。请注意,offset是必需的参数。
struct.unpack(format, buffer)
根据格式字符串格式从缓冲区buffer中解压(大概是 packed by ) 。结果是一个元组,即使它只包含一个项目。缓冲区的大小(以字节为单位)必须与格式所需的大小相匹配,如.
pack(format, ...)
struct.unpack_from(format, /, buffer, offset=0)
根据格式字符串格式,从位置offset开始的缓冲区中解包。结果是一个元组,即使它只包含一个项目。缓冲区的大小(以字节为单位)从位置offset开始,必须至少为格式所 需 的大小
struct.iter_unpack(format, buffer)
根据格式字符串format迭代地从缓冲区buffer中解包。此函数返回一个迭代器,该迭代器将从缓冲区中读取相同大小的块,直到其所有内容都被消耗为止。缓冲区的大小(以字节为单位)必须是格式所需大小的倍数,如.calcsize()
struct.calcsize(format)
返回对应于格式字符串format的结构(以及由 生成的字节对象 )的大小。
pack(format, ...)
struct中支持的格式
Format | C Type | Python | 字节数 |
---|---|---|---|
x | pad byte 填充字节 | no value 没有值 | 1 |
c | char 字符 | string of length 1 整数 | 1 |
b | signed char 签名字符 | integer 整数 | 1 |
B | unsigned char 无符号字符 | integer 整数 | 1 |
? | _Bool _布尔 | bool 布尔 | 1 |
h | short 短的 | integer 整数 | 2 |
H | unsigned short 无符号短 | integer 整数 | 2 |
i | int 整数 | integer 整数 | 4 |
I | unsigned int 无符号整数 | integer or long 整数 | 4 |
l | long 长 | integer 整数 | 4 |
L | unsigned long 无符号长 | long 整数 | 4 |
q | long long 长长的 | long 整数 | 8 |
Q | unsigned long long 无符号长 | long 整数 | 8 |
f | float 浮点 | float 浮点 | 4 |
d | double 双倍的 | float 浮点 | 8 |
s | char[] 字符[ ] | string | 1 |
p | char[] 字符[ ] | string | 1 |
P | void * 无效* | long 整数 |
注1.q和Q只在机器支持64位操作时有意思
为了同c中的结构体交换数据,还要考虑有的c或c++编译器使用了字节对齐,通常是以4个字节为单位的32位系统,故而struct根据本地机器字节顺序转换.可以用格式中的第一个字符来改变对齐方式.定义如下:
Character | Byte order | Size and alignment |
---|---|---|
@ | native 本地 | native 凑够4个字节 |
= | native 本地 | standard 按原字节数 |
< | little-endian 小端 | standard 按原字节数 |
> | big-endian 大端 | standard 按原字节数 |
! | network (= big-endian) 网络(=大端) | standard 按原字节数 |
使用方法是放在fmt的第一个位置,就像'@5s6sif'
socketserver
简介
该模块简化了编写网络服务器的任务,SocketServer使用了select它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步。
TCPServer ( server_address , RequestHandlerClass , bind_and_activate = True )
TCP 协议,该协议在客户端和服务器之间提供连续的数据流。如果bind_and_activate为真,则构造函数会自动尝试调用server_bind() and server_activate()。其他参数传递给 BaseServer基类。
UDPServer ( server_address , RequestHandlerClass , bind_and_activate = True )
这使用数据报,它们是离散的信息包,可能会乱序到达或在传输过程中丢失。参数与 TCPServer相同
UnixStreamServer ( server_address , RequestHandlerClass , bind_and_activate = True ) 只能在Unix 平台上用。TCP 参数与 TCPServer相同
UnixDatagramServer ( server_address , RequestHandlerClass , bind_and_activate = True ) 只能在Unix 平台上用。UDP 参数与 TCPServer相同
创建一个socketserver至少分以下几步:
首先,您必须创建一个请求处理类,并且通过继承BaseRequestHandler,再重写父类里的handle()方法
其次,先实例化其中一个服务器类(例:TCPServer),再传递 server ip 和上面创建的请求处理类给这个服务器类(例:TCPServer)。
然后调用服务器对象的 handle_request() 只处理一个请求 or serve_forever()处理多个请求 永远执行。
最后,调用server_close() 关闭套接字(除非您使用with
语句)。
class socketserver.BaseServer(server_address, RequestHandlerClass) 这是模块中所有服务器对象的超类。它定义了下面给出的接口,但没有实现大部分方法,这些方法是在子类中完成的。这两个参数存储在各自的 server_address和RequestHandlerClass属性中。
fileno() 文件描述 返回服务器正在侦听的套接字的整数文件描述符。此函数最常传递给selectors,以允许在同一进程中监视多个服务器。
handle_request() 处理单个请求
serve_forever(poll_interval=0.5) 处理请求,直到一个明确的shutdown()请求。每poll_interval秒轮询一次shutdown。忽略self.timeout。如果你需要做周期性的任务,建议放置在其他线程。
service_actions() 这在serve_forever()循环中被调用。此方法可以被子类或混合类覆盖,以执行特定于给定服务的操作,例如清理操作。
shutdown() 关机 告诉serve_forever()循环停止并等待其停止。
server_close() 服务器关闭 清理服务器。可能被覆盖。
address_family 地址家族,比如socket.AF_INET和socket.AF_UNIX。
RequestHandlerClass 用户提供的请求处理类,这个类为每个请求创建实例。
server_address 服务器侦听的地址。格式根据协议家族地址的各不相同,请参阅socket模块的文档。
allow_reuse_address 允许重用地址 服务器是否允许重复使用地址。这默认为 False,并且可以在子类中设置以更改策略。
request_queue_size 请求队列大小 请求队列的大小。如果处理单个请求需要很长时间,则在服务器繁忙时到达的任何请求都将放入队列中,直到request_queue_sizerequests。一旦队列已满,来自客户端的进 一步请求将收到“连接被拒绝”错误。默认值通常是 5,但可以被子类覆盖
socket_type 套接字类型 服务器使用的套接字类型;socket.SOCK_STREAM并且 socket.SOCK_DGRAM是两个共同的价值观。
timeout 超时 超时持续时间,以秒为单位,或者None如果不需要超时。如果handle_request()在超时期限内没有收到传入请求,handle_timeout()则调用该方法。
finish_request(request, client_address) 实际上通过实例化RequestHandlerClass和调用它的handle()方法来处理请求。
get_request() 必须接受来自套接字的请求,并返回一个二元组,其中包含 用于与客户端通信的新套接字对象,以及客户端的地址。
handle_error(request, client_address) handle() 如果实例的方法RequestHandlerClass引发异常,则调用此函数。默认操作是将回溯打印到标准错误并继续处理进一步的请求。
handle_timeout() 当该timeout属性被设置为除 之外的值None并且超时期限已过且未收到任何请求时,将调用此函数。分叉服务器的默认操作是收集任何已退出的子进程的状态,而在线程服务器中,此方法不执行任何操作。
process_request(request, client_address) 调用finish_request()以创建 的实例 RequestHandlerClass。如果需要,这个函数可以创建一个新的进程或线程来处理请求;theForkingMixIn和 ThreadingMixInclasses 做这个。
server_activate() 由服务器的构造函数调用以激活服务器。TCP 服务器的默认行为只是调用listen() 服务器的套接字。可能被覆盖。
server_bind() 由服务器的构造函数调用以将套接字绑定到所需的地址。可能被覆盖。
verify_request(request, client_address) 必须返回一个布尔值;如果值为True,请求将被处理,如果是False,请求将被拒绝。可以重写此函数以实现对服务器的访问控制。默认实现总是返回True。 请求处理程序对象
class socketserver.BaseRequestHandler
setup() 设置 handle()在执行所需的任何初始化操作的方法之前调用。默认实现什么也不做。
handle() 句柄 此功能必须完成为请求提供服务所需的所有工作。默认实现什么也不做。它有几个实例属性;该请求可作为self.request;客户地址为self.client_address;和服务器实例
self.server,以防它需要访问每个服务器的信息。 finish() 完成 handle()在执行所需的任何清理操作的方法之后调用。默认实现什么也不做。如果setup() 引发异常,则不会调用此函数。
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self): #在父类里为空,跟客服端所有的交接都在handle里写的
print("客户端IP%s" % (self.client_address[0])) # 打印客户端 ip地址
while True:
self.data = self.request.recv(1024).strip() #收 实例化接收对象
print(self.data)
len_data = len(self.data)
print(len_data)
self.request.send(self.data.upper()) #发
if not self.data:break
"if name == ‘main’ 我们简单的理解就是: 如果模块是被直接运行的,则if __name__ == '__main__'下的代码块被运行;" \
"如果该模块被其他程序导入,则if __name__ == '__main__'下的代码块不被运行。"
if __name__ == "__main__":
HOST, PORT = "localhost", 9995 #创建服务器,并绑定到端口9999上的本地主机
#socketserver.TCPServer((HOST, PORT), MyTCPHandler)
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) #第2步修改ThreadingTCPServer,进行多并发
server.serve_forever()