套接字是计算机网络的数据结构,它体现了“通行端点”的概念。在任何类型的通信开始之前,网络应用程序都要创建套接字。有两种类型的套接字:基于文件的和基于网络的。
UNIX套接字拥有一个“家族名字” AF_UNIX(又名AF_LOCAL),它代表地址家族(address family):UNIX。因为两个进程运行在同一个计算机上,所以这些套接字都是基于文件的,这意味着文件系统支持他们的底层基础结构。因为文件系统是一个运行在同一台主机上的多个进程之间的共享常量。
另一种类型的套接字是基于网络的,它也有自己的家族名字 :AF_INET,或者地址家族:因特网。另一个地址家族 AF_INET6用于IPv6。
总的来说,Python 支持 AF_UNIX、AF_NETLINK、AF_TIPC 和 AF_INET 家族。
有效的端口号范围为 0~65535(尽管小于 1024 的端口号预留给了系统),如果使用的是 POSIX 兼容的系统,可以在 /etc/services 文件中找到预留的端口号(以及服务器/协议和套接字类型)。
无论你使用哪种地址家族,都有两种不同风格的套接字连接。第一种是面向连接的,这种类型的通信称为虚拟电路或流套接字。
面向连接的通信提供序列化的(拆分为多个片段)、可靠的和不重复的数据交付,而没有记录边界。为了实现 TCP 套接字,必须使用 SOCK_STREAM 作为套接字类型。
数据报类型的套接字。在数据传输过程中无法保证它的顺序性、可靠性或重复性。整个消息是整体发送的(不拆分),即保证了数据的边界记录。
实现这种连接类型的主要协议是用户数据报协议(UDP),为了创建 UDP 套接字,必须使用 SOCK_DGRAM 作为套接字类型。
要创建套接字,必须使用socket.socket()函数,它的一般语法如下:
socket(socket_family,socket_type,protocol=0)
其中 socket_family 是 AF_UNIX 或 AF_INET;socket_type 是 SOCK_STREAM 或 SOCK_DGRAM;protocol通常省略,默认为0。
所以,为了创建 TCP/IP 套接字,可以使用下面的方式调用 socket.socket():
tcpSock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
所以,为了创建 UDP/IP 套接字,可以使用下面的方式调用 socket.socket():
udpSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
可以把 socket 模块导入命名空间,这样可以缩短代码:
from socket import *
tcpSock = socket(AF_INET,SOCK_STREAM)
方法 | 说明 |
---|---|
服务器套接字方法 | |
s.bind() | 将地址(主机名、端口号对)绑定到套接字上 |
s.listen() | 设置并启动TCP监听器 |
s.accept() | 被动接受TCP客户端连接,一直等待直到连接到达(阻塞) |
客户端套接字方法 | |
s.connect() | 主动发起TCP服务器连接 |
s.connect_ex() | connect()的扩展版本,此时会以错误码的形式返回问题,而不是抛出一个异常 |
普通的套接字方法 | |
s.recv() | 接受TCP消息 |
s.recv_into() | 接受TCP消息到指定的缓冲区 |
s.send() | 发送TCP消息 |
s.sendall() | 完整地发送TCP消息 |
s.recvfrom() | 接受UDP消息 |
s.recvfrom_into() | 接受UDP消息到指定的缓冲区 |
s.sendto() | 发送UDP消息 |
s.getpeername() | 连接到套接字(TCP)的远程地址 |
s.getdocketname() | 当前套接字地址 |
s.getsockopt() | 返回给定套接字选项的值 |
s.setsockopt() | 设置给定套接字选项的值 |
s.shutdown() | 关闭连接 |
s.close() | 关闭套接字 |
s.detach() | 在未关闭文件描述符的情况下关闭套接字,返回文件描述符 |
s.ioctl() | 控制套接字的模式(仅支持Windows) |
面向阻塞的套接字方法 | |
s.setblocking() | 设置套接字的阻塞或非阻塞模式 |
s.settimeout() | 设置阻塞套接字操作的超时时间 |
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.recv()/cs.send() #对话(接受/发送)
cs.close() #关闭客户端套接字
ss.close() #关闭服务器套接字(可选)
#第一次发数据要用encode函数,第一次收数据要用decode函数,指定编码
所有的套接字都是通过 socket.socket() 函数创建的。因为服务器需要占用一个端口并等待客户端的请求,所以他们必须绑定到一个本地地址。特别的,TCP 服务器必须监听(传入)的连接。一旦这个安装过程完成后,服务器就可以开始他的无限循环。
调用 accept() 函数之后,就开启了一个简单的(单线程)服务器,它会一直等待客户端的连接。默认情况下,accpet()是阻塞的,这意味着执行将被暂停,直到一个连接到达。
一旦服务器接受了一个连接,就会返回(利用 accept() )一个独立的客户端套接字,用来与即将到来的消息进行交换。这将能够空出原始服务器套接字,以便可以继续等待新的请求。同样的,当一个传入的请求到达时,服务器会创建一个新的通信端口直接与客户端进行通信,再次空出主要的端口,以使其能够接受新的客户端连接。
举例来说:我们创建一个 TCP 服务器,它接受客户端发送的数据字符串,并将其打上时间戳并返回给客户端:
#tsTeser.py文件 IP:10.0.0.246 OS:Linux
#!/usr/bin/python3
from socket import *
from time import ctime
HOST = '' #HOST 变量是空白的,这是对 bind()方法的标识,表示它可以使用任何可用的地址。
PORT = 21567
BUFSIZ = 1024 #缓冲区
ADDR = (HOST,PORT)
tcpSerSock = socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5) #传入连接请求的最大数
while True:
print("等待客户端的连接...")
tcpCliSock,addr = tcpSerSock.accept()
print("...连接来自:",addr)
while True:
data = tcpCliSock.recv(BUFSIZ)
if not data:
break
tcpCliSock.send((ctime() + "\t").encode('utf-8') + data) #将字符串作为一个 ASCII字节“字符串”发送,而非 Uniocde 编码
tcpCliSock.close()
tcpSerSock.close()
创建TCP客户端的伪代码
cs = socket() #创建客户端套接字
cs.connect() #尝试连接服务器
comm_loop: #通信循环
cs.send()/cs.recv() #对话(发送/接收)
cs.close() #关闭客户端套接字
所有套接字都是利用 socket.socket() 创建的。一旦客户端拥有了一个套接字,它就可以利用套接字 connect() 方法直接创建一个到服务器的连接。当连接建立后,它就可以参与服务器的一个对话中。最后,一旦客户端完成了它的事务,它就可以关闭套接字,终止此次连接。
#tsTclnt.py IP:10.0.0.100 OS:Windows
from socket import *
HOST = '10.0.0.246' # 'li' #指定服务器 IP 或主机名
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST,PORT)
tcpCliSock = socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
data = input()
if not data:
break
tcpCliSock.send(data.encode('utf-8'))
data = tcpCliSock.recv(BUFSIZ) #从服务器接受的数据
if not data:
break
print(data.decode('utf-8'))
tcpCliSock.close()
运行结果:
#服务器端
[root@li python]# ./tsTestser.py
等待客户端的连接...
...连接来自: ('10.0.0.100', 60322)
等待客户端的连接...
#客户端
hi
Mon Aug 3 09:13:52 2020 hi
hello
Mon Aug 3 09:13:56 2020 hello
创建UDP服务器的伪代码
ss = socket() #创建服务器套接字
ss.bind() #绑定服务器套接字
inf_loop: #服务器无限循环
cs = ss.recvfrom()/ss.sendto() #关闭(接收/发送)
ss.close() #关闭服务器套接字
除了普通的创建套接字并绑定到本地地址外,并没有额外的工作。
#tsUserv.py OS:Linux
#!/usr/bin/python3
from socket import *
from time import ctime
host = ''
port = 21567
bufsiz = 1024
addr = (host,port)
udpSerSock = socket(AF_INET,SOCK_DGRAM)
udpSerSock.bind(addr)
while True:
print('等待数据报...')
data,addr = udpSerSock.recvfrom(bufsiz)
udpSerSock.sendto(((ctime() + "\t ").encode('utf-8') + data), addr)
print('...收到的数据来自:',addr)
创建UDP客户端伪代码
cs = socket() #创建客户端套接字
comm_loop: #通信循环
cs.sendto()/cs.recvfrom() #对话(发送/接收)
cs.close() #关闭客户端套接字
#tsUclnt.py OS:Windows
from socket import *
host = '10.0.0.246'
port = 21567
bufsiz = 1024
addr = (host,port)
udpCliSock = socket(AF_INET,SOCK_DGRAM)
while True:
data = input('> ')
if not data:
break
udpCliSock.sendto(data.encode('utf-8'),addr)
data,addr = udpCliSock.recvfrom(bufsiz)
if not data:
break
print(data.decode('utf-8'))
运行结果:
#服务器端
[root@li python]# ./tsUserv.py
等待数据报...
...收到的数据来自: ('10.0.0.100', 64916)
等待数据报...
...收到的数据来自: ('10.0.0.100', 64916)
等待数据报...
#客户端
> hi
Mon Aug 3 09:37:09 2020 hi
> hello
Mon Aug 3 09:37:19 2020 hello
属性名称 | 说明 |
---|---|
数据属性 | |
AF_UNIX、AF_INET、AF_INET6、AF_NETLINK、AF_TIPC | Pyhton中支持的套接字地址家族 |
SO_STREAM、SO_DGRAM | 套接字类型(TCP=流,UDP=数据报) |
has_ipv6 | 指示是否支持ipv6的布尔标识 |
异常 | |
error | 套接字相关错误 |
herror | 主机与地址相关错误 |
gaierror | 地址相关错误 |
timeout | 超时 |
函数 | |
socket() | 以给定的地址家族、套接字类型和协议类型(可选)创建一个套接字 |
socketpair() | 以给定的地址家族、套接字类型和协议类型(可选)创建一对套接字 |
create_connection() | 常规函数,它接收一个地址(主机名,端口号)对,返回套接字对象 |
fromfd() | 以一个打开的文件描述符创建一个套接字对象 |
ssl() | 通过套接字启动一个安全套接字层连接;不执行证书验证 |
getaddrinfo() | 获取一个五元组序列形式的地址信息 |
getnameinfo() | 给定一个套接字地址,返回(主机名,端口号)二元组 |
getfqdn() | 返回完整的域名 |
gethostname() | 返回当前主机名 |
gethostbyname() | 将一个主机名映射到它的IP地址 |
gethostbyname_ex() | gethostname()的扩展版本,它返回主机名、别名主机集合和IP地址列表 |
gethostbyaddr() | 将一个IP地址映射到DNS信息;返回与gethostbyname_ex()相同的3元组 |
getprotobyname() | 将一个协议名映射到数字 |
getservbyname()/getservbyport() | 将一个服务名映射到端口号,或者反过来;对于任何一个函数来说,协议名都是可选的 |
ntohl()/ntohs() | 将来自网络的整数转换为主机字节顺序 |
htonl()/htons() | 将来自主机的整数转换为系网络字节顺序 |
inet_aton()/inet_ntoa() | 将IP地址八进制字符串转换为32位的包格式,或者反过来 |
inet_pton()/inet_ntop() | 将IP地址字符串转换成打包的二进制格式,或者反过来 |
getfaulttimeout()/setdefaulttimeout() | 以秒(浮点数)为单位返回默认套接字超时时间;以秒(浮点数)为单位设置默认套接字超时时间 |
socketserver是标准库中的一个高级模块,它的目的是简化很多样板代码。他们是创建网络客户端和服务器所必需的代码。使用类来编写程序,应用程序是事件驱动的,这意味着只有在系统中的事件发生时,它们才会工作。
类 | 说明 |
---|---|
BaseServer | 仅用于推导,这样不会创建这个类的实例,可以用TCPServer和UDPServer创建类的实例 |
TCPServer/UDPServer | 基础的网络同步TCP/UDP服务器 |
UnixStreamServer/UnixDatagramServer | 基于文件同步的TCP/UDP服务器 |
ForkingMixIn/ThreadingMixIn | 核心派出或线程功能,不能直接实例化这个类 |
ForkingTCPServer/ForkingUDPServer | ForkingMixIn和TCPServer/UDPServer的组合 |
ThreadingTCPServer/ThreadingUDPServer | ThreadingMixIn和TCPServer/UDPServer的组合 |
BaseRequestHandler | 包含处理服务请求的核心功能;仅仅用于推导,不能实例化这个类 |
StreamRequestHandler/DatagramRequestHandler | 实现TCP/UDP服务器的服务处理器 |
事件包括消息的发送与接收。类只定义了一个用来接收客户端消息的事件处理程序。在原始的服务器循环中,我们阻塞等待请求,当接收到请求时就对其提供服务,然后继续等待。在此处的服务器循环中,并非在服务器中创建代码,而是定义一个处理程序,这样当服务器接收到一个传入的请求时,服务器就可以调用你的函数。
首先导入服务器类,然后定义与之前相同的主机常量。其次是请求处理程序类,最后启动它。
#!/usr/bin/python3
from socketserver import (TCPServer as TCP,StreamRequestHandler as SRH)
from time import ctime
host = ''
port = 21567
addr = (host,port)
class MyRequestHandler(SRH): #继承 StreamRequestHandler 类
def handle(self) -> None: #重写 handle() 方法
print('...conncected from:',self.client_address)
self.wfile.write(ctime().decode('utf-8'),self.rfile.readline())
tcpServ = TCP(addr,MyRequestHandler)
print("waiting for connection...")
tcpServ.serve_forever()
from socket import *
host = '10.0.0.246'
port = 21567
bufsize = 1024
addr = (host,port)
while True:
tcpCliSock = socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(addr)
data = input()
if not data:
break
tcpCliSock.send(data.encode('utf-8'))
data = tcpCliSock.recv(bufsize)
if not data:
break
print(data.decode('utf-8'))
tcpCliSock.close()
文章浏览阅读187次。Spring Cloud Alibaba Seata处理分布式事务基于分布式的事务管理前言这篇博客我是不想发的,因为这里面的事务回滚功能,并没有完美实现,如果你看到这篇博客的话,就当做了解即可,以后有了解决方案的话,我再更新。分布式事务分布式之前,单机单库没有这个问题,从 1:1 -> 1:N -> N:N跨数据库,多数据源的统一调度,就会遇到分布式事务问题如下图,单体应用被拆分成微服务应用,原来的三个模板被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三个_seata insert all
文章浏览阅读468次。PHP isset函数作用isset函数是检测变量是否设置。格式:bool isset( mixed var [, mixed var [, ...]] )返回值:若变量不存在则返回FALSE若变量存在且其值为NULL,也返回FALSE若变量存在且值不为NULL,则返回TURE同时检查多个变量时,每个单项都符号上一条要求时才返回TRUE,否则_php empty和isset同时使用
文章浏览阅读5.4k次,点赞2次,收藏6次。在OpenGL的shader编程中,最常用的可能就是程序对象了,GLSL的程序对象的创建、加载shader等步骤都是固定的,如果每次都写同样的代码,觉得十分浪费时间,所以现在就将我在Shader学习过程中自己封装的GLSLProgram类奉献出来供大家参考:头文件如下:/* * GLSLProgram.h * * Created * Author: zhouxuguang *_c++ glsl
文章浏览阅读1.1k次。2022史上最全java面试题题库大全800题含答案1、meta标签的作用是什么2、ReenTrantLock可重入锁(和synchronized的区别)总结3、Spring中的自动装配有哪些限制?4、什么是可变参数?5、什么是领域模型(domain model)?贫血模型(anaemic domain model)和充血模型(rich domain model)有什么区别?6、说说http,https协议7、"= ="和equals方法究竟有什么区别?8、&和..._面试题库精选800题
文章浏览阅读604次。BlueZ 做为 linux 标准的协议栈,提供非常多的 profile ,各种的支持,ble , 蓝牙网络,文件传输,a2dp 音频传输。A2DP——Advanced Audio Distribution Profile A2DP Sink 输入设备,如蓝牙耳机。A2DP Source 输出设备,如手机。音频压缩格式支持:其中SBC是必须支持的,可选的格式有MPEG-1, M..._bluez avrcp
文章浏览阅读544次。核心动画,开发人员可以为他们的应用创建动态用户界面,而无需使用低级别的图形 API,如 OpenGL 来获取高效的动画性能。 前言,核心动画的好处1.简单易用的高性能混合编程模型。2.类似视图一样,你可以通过使用图层来创建复杂的接口。 通过是CALayer来使用更复杂的一些动画。 3.轻量级的数据结构,它可以同时显示并让上百个图层产生动画效果。 控制多个CALayer来显示动画效果 4.一套简单的_ios coreanimation catiledlayer
文章浏览阅读7.6k次,点赞15次,收藏75次。目录1.用户需求2.数据库设计3.Java后台实现3.1Mybatis对应的配置文件GoodsCollectDao.xml3.2 GoodsCollectDao实现3.3 Service接口及实现4.微信小程序实现4.1index.wxml4.2 index.wxss5.我的收藏效果1.用户需求我的收藏需求如下:1).在小程序的底部菜单中,新增“我的收藏”,显示收藏商品列表。点收藏某个商品后,跳转到商品详情2).商品详情页面,可以收藏和取消收藏。_小程序收藏功能
文章浏览阅读6k次。package com.zing;import java.math.BigDecimal;import java.text.DecimalFormat;public class Test {public static void main(String[] arg) {BigDecimal bd = new BigDecimal(123456789.3);System.out.pr_bigde 千分位分隔符
文章浏览阅读2k次,点赞5次,收藏33次。文章目录前言一、单链表是什么?二、写代码时注意点:三、代码1.每一个结点的创建2.Linklist完整代码3.代码测试前言单链表是数据结构中为非常基础的内容,所以我们也来简单学一下喽^-^一、单链表是什么?单链表是存储、操作数据的一种基本结构,类似于只能单向遍历的列表,只是连接时靠结构体依次连接。二、写代码时注意点:1.关联性:将self.head赋值给临时变量时,改变变量由self.head带来的相关属性时,会改变self.head的相关属性_数据结构头插法建立单链表python
文章浏览阅读482次。文档地址:http://www.opensource.apple.com/source/objc4/objc4-493.9/runtime/objc-internal.h/* * Copyright (c) 2009 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This f_ios中_internal.h
文章浏览阅读1.7k次。在用C#写串口相关的上位机的时候,遇到了一个问题:用串口read方法有时不能读取完整的一个字符串,总是需要多次触发接收,这样显示出来不太直观。于是改用readline方法,完美解决。_c#通过调用一次read()方法并不能确保所读取的数据就是完整一帧
文章浏览阅读491次。关于这些建议这些建议并不适用于所有的项目这些建议是基于我与3-20人的小团队项目经验总结出来的结构、可重复使用性、明晰度都是有价的——团队规模和项目规模决定了是否值得付这个价。一些建议也许公然违抗了传统的Unity开发。例如:使用专业化的组合而不是使用实例就很不像Unity的作风,价格也很高。即使看上去挺疯狂的,但我还是看到了这些建议给开发者带来了利益。 过程方面_unity游戏中的一些规范和优化建议