引入场景:客户与银行关系
银行职员负责给客户提供取钱服务,客户通过账户密码跟银行职员建立合作关系。此时银行职员就可以作为服务器,当用户A取完钱后他需要等待下一个用户的接入,用户的账号密码就是建立合作关系的凭据。------简单的客户端/服务器架构模型。
客户端/服务器网络编程过程
一:创建套接字(通信端点)
AF_XXX解释:地址家族名称,AF:Address Family
基于文件套接字
AF_UNIX
基于网络套接字
AF_INET 代表ipv4 (python网络编程中常用的套接字)
AF_INET6代表ipv6
AF_NETLINK 允许使用标准的BSD套接字进行用户级别和内核级别代码之间的IPC (针对linux)
AF_TIPC 允许计算机集群之中的机器相互通信,而无需使用基于ip的寻址 (针对linux)
二:主机-端口对
主机名和端口号组合用于填充套接字,使别人能准确寻址目标。 有效的端口号为0~65535。但有的端口号已经预留给了系统,自定义端口号时不能使用这些。系统预留端口号列表
三:面向连接的套接字与无连接的套接字
面向连接的套接字(SOCK_STREAM):通信之前必须建立一个连接,SOCK_STREAM提供序列化的、可靠的和不重复的数据交付,而没偶记录边界。换句话说就是每条信息都可以被拆分为多个片段,并且每条信息片段都确保能到达目的地,最后把完整的信息传递给等待的应用程序。实现这种类型连接的主要协议是传输控制协议(TCP),因为这些套接字的网络版是基于因特网协议IP来搜寻网络主机,所以整个系统通常结合这两种协议(tcp 和ip)来进行。简单点说就是tcp为信息提供了保障,ip为网络寻址提供服务。
无连接的套接字(SOCK_DGRAM):无连接的套接字也叫数据报类型的套接字,意味在通信开始之前并不需要建立连接。此时在数据传输过程中并无法保证它的顺序性、可靠性或重复性,但数据报保留了记录边界,这就意味着消息是以整体发送的,而并非首先分个多个片段。实现这种连接类型的主要协议是用户数据报协议(UDP),网络版的寻址方式用的也是基于因特网协议IP来搜寻网络主机。所以整个系统通常以UDP/IP来进行。
通过成本决定选用哪种套接字
由于面向连接套接字所提供的保障(序列化、可靠和不重复的数据交付)所以它们的设置以及对虚拟电路连接的维护需要大量的开销,然而数据报不需要这些开销,即数据报套接字的成本较为“低廉”,因此能提供更好的性能并且适合一些类型的应用程序。但是用哪种套接字,要依赖于要完成哪些业务目标也就是依据任务来决定是用哪种套接字。
Python中使用Socket模块实现连接通信
socket函数一般语法
1 socket(socket_family(家族名称),socket_type(套接字类型),protocol=0(通常省略,默认为0))
一 :创建一个地址家族基于网络的,套接字类型为面向连接的socket,因为面向连接的套接字使用的是tcp协议,因此可以使用tcpSock命名
tcpSock=socket(AF_INET,SOCK_STREAM)
二:创建一个地址家族基于网络的,套接字类型为无连接的socket,因为无连接的套接字使用的是UDP协议,因此可以使用udpSock命名
1 udpSock=socket(AF_INET,SOCK_DGRAM)
常用套接字对象内置方法
名称 | 描述 |
服务器套接字方法 | |
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.recffrom_into() | 接收UDP消息到指定缓冲区 |
s.sendto() | 发送UDP消息 |
s.getpeername() | 连接到套接字(TCP)的远程地址 |
s.getsockname() | 当前套接字地址 |
s.getsocktop() | 返回给定套接字指定的值 |
s.setsockopt() | 设置给定套接字选项的值 |
s.shutdown() | 关闭连接 |
s.close() | 关闭套接字 |
s.ioctl() | 控制套接字的模式(仅支持windows) |
s.detach() | 在未关闭文件描述符的情况下关闭套接字,返回文件描述符 |
面向阻塞的套接字方法 | |
s.setblocking() | 设置套接字的阻塞或非阻塞模式 |
s.settimeout() | 设置阻塞套接字操作的超时时间 |
s.gettimeout() | 获取阻塞套接字操作的超时时间 |
面向文件的套接字方法 | |
s.fileno() | 套接字的文件描述符 |
s.makefile() | 创建与套接字关联的文件对象 |
数据属性 | |
s.family | 套接字家族 |
s.type | 套接字类型 |
s.proto | 套接字协议 |
创建TCP服务器端和客户端使其通信,并在服务器端向客户端返回通信时间。(单线程版)
服务器端
1 from socket import * 2 HOST='' 3 PORT=21567 4 BUFSIZ=1024 5 ADDR=(HOST,PORT) 6 tcpSerSock=socket(AF_INET,SOCK_STREAM) 7 tcpSerSock.bind(ADDR) 8 tcpSerSock.listen(5) 9 while True: 10 tcpClConn, addr = tcpSerSock.accept() 11 while True: 12 ret=tcpClConn.recv(BUFSIZ) 13 if ret==b'q':break 14 print(ret.decode('utf-8')) 15 msg = input('>>>').encode('utf-8') 16 tcpClConn.send(msg) 17 if msg.lower() == b'q': break 18 tcpClConn.close() 19 tcpSerSock.close()
客户端
1 from socket import * 2 HOST='127.0.0.1' 3 PORT=21567 4 BUFSIZ=1024 5 ADDR=(HOST,PORT) 6 tcpCliSock=socket(AF_INET,SOCK_STREAM) 7 tcpCliSock.connect(ADDR) 8 while True: 9 msg=input('>>>').encode('utf-8') 10 tcpCliSock.send(msg) 11 ret = tcpCliSock.recv(BUFSIZ) 12 if msg.lower()==b'q':break 13 if ret.lower()==b'q':break 14 print(ret.decode('utf-8')) 15 tcpCliSock.close()