zoukankan      html  css  js  c++  java
  • 小白学习之路,网络编程(上)

    一,计算机网络基础

    在讲网络编程之前,先跟大家简单的介绍一下一些网络相关的知识。

    在最早之前,两台电脑之间通信是通过电脑的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地址进行按位与运算就能得出一个机器所在的网段。

    一个ip地址能够找到一个固体的计算机,然后端口就表示你计算机具体运行的那个程序。端口号的范围在0-65535,我们一般选择用8000以后的端口号。一些应用默认端口号有8000-酷狗音乐 ,22-ssh ,3306-mysql,oracle-1521,redis-6379,RabbitMQ-15672等等。所以当然知道唯一的ip跟端口号,就能确定一个计算机上的唯一应用。

    网络层次划分

    为了使不同计算机厂家生产的计算机能够相互通信,以便在更大的范围内建立计算机网络,国际标准化组织(ISO)在1978年提出了“开放系统互联参考模型”,即著名的OSI/RM模型(Open System Interconnection/Reference Model)。它将计算机网络体系结构的通信协议划分为七层,自下而上依次为:物理层(Physics Layer)、数据链路层(Data Link Layer)、网络层(Network Layer)、传输层(Transport Layer)、会话层(Session Layer)、表示层(Presentation Layer)、应用层(Application Layer)。其中第四层完成数据传送服务,上面三层面向用户。

      除了标准的OSI七层模型以外,常见的网络层次划分还有TCP/IP四层协议以及TCP/IP五层协议,它们之间的对应关系如下图所示:
    当然我们在这里具体对Tcp/ip5层模型就行介绍。每个层对应的协议流程关系图如下:

    TCP/UDP协议

    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,b/s架构

    1,c/s架构

    C/S 架构是一种典型的两层架构,其全称是Client/Server,即客户端服务器端架构。

    其客户端包含一个或多个在用户的电脑上运行的程序,而服务器端有两种,一种是数据库服务器端,客户端通过数据库连接访问服务器端的数据;另一种是Socket服务器端,服务器端的程序通过Socket与客户端的程序通信。

    C/S 架构也可以看做是胖客户端架构。因为客户端需要实现绝大多数的业务逻辑和界面展示。这种架构中,作为客户端的部分需要承受很大的压力,因为显示逻辑和事务处理都包含在其中,通过与数据库的交互(通常是SQL或存储过程的实现)来达到持久化数据,以此满足实际项目的需要。

    2,b/s架构

     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?

    Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。

    首先先熟悉一下流程图,然后根据流程在进行具体的代码分析。

    1,Socket Families(地址簇)

    socket.AF_UNIX unix本机进程间通信 

    socket.AF_INET IPV4 

    socket.AF_INET6  IPV6

    2,Socket Types(传输协议)

    socket.SOCK_STREAM  #for tcp

    socket.SOCK_DGRAM   #for udp 

    3,参数

    • family: 套接字家族可以使AF_UNIX或者AF_INET
    • type: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAMSOCK_DGRAM
    • protocol: 一般不填默认为0

    Python 中,我们用 socket()函数来创建套接字,语法格式如下:

    socket.socket([family[, type[, proto]]]),如果里面不传参数,默认为ipv4,TCP协议

    4,socket常用方法

    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使用

    前面讲的一些网络知识,其实在写代码的过程中,没有用到,知识让你明白流程是如何的。下面就写一个简单的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()#关闭
    View Code

    客户端(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())#收到服务端发来的消息
    View Code

    当然,这只是最简单的例子了,我们明白了基本的道理,可以在上面的基础上实现客户端跟服务端两个的聊天。

    服务端(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()#关闭
    View Code

    客户端(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()
    View Code

    当然前面的两个例子都是比较简单的socket通信。后面我们会讲到做一个简单的ssh,前面接收的数据只有1024字节,那如果在接收的过程中,数据量比较大应该怎么办呢,还有一种情况就是如果同时发两条消息,然后前面的信息没接收完,后面的又发过去了,就会产生粘包现在,后面我们会深入讲到网络通信的一些东西。

  • 相关阅读:
    设计模式_EventObject和EventListener
    设计模式_Observable与Observer
    zooKeeper_《ZooKeeper官方指南》一致性保障
    thread_为什么多线程是个坏主意
    gtk+学习笔记(三)
    linux c下输入密码不回显
    浮点数在计算机内存中的存储方式
    gtk+学习笔记(二)
    linux下c图形化编程之gtk+2.0简单学习
    关于字符串排序合并的问题
  • 原文地址:https://www.cnblogs.com/zzqit/p/9240112.html
Copyright © 2011-2022 走看看