在讲网络编程以前,先跟你们简单的介绍一下一些网络相关的知识。前端
在最先以前,两台电脑之间通讯是经过电脑的mac地址找到对方,并实现相互通讯。固然每台电脑都只存在惟一的mac地址,在生产时就已经固定了。后来慢慢的想用一个相似编号的来代替mac地址,也是后来的ip地址。ip地址能够看出是由四个点分十进制构成,因为一个占8位,最大为255,因此ip地址中最大能够表示为255.255.255.255,最小为0.0.0.0。固然这是ipv4,随着如今愈来愈多的计算机,因此出现了ipv6,让更多的计算机能够分配到本身固有的ip地址。ipv6跟ipv4差很少,只是是由六个点分十进制构成的。讲到ip地址,其中127.0.0.0/8被用做回环地址,回环地址表示本机的地址,经常使用于对本机的测试,用的最多的是127.0.0.1。其中经过ip地址找到mac地址是经过Arp协议找到的。子网掩码跟你的ip地址进行按位与运算就能得出一个机器所在的网段。mysql
一个ip地址可以找到一个固体的计算机,而后端口就表示你计算机具体运行的那个程序。端口号的范围在0-65535,咱们通常选择用8000之后的端口号。一些应用默认端口号有8000-酷狗音乐 ,22-ssh ,3306-mysql,oracle-1521,redis-6379,RabbitMQ-15672等等。因此固然知道惟一的ip跟端口号,就能肯定一个计算机上的惟一应用。redis
为了使不一样计算机厂家生产的计算机可以相互通讯,以便在更大的范围内创建计算机网络,国际标准化组织(ISO)在1978年提出了“开放系统互联参考模型”,即著名的OSI/RM模型(Open System Interconnection/Reference Model)。它将计算机网络体系结构的通讯协议划分为七层,自下而上依次为:物理层(Physics Layer)、数据链路层(Data Link Layer)、网络层(Network Layer)、传输层(Transport Layer)、会话层(Session Layer)、表示层(Presentation Layer)、应用层(Application Layer)。其中第四层完成数据传送服务,上面三层面向用户。sql
TCP是面向链接的通讯协议,经过三次握手创建链接,通信完成时要拆除链接,因为TCP是面向链接的因此只能用于端到端的通信。TCP提供的是一种可靠的数据流服务,采用“带重传的确定确认”技术来实现传输的可靠性。TCP还采用一种称为“滑动窗口”的方式进行流量控制,所谓窗口实际表示接收能力,用以限制发送方的发送速度。数据库
UDP与TCP位于同一层,但它无论数据包的顺序、错误或重发。所以,UDP不被应用于那些使用虚电路的面向链接的服务,UDP主要用于那些面向查询---应答的服务,例如NFS。相对于FTP或Telnet,这些服务须要交换的信息量较小。编程
使用TCP的协议:FTP(文件传输协议)、Telnet(远程登陆协议)、SMTP(简单邮件传输协议)、POP3(和SMTP相对,用于接收邮件)、HTTP协议等。后端
使用UDP协议包括:TFTP(简单文件传输协议)、SNMP(简单网络管理协议)、DNS(域名解析协议)、NFS、BOOTP。浏览器
在谈到TCP的时候你们可能就很快能想到三次握手,四次挥手,我用个人理解来分析一下怎么回事。服务器
简单讲呢,其实就是两个之间创建通讯,你也能够理解为挖一条链接两个地方的通道。网络
TCP是因特网中的传输层协议,使用三次握手协议创建链接。当主动方发出SYN链接请求后,等待对方回答SYN+ACK[1],并最终对对方的 SYN 执行 ACK 确认。这种创建链接的方法能够防止产生错误的链接。[1] TCP三次握手的过程以下: 客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。 三次握手完成,TCP客户端和服务器端成功地创建链接,能够开始传输数据了。
当应用程序但愿经过 TCP 与另外一个应用程序通讯时,它会发送一个通讯请求。这个请求必须被送到一个确切的地址。在双方“握手”以后,TCP 将在两个应用程序之间创建一个全双工 (full-duplex) 的通讯。
下面是方便理解的版本。画的比较丑,emmm,只有将就看。
有人就会想,为何是三次握手四次挥手,不是三次挥手呢。好吧,其实最开始我也是这样想的。
创建一个链接须要三次握手,而终止一个链接要通过四次握手,这是由TCP的半关闭(half-close)形成的。
(1) 某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP因而发送一个FIN分节,表示数据发送完毕。
(2) 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认。
注意:FIN的接收也做为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其余数据以后,由于,FIN的接收意味着接收端应用进程在相应链接上再无额外数据可接收。
(3) 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这致使它的TCP也发送一个FIN。
(4) 接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。
既然每一个方向都须要一个FIN和一个ACK,所以一般须要4个分节。
注意:
(1) “一般”是指,某些状况下,步骤1的FIN随数据一块儿发送,另外,步骤2和步骤3发送的分节都出自执行被动关闭那一端,有可能被合并成一个分节。
(2) 在步骤2与步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为“半关闭”(half-close)。
(3) 当一个Unix进程不管自愿地(调用exit或从main函数返回)仍是非自愿地(收到一个终止本进程的信号)终止时,全部打开的描述符都被关闭,这也致使仍然打开的任何TCP链接上也发出一个FIN。
不管是客户仍是服务器,任何一端均可以执行主动关闭。一般状况是,客户执行主动关闭,可是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。
UDP协议
当应用程序但愿经过UDP与一个应用程序通讯时,传输数据以前源端和终端不创建链接。
当它想传送时就简单地去抓取来自应用程序的数据,并尽量快地把它扔到网络上。
TCP与UDP比较
TCP---传输控制协议,提供的是面向链接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间创建一个TCP链接,以后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另外一端。
UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,可是并不能保证它们能到达目的地。因为UDP在传输数据报前不用在客户和服务器之间创建一个链接,且没有超时重发等机制,故而传输速度很快。
C/S 架构是一种典型的两层架构,其全称是Client/Server,即客户端服务器端架构。
其客户端包含一个或多个在用户的电脑上运行的程序,而服务器端有两种,一种是数据库服务器端,客户端经过数据库链接访问服务器端的数据;另外一种是Socket服务器端,服务器端的程序经过Socket与客户端的程序通讯。
C/S 架构也能够看作是胖客户端架构。由于客户端须要实现绝大多数的业务逻辑和界面展现。这种架构中,做为客户端的部分须要承受很大的压力,由于显示逻辑和事务处理都包含在其中,经过与数据库的交互(一般是SQL或存储过程的实现)来达到持久化数据,以此知足实际项目的须要。
B/S架构的全称为Browser/Server,即浏览器/服务器结构。
Browser指的是Web浏览器,极少数事务逻辑在前端实现,但主要事务逻辑在服务器端实现,Browser客户端,WebApp服务器端和DB端构成所谓的三层架构。B/S架构的系统无须特别安装,只有Web浏览器便可。
B/S架构中,显示逻辑交给了Web浏览器,事务处理逻辑在放在了WebApp上,这样就避免了庞大的胖客户端,减小了客户端的压力。由于客户端包含的逻辑不多,所以也被成为瘦客户端。
B/S实际上是属于C/S中的一种特殊状况。可是如今随着internet的发展,不少人习惯于直接访问网页,因此B/S也变得更加的流行起来。
Socket又称"套接字",应用程序一般经过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间能够通信。
首先先熟悉一下流程图,而后根据流程在进行具体的代码分析。
socket.
AF_UNIX unix本机进程间通讯
socket.
AF_INET IPV4
socket.
AF_INET6 IPV6
socket.
SOCK_STREAM #for tcp
socket.
SOCK_DGRAM #for udp
SOCK_STREAM
或SOCK_DGRAM
Python 中,咱们用 socket()函数来建立套接字,语法格式以下:
socket.socket([family[, type[, proto]]]),若是里面不传参数,默认为ipv4,TCP协议
bind():绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。
listen():开始TCP监听。backlog指定在拒绝链接以前,操做系统能够挂起的最大链接数量。该值至少为1,大部分应用程序设为5就能够了。
accept() :被动接受TCP客户端链接,(阻塞式)等待链接的到来接受链接并返回(conn,addr),其中conn是新的套接字对象,能够用来接收和发送数据。addr是链接客户端的地址,
connect():主动初始化TCP服务器链接,。通常address的格式为元组(hostname,port),若是链接出错,返回socket.error错误。
recv():接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其余信息,一般能够忽略。
send():发送TCP数据,将string中的数据发送到链接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
recvfrom():接收UDP数据,与recv()相似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
sendto():发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
close():关闭套接字
前面讲的一些网络知识,其实在写代码的过程当中,没有用到,知识让你明白流程是如何的。下面就写一个简单的socket实现通讯的代码吧。
服务端(server)
1 import socket 2 server=socket.socket() 3 server.bind(('127.0.0.1',1314))#绑定ip地址跟端口号 4 server.listen()#打开监听 5 conn,addr=server.accept()#接受链接并返回(conn,address),其中conn是新的套接字对象,能够用来接收和发送数据。address是链接客户端的地址 6 date=conn.recv(1024)#收到客户端发来的消息(bytes类型) 7 print(date.decode()) 8 conn.send('我爱你'.encode())#向客户端发送消息 9 conn.close()#关闭与这个客户端的链接 10 server.close()#关闭
客户端(client)
1 import socket 2 client=socket.socket() 3 client.connect(('127.0.0.1',1314))#链接到这个ip地址和这个端口号 4 client.send(b'gmx')#向服务端发送消息 5 print(client.recv(1024).decode())#收到服务端发来的消息
固然,这只是最简单的例子了,咱们明白了基本的道理,能够在上面的基础上实现客户端跟服务端两个的聊天。
服务端(server)
1 import socket 2 server=socket.socket() 3 server.bind(('127.0.0.1',1314))#绑定ip地址跟端口号 4 server.listen()#打开监听 5 while True: 6 conn,addr=server.accept()#在一个客户端断开,还有其余的能连 7 while True: 8 recv_data=conn.recv(1024) 9 print('客户端发来的消息:%s' %recv_data.decode()) 10 send_data=input(">>>>") 11 conn.send(send_data.encode()) 12 conn.close() 13 server.close()#关闭
客户端(client)
1 import socket 2 client=socket.socket() 3 client.connect(('127.0.0.1',1314))#链接到这个ip地址和这个端口号 4 while True: 5 send_data=input('>>>') 6 if not send_data: 7 print('客户端断开') 8 break 9 client.send(send_data.encode()) 10 recv_data = client.recv(1024) 11 print(recv_data.decode()) 12 client.close()
固然前面的两个例子都是比较简单的socket通讯。后面咱们会讲到作一个简单的ssh,前面接收的数据只有1024字节,那若是在接收的过程当中,数据量比较大应该怎么办呢,还有一种状况就是若是同时发两条消息,而后前面的信息没接收完,后面的又发过去了,就会产生粘包如今,后面咱们会深刻讲到网络通讯的一些东西。