""" 一、客户端/服务端架构: 1、硬件C/S架构(打印机) 2、软件C/S架构 互联网中处处都是CS架构,服务端与客户端 CS架构与socket的关系 - 我们学习socket就是为了完成C/S架构的开发 二、OSI七层模型: 一个完整的计算机系统是由硬件、操作系统、应用软件三者组成,一台计算机只能自己玩自己,要想跟别人玩就需要上网了 互联网的核心就是由一堆协议组成,协议就是标准,比如全世界人通信的标准是英语; 如果把计算机比做人,那么互联网协议就是计算机界的英语了,如果所有的计算机都学会了这门英语,那么就可以互相通信了 为何学习socket一定要先学习互联网协议: - 1、首先我们的目标是基于socket编程,来开发一款自己的cs架构软件 - 2、其次cs架构软件(软件属于应用层)是基于网络进行通信的 - 3、网络的核心即一堆协议,协议即标准,你想开发一款基于网络通信的软件,就必须遵循这些标准 三、socket层 socket层位于运输层和应用层之间,即socket抽象层 四、socket是什么? socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,socket其实就是一个门面模式,它吧复杂的 TCP/IP协议族隐藏在了socket接口的后面,对用户来说,一组简单的接口就是全部,让socket去组织数据,以符合指定的协议 所以我们无需深入理解tcp/udp协议,socket已经帮我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然符合标准 - 也有人将socket说成是IP+port,IP是用来标识互联网中的一台主机的位置,而port是用来标识这台主机上的一个应用程序,IP地址 配置到网卡上的,而port是应用程序开启的,IP与port的绑定就标识了互联网中独一无二的应用程序 程序的pid是同一台机器上不同进程或者线程的标识 五、套接字的分类: - 基于文件类型的套接字家族 套接字家族的名字:AF_UNIX unix一切皆文件,基于文件的套接字调用的就是底层文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个 间接完成通信。 - 基于网络类型的套接字家族 套接字家族的名字:AF_INET ···所有的地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以我们 大部分时间都只是使用AF_INET 六、套接字的工作流程: 一个生活中的场景:你要给你前对象打电话,先拨号,前对象听到电话铃声后拿起电话,这时候你俩就建立起来了不正当的链接,然后 就可以进行不正当的交易了。交易完成后,挂断电话结束此次交谈; 先从服务端说起,服务端先初始化socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端链接 在这个时候如果有客户端初始化一个socket,然后链接服务器(connect),如果链接成功,这时客户端与服务端的链接就建立了。 客户端发送数据请求,服务端接收请求并处理,然后把回应数据发送给客户端,客户端读取数据,关闭链接,交互结束 - socket函数的用法 import socket socket.socket(socket_family,socket_type,protocal=0) socket_family:可以是AF_UNIX或者是AF_INET;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) 由于socket模块中有太多的属性,我们直接在这里使用 from xxx import * 的语句导入,这样就可以减少代码量 eg:tcpsock = socket(AF_INET,SOCK_STREAM) - 服务端套接字函数 s.bind()绑定(主机,端口号)到套接字 s.listen()开始tcp监听 s.accept()被动接受tcp客户的连接,(阻塞式)等待连接的到来 - 客户端套接字函数 c.connect()被动初始化tcp服务器连接 c.connect_ex() connect()函数的扩展版本,出错时返回错误码,而不是抛出异常 - 公共用途的套接字函数 s.recv()接受tcp数据 s.send()发送tcp数据(send在待发送数据量大于已缓存区剩余的空间时,数据丢失,不会发完) s.sendall()发送完整的tcp数据(本质就是循环send,···数据不会丢失) s.recvfrom()接受udp数据 s.sendto()发送udp数据 s.getpeername()链接到当前套接字的远程地址 s.getsockname()当前套接字的地址 s.getsockopt()返回指定套接字的参数 s.setsockopt()设置指定套接字的参数 s.close()关闭套接字链接 - 面向锁的套接字方法 s.setblocking()设置套接字的阻塞与非阻塞模式 s.settimeout()设置阻塞套接字操作的超时时间 s.gettimeout()得到阻塞套接字操作的超时时间 - 面向文件的套接字函数 s.fileno()套接字的文件描述符 s.makefile()创建一个与该套接字相关的文件 七、基于tcp的套接字 tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端 - tcp服务端: server = socket() #创建服务器套接字 server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) server.bind() #把地址绑定到套接字 server.listen() #监听链接 while true: #服务器无限循环 client,addr = server.accept() #接受客户端链接 while True: #通信循环 client.recv()/client.send() #接收与发送数据 client.close() #关闭客户端套接字 server.close() #关闭服务器套接字 - tcp客户端: client = socket() #创建客户端套接字 client.connect() #尝试链接服务器 while True: client.send()/client.recv() #发送/接收数据 client.close() #关闭客户端套接字 八、基于udp的套接字 - udp服务端 server = socket() #创建服务器套接字 server.bind() #绑定套接字地址 while True: #服务器无线循环 client,addr = server.recvfrom()/server.sendto() #接收与发送数据 server.close() #关闭服务器套接字 - udp客户端 client = socket() #创建客户端套接字 while True: #通信循环 client.sendto()/client.recvfrom() #发送与接收数据 client.close() #关闭客户端套接字 九、粘包现象(了解) 如果当我们用socket模拟cmd执行远程命令时,有些数据太长,超过了1024个字节,一次接受不完,就会造成粘包 - 什么是粘包? 只有tcp才有粘包现象,udp永远不会粘包,因为udp是基于流收发数据的 所谓粘包的问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少数据造成的 - 粘包的解决 - struct模块 - 自定义报头 - ··· - 什么时候才会发生粘包? 发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据很小会喝到一起,产生粘包) 接收方不及时接收缓冲区的包,造成多个包接收产生粘包 - 了解 tcp是可靠传输,udp是不可靠传输 十、socketserver模块 - 终极必杀技 """