1、单机架构python
应用领域:linux
2、CS架构面试
应用领域:数据库
计算机发展初期用户去取数据,直接就去主机拿,从这里开始就分出了客户端和服务端。编程
客户端:用户安装的软件;设计模式
服务端:统一管理数据库的主机中的软件就叫作服务端,再后来服务端不仅是管理数据,外加处理业务逻辑。浏览器
2.1 CS架构要求缓存
2.2 面试题:数据放在服务端和客户端的利与弊?安全
答:服务器
3、BS架构
应用领域:
统一客户端即默认安装用户电脑中的浏览器,访问同种类的网站,具体业务的处理根据相应协议和标准提供通用的服务器程序,在不一样的服务器处理。
3.1 两种BS架构
OSI主要用于教学(万恶的大学、绿本的计算机书),咱们在编程的时候用的都是TCP/IP。
TCP/IP的对应关系,就像咱们在淘宝购物,所在位置有的快递(网络接入层),告诉卖家地址(网络互联层)、快递送货(运输层)、收到货物拆包使用(应用层)。
注意:对于普遍使用的东西就须要制定相应的标准,就像大公司有不少制度来规范作事情的流程。因为网络传输应用很是普遍,可是规矩不是强制性的,因此叫作协议而不是标准,TCP/IP参考模型也能够看作是一种协议。BS结构中TCP/IP模型中的网络接入层没有响应的协议,网络互联层是IP协议,传输层是TCP协议,应用层是HTTP协议,另外仍是用到了DNS结构,并且在HTTP上层还有相应。
基于BS结构下的程序就要求解决速度问题,而速度问题的核心就是解决海量数据操做和高并发问题,网站复杂架构就是从这两个问题演变出来的。
4、CS架构和BS架构区别
互联网的本质就是一系列的网络协议,这个协议就叫OSI协议(一系列协议),按照功能不一样,分工不一样,人为的分层七层。实际上这个七层是不存在的。没有这七层的概念,只是人为的划分而已。区分出来的目的只是让你明白哪一层是干什么用的。
每一层都运行不一样的协议。协议是干什么的,协议就是标准。
实际上还有人把它划成五层、四层。
七层划分为:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
五层划分为:应用层、传输层、网络层、数据链路层、物理层。
四层划分为:应用层、传输层、网络层、网络接口层。
每层运行常见的物理设备
1、物理层
物理层功能:主要是基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0
物理层字面意思解释:物理传输、硬件、物理特性。在深圳的你与北京的朋友聊天,你的电脑必需要能上网,物理体现是什么?是否是接一根网线,插个路由器,北京的朋友那边是否是也有根网线,也得插个路由器。也就是说计算机与计算机之间的通讯,必需要有底层物理层方面的连通,就相似于你打电话,中间是否是必须得连电话线。
中间的物理连接能够是光缆、电缆、双绞线、无线电波。中间传的是电信号,即010101...这些二进制位。
底层传输的010010101001...这些二进制位怎么才能让它有意义呢?
要让这些010010101001...有意思,人为的分组再适合不过了,8位一组,发送及接收都按照8位一组来划分。接收到8位为一组的话,那么就能够按照这8位数来作运算。若是没有分组,对方接收的计算机根本就不知道从哪一位开始来作计算,也解析不了收到的数据。我发了16位你就按照16位来作计算吗?我发100位你就按照100位作计算吗?没什么意义是吧。所以要想让底层的电信号有意义,必需要把底层的电信号作分组。我作好8位一组,那么我收到数据,我就知道这几个8位作一组,这几个8位作一组。那么每一个8位就能够获得一个肯定的数。分组是谁干的活呢?物理层干不了,这个是数据链路层干的。
2、数据链路层
数据链路层由来:单纯的电信号0和1没有任何意义,必须规定电信号多少位一组,每组什么意思
数据链路层的功能:定义了电信号的分组方式
2.1 以太网协议
早期的时候,数据链路层就是来对电信号来作分组的。之前每一个公司都有本身的分组方式,后来造成了统一的标准,即以太网协议ethernet
ethernet规定:一组电信号构成一个数据报,叫作'帧',每一数据帧分红:报头head和数据data两部分
- | - |
---|---|
head | data |
这就像写信,发送者的地址(源地址)就是你家的地址,接收者地址(目标地址)就是对方的收信地址,你家的路由器就至关于邮局。其实在计算机通讯中的源地址和目标地址指的是Mac地址。
2.2 Mac地址
head中包含的源和目标地址由来:ethernet规定接入internet的设备都必须具有网卡,发送端和接收端的地址即是指网卡的地址,即Mac地址
2.3 广播地址
有了Mac地址之后,计算机就能够通讯了,假设一个教室就是一个局域网(隔离的网络),这个教室里面有几台计算机,计算机的通讯和人的通讯是一个道理,把教室里面的人都比做一个个计算机,假设教室里面的人都是瞎子,其实计算机就是瞎子的,计算机通讯基本靠吼,如今我要找教室里面的飞哥要战狼2的片,而后我就吼一声,说我要找飞哥要战狼2的片,战狼2的片就属于个人数据,可是我在发的时候我是否是要标识我是谁,我要找谁,我是谁就是个人Mac地址,我要找谁就是飞哥的Mac地址,这两个地址作数据包的头部,再加上数据战狼2的片就构成了一个数据帧。
这个数据包封装好之后就往外发,到物理层之后就所有转成二进制,往外发是怎么发的呢?就是靠吼。即“我是nick,我找飞哥要战狼2的片”。这么吼了一嗓子之后,全屋子的人都能听到,这就是广播。
计算机底层,只要在一个教室里(一个局域网),都是靠广播的方式,吼。
广播出去之后,全部人都听得见,全部人都会拆开这个包,读发送者是谁,接收者是谁,只要接收者不是本身就丢弃掉。对计算机来讲,它会看本身的Mac地址,飞哥收到之后,他就会把片发给我,发送回来一样采用广播的方式了,靠吼。
同一个教室(同一个局域网)的计算机靠吼来通讯,那不一样教室的计算机又如何?
好比说局域网1的pc1与局域网2的pc10如何通讯?你在教室1(局域网1)吼,教室2(局域网2)的人确定是听不见的。这就是跨网络进行通讯,数据链路层就解决不了这个问题了,这就得靠网络层出面了。
注意:在讲网络层以前,其实基于广播的这种通讯就能够实现全世界通讯了,你吼一声,若是全世界是一个局域网,全世界的计算机确定能够听得见,从理论上彷佛行得通,若是全世界的计算机都在吼,你想想,这是否是一个灾难。所以,全世界不能是一个局域网。因而就有了网络层。
3、网络层
网络层功能:引入一套新的地址用来区分不一样的广播域/子网,这套地址即网络地址
网络层的由来:有了ethernet、Mac地址、广播的发送方式,世界上的计算机就能够彼此通讯了,问题是世界范围的互联网是由 一个个彼此隔离的小的局域网组成的,那么若是全部的通讯都采用以太网的广播方式,那么一台机器发送的包全世界都会收到
为了解决上述灾难,网络层定义了一个IP协议,
你想,我是这个教室的一个学生,我想找隔壁教室一个叫老王的学生,我也不认识老王,那怎么办,我吼?老王在另一个教室确定是听不到的。找教室的负责人,这个教室的负责人就负责和隔壁教室的负责人说话,说咱们教室的有个学生要找大家教室的老王。往外传的东西交给负责人就能够了,内部的话上面已经提到,经过广播的方式,对外的东西广播失效。教室的负责人就是网关,网关即网络关口的意思。
Mac地址是用来标识你这个教室的某个位置,IP地址是用来标识你在哪一个教室(哪一个局域网)。你要跨网络发包你是否是要知道对方的IP地址,好比你要访问百度,你确定得知道百度服务器的IP地址。计算机在发包前,会判断你在哪一个教室,对方在哪一个教室,若是在一个教室,基于Mac地址的广播发包就OK了;若是不在一个教室,即跨网络发包,那么就会把你的包交给教室负责人(网关)来转发。Mac地址及IP地址惟一标识了你在互联网中的位置。
数据链路层中会把网络层的数据包封装到数数据链路层的数据位置,而后再添加上本身的包头,再发给物理层,物理层发给网关,网关再发给对方教室的网关,对方教室的网关收到后在那个教室作广播。
在数据链路层看,数据封装了两层,跟玩俄罗斯套娃有点相似,一层套了一层。
最终变成
- | - | - |
---|---|---|
以太网头 | IP头 | IP数据 |
如今来看另外一个问题,在吼以前怎么知道对方的Mac地址?这就得靠ARP协议。
ARP协议的由来:在你找飞哥要片以前,你的先干一件事,想办法知道飞哥的Mac地址。即你的机器必须先发一个ARP包出去,ARP也是靠广播的方式发,ARP发送广播包的方式以下:
- | 源Mac | 目标Mac | 源IP | 目标IP | 数据部分 |
---|---|---|---|---|---|
发送端主机 | 发送端Mac | FF:FF:FF:FF:FF:FF | 172.16.10.10/24 | 172.16.10.11/24 | 数据 |
局域网中怎么获取对方的Mac地址:
确定要知道对方的IP地址,这是最基本的,就像你要访问百度,确定得知道百度的域名,域名就是百度的IP地址。本身的IP能够轻松得到,本身的Mac也轻松获取,目标Mac为12个F,咱们叫广播地址,表达的意思是我想要获取这个目标IP地址172.16.10.11的机器的Mac地址。Mac为12个F表明的是一种功能,这个功能就是获取对方的Mac地址,计算机的Mac永远不多是12个F。假设是在本教室广播,一嗓子吼出去了,全部人开始解包,只有IP地址是172.16.10.11的这我的才会返回他的Mac地址,其余人所有丢弃。发回来源Mac改为飞哥本身的Mac地址,同时把飞哥的Mac地址放在数据部分。
跨网络怎么获取对方的Mac地址:
经过IP地址区分,计算机运算判断出飞哥不在同一个教室,目标IP就变成了网关的IP了。网关的IP在计算机上配死了,能够轻松获取。
- | 源Mac | 目标Mac | 源IP | 目标IP | 数据部分 |
---|---|---|---|---|---|
发送端主机 | 发送端Mac | FF:FF:FF:FF:FF:FF | 172.16.10.10/24 | 172.16.10.11/24 | 数据 |
- | 源Mac | 目标Mac | 源IP | 目标IP | 数据部分 |
---|---|---|---|---|---|
发送端主机 | 发送端Mac | FF:FF:FF:FF:FF:FF | 172.16.10.10/24 | 网关地址 | 数据 |
- | 源Mac | 目标Mac | 源IP | 目标IP | 数据部分 |
---|---|---|---|---|---|
发送端主机 | 发送端Mac | 网关Mac | 172.16.10.10/24 | 飞哥的IP | 数据 |
注意:网关帮你去找飞哥,但对用户来讲,因为速度太快咱们根本就感受不到网关的存在。
3.1 IP协议详解
规定网络地址的协议叫IP协议,它定义的地址称之为IP地址,普遍采用的v4版本即IPv4,它规定网络地址由32位2进制表示
范围0.0.0.0-255.255.255.255
例:172.16.10.1与172.16.10.2并不能肯定两者处于同一子网
3.2 子网掩码详解
所谓”子网掩码”,就是表示子网络特征的一个参数。它在形式上等同于IP地址,也是一个32位二进制数字,它的网络部分所有为1,主机部分所有为0。好比,IP地址172.16.10.1,若是已知网络部分是前24位,主机部分是后8位,那么子网络掩码就是11111111.11111111.11111111.00000000,写成十进制就是255.255.255.0。
知道”子网掩码”后,咱们就能判断,任意两个IP地址是否处在同一个子网络。方法是将两个IP地址与子网掩码分别进行AND运算(两个数位都为1,运算结果为1,不然为0),而后比较结果是否相同,若是是的话,就代表它们在同一个子网络中,不然就不是。
好比,已知IP地址172.16.10.1和172.16.10.2的子网掩码都是255.255.255.0,请问它们是否在同一个子网络?二者与子网掩码分别进行AND运算
总结一下,IP协议的做用主要有两个,一个是为每一台计算机分配IP地址,另外一个是肯定哪些地址在同一个子网络。
3.3 IP数据包详解
IP数据包也分为head和data部分,无须为IP包定义单独的栏位,直接放入以太网包的data部分
注意:以太网数据包的"数据"部分,最长只有1500字节。所以,若是IP数据包超过了1500字节,它就须要分割成几个以太网数据包,分开发送了。
- | - | - |
---|---|---|
以太网头 | IP头 | IP数据 |
有了Mac地址+IP地址,咱们就能肯定世界上独一无二的一台计算机。
3.4 ARP协议详解
arp协议由来:计算机通讯基本靠吼,即广播的方式,全部上层的包到最后都要封装上以太网头,而后经过以太网协议发送,在谈及以太网协议时候,我门了解到:通讯是基于Mac的广播方式实现,计算机在发包时,获取自身的Mac是容易的,如何获取目标主机的Mac,就须要经过arp协议
arp协议功能:广播的方式发送数据包,获取目标主机的Mac地址
协议工做方式:每台主机IP都是已知的,例如:主机172.16.10.10/24访问172.16.10.11/24
1.首先经过IP地址和子网掩码区分出本身所处的子网
场景 | 数据包地址 |
---|---|
同一子网 | 目标主机Mac,目标主机IP |
不一样子网 | 网关Mac,目标主机IP |
2.分析172.16.10.10/24与172.16.10.11/24处于同一网络(若是不是同一网络,那么下表中目标IP为172.16.10.1,经过arp获取的是网关的Mac)
- | 源Mac | 目标Mac | 源IP | 目标IP | 数据部分 |
---|---|---|---|---|---|
发送端主机 | 发送端Mac | FF:FF:FF:FF:FF:FF | 172.16.10.10/24 | 172.16.10.11/24 | 数据 |
3.这个包会以广播的方式在发送端所处的自网内传输,全部主机接收后拆开包,发现目标IP为本身的,就响应,返回本身的Mac
4、传输层
传输层的由来:网络层的IP帮咱们区分子网,以太网层的Mac帮咱们找到主机,而后你们使用的都是应用程序,你的电脑上可能同时开启qq,暴风影音,等多个应用程序。
那么咱们经过IP和Mac找到了一台特定的主机,如何标识这台主机上的应用程序,答案就是端口,端口即应用程序与网卡关联的编号。
传输层功能:创建端口到端口的通讯
补充:端口范围0-65535,0-1023为系统占用端口
4.1 TCP协议
- | - | - | - |
---|---|---|---|
以太网头 | IP头 | TCP头 | 数据 |
4.2 UDP协议
- | - | - | - |
---|---|---|---|
以太网头 | IP头 | UDP头 | 数据 |
4.3 TCP报文
4.4 TCP三次握手和四次挥手
5、应用层
应用层由来:用户使用的都是应用程序,均工做于应用层,互联网是开发的,你们均可以开发本身的应用程序,数据多种多样,必须规定好数据的组织形式
应用层功能:规定应用程序的数据格式。
注意:数据通过以上几层的折腾,已经不成样子了。
1、背景描述
经过OSI七层网络模型中IP层的介绍,咱们知道网络层,能够实现两个主机之间的通讯。可是这并不具体,由于,真正进行通讯的实体是在主机中的进程,是一个主机中的一个进程与另一个主机中的一个进程在交换数据。IP协议虽然能把数据报文送到目的主机,可是并无交付给主机的具体应用进程。而端到端的通讯才应该是应用进程之间的通讯。
UDP,在传送数据前不须要先创建链接,远地的主机在收到UDP报文后也不须要给出任何确认。虽然UDP不提供可靠交付,可是正是由于这样,省去和不少的开销,使得它的速度比较快,好比一些对实时性要求较高的服务,就经常使用的是UDP。对应的应用层的协议主要有 DNS,TFTP,DHCP,SNMP,NFS 等。
TCP,提供面向链接的服务,在传送数据以前必须先创建链接,数据传送完成后要释放链接。所以TCP是一种可靠的的运输服务,可是正由于这样,不可避免的增长了许多的开销,好比确认,流量控制等。对应的应用层的协议主要有 SMTP,TELNET,HTTP,FTP 等。
2、经常使用的熟知端口号
应用程序 | FTP | TFTP | TELNET | SMTP | DNS | HTTP | SSH | MYSQL |
---|---|---|---|---|---|---|---|---|
熟知端口 | 21,20 | 69 | 23 | 25 | 53 | 80 | 22 | 3306 |
传输层协议 | TCP | UDP | TCP | TCP | UDP | TCP | TCP | TCP |
3、TCP概述
TCP把链接做为最基本的对象,每一条TCP链接都有两个端点,这种端点咱们叫做套接字(socket),它的定义为端口号拼接到IP地址即构成了套接字,例如,若IP地址为192.3.4.16 而端口号为80,那么获得的套接字为192.3.4.16:80。
4、TCP报文首部
5、TCP链接的创建(三次握手)
6、TCP四次挥手
7、面试题
7.1 为何客户端最后还要等待2MSL?
MSL(Maximum Segment Lifetime),TCP容许不一样的实现能够设置不一样的MSL值。
7.2 为何创建链接是三次握手,关闭链接确是四次挥手呢?
创建链接的时候,服务器在LISTEN状态下,收到创建链接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭链接时,服务器收到对方的FIN报文时,仅仅表示对方再也不发送数据了可是还能接收数据,而本身也未必所有数据都发送给对方了,因此己方能够当即关闭,也能够发送一些数据给对方后,再发送FIN报文给对方来表示赞成如今关闭链接,所以,己方ACK和FIN通常都会分开发送,从而致使多了一次。
7.3 若是已经创建了链接,可是客户端忽然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端若是出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会从新复位这个计时器,时间一般是设置为2小时,若两小时尚未收到客户端的任何数据,服务器就会发送一个探测报文段,之后每隔75秒发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭链接。
1、什么是Scoket
Socket是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来讲,一组简单的接口就是所有,让Socket去组织数据,以符合指定的协议。
因此,咱们无需深刻理解tcp/udp协议,socket已经为咱们封装好了,咱们只须要遵循socket的规定去编程,写出的程序天然就是遵循tcp/udp标准的。
2、套接字发展史及分类
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 所以,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通信。这也被称进程间通信,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
2.1 基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,能够经过访问同一个文件系统间接完成通讯
2.2 基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其余的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是不多被使用,或者是根本没有实现,全部地址家族中,AF_INET是使用最普遍的一个,python支持不少种地址家族,可是因为咱们只关心网络编程,因此大部分时候我么只使用AF_INET)
3、套接字工做流程
一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就创建起了链接,就能够讲话了。等交流结束,挂断电话结束这次交谈。 生活中的场景就解释了这工做原理。
先从服务器端提及。服务器端先初始化Socket,而后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端链接。在这时若是有个客户端初始化一个Socket,而后链接服务器(connect),若是链接成功,这时客户端与服务器端的链接就创建了。客户端发送数据请求,服务器端接收请求并处理请求,而后把回应数据发送给客户端,客户端读取数据,最后关闭链接,一次交互结束,使用如下Python代码实现:
import socket # socket_family 能够是 AF_UNIX 或 AF_INET。socket_type 能够是 SOCK_STREAM 或 SOCK_DGRAM。protocol 通常不填,默认值为 0 socket.socket(socket_family, socket_type, protocal=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 module import *'语句。使用 'from socket import *',咱们就把 socket 模块里的全部属性都带到咱们的命名空间里了,这样能大幅减短咱们的代码 tcpSock = socket(AF_INET, SOCK_STREAM)
3.1 服务端套接字函数
方法 | 用途 |
---|---|
s.bind() | 绑定(主机,端口号)到套接字 |
s.listen() | 开始TCP监听 |
s.accept() | 被动接受TCP客户的链接,(阻塞式)等待链接的到来 |
3.2 客户端套接字函数
方法 | 用途 |
---|---|
s.connect() | 主动初始化TCP服务器链接 |
s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 |
3.3 公共用途的套接字函数
方法 | 用途 |
---|---|
s.recv() | 接收TCP数据 |
s.send() | 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完) |
s.sendall() | 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完) |
s.recvfrom() | 接收UDP数据 |
s.sendto() | 发送UDP数据 |
s.getpeername() | 链接到当前套接字的远端的地址 |
s.getsockname() | 当前套接字的地址 |
s.getsockopt() | 返回指定套接字的参数 |
s.setsockopt() | 设置指定套接字的参数 |
s.close() | 关闭套接字 |
3.4 面向锁的套接字方法
方法 | 用途 |
---|---|
s.setblocking() | 设置套接字的阻塞与非阻塞模式 |
s.settimeout() | 设置阻塞套接字操做的超时时间 |
s.gettimeout() | 获得阻塞套接字操做的超时时间 |
3.5 面向文件的套接字的函数
方法 | 用途 |
---|---|
s.fileno() | 套接字的文件描述符 |
s.makefile() | 建立一个与该套接字相关的文件 |
4、基于TCP协议的套接字编程(简单)
netstat -an | findstr 8080
查看套接字状态4.1 服务端
import socket #一、买手机 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #tcp称为流式协议,udp称为数据报协议SOCK_DGRAM # print(phone) #二、插入/绑定手机卡 # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1', 8081)) #三、开机 phone.listen(5) # 半链接池,限制的是请求数 #四、等待电话链接 print('start....') conn, client_addr = phone.accept() #(三次握手创建的双向链接,(客户端的ip,端口)) print(conn) print(client_addr) #五、通讯:收\发消息 data = conn.recv(1024) #最大接收的字节数 print('来自客户端的数据', data) conn.send(data.upper()) # import time # time.sleep(500) #六、挂掉电话链接 conn.close() #七、关机 phone.close()
4.2 客户端
import socket # socket.AF #一、买手机 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print(phone) #二、拨电话 phone.connect(('127.0.0.1', 8081)) # 指定服务端ip和端口 #三、通讯:发\收消息 phone.send('hello'.encode('utf-8')) # phone.send(bytes('hello',encoding='utf-8')) data = phone.recv(1024) print(data) # import time # time.sleep(500) #四、关闭 phone.close()
5、基于TCP协议的套接字编程(循环)
5.1 服务端
import socket #一、买手机 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #tcp称为流式协议,udp称为数据报协议SOCK_DGRAM # print(phone) #二、插入/绑定手机卡 # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1', 8080)) #三、开机 phone.listen(5) # 半链接池,限制的是请求数 #四、等待电话链接 print('start....') while True: # 链接循环 conn, client_addr = phone.accept() #(三次握手创建的双向链接,(客户端的ip,端口)) # print(conn) print('已经有一个链接创建成功', client_addr) #五、通讯:收\发消息 while True: # 通讯循环 try: print('服务端正在收数据...') data = conn.recv(1024) #最大接收的字节数,没有数据会在原地一直等待收,即发送者发送的数据量必须>0bytes # print('===>') if len(data) == 0: break #在客户端单方面断开链接,服务端才会出现收空数据的状况 print('来自客户端的数据', data) conn.send(data.upper()) except ConnectionResetError: break #六、挂掉电话链接 conn.close() #七、关机 phone.close()
5.2 客户端1
import socket #一、买手机 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # print(phone) #二、拨电话 phone.connect(('127.0.0.1', 8080)) # 指定服务端ip和端口 #三、通讯:发\收消息 while True: # 通讯循环 msg = input('>>: ').strip() #msg='' if len(msg) == 0: continue phone.send(msg.encode('utf-8')) # print('has send----->') data = phone.recv(1024) # print('has recv----->') print(data) #四、关闭 phone.close()
5.3 客户端2
import socket #一、买手机 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # print(phone) #二、拨电话 phone.connect(('127.0.0.1', 8080)) # 指定服务端ip和端口 #三、通讯:发\收消息 while True: # 通讯循环 msg = input('>>: ').strip() phone.send(msg.encode('utf-8')) data = phone.recv(1024) print(data) #四、关闭 phone.close()
6、地址占用问题
有的同窗在重启服务端时可能会遇到:
这个是因为你的服务端仍然存在四次挥手的time_wait状态在占用地址(若是不懂,请深刻研究1.tcp三次握手,四次挥手 2.syn洪水攻击 3.服务器高并发状况下会有大量的time_wait状态的优化方法)
6.1 方法一
# 加入一条socket配置,重用ip和端口 phone=socket(AF_INET,SOCK_STREAM) phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 phone.bind(('127.0.0.1',8080))
6.2 方法二(Linux)
发现系统存在大量TIME_WAIT状态的链接,经过调整linux内核参数解决, vi /etc/sysctl.conf 编辑文件,加入如下内容: net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 30 而后执行 /sbin/sysctl -p 让参数生效。 net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少许SYN攻击,默认为0,表示关闭; net.ipv4.tcp_tw_reuse = 1 表示开启重用。容许将TIME-WAIT sockets从新用于新的TCP链接,默认为0,表示关闭; net.ipv4.tcp_tw_recycle = 1 表示开启TCP链接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。 net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间