网络编程(2) - 网络通信方式-TCP

网络通信方式-TCP

2.1,TCP介绍

  • TCP协议,传输控制协议(英文:Transmission Control Protocol,缩写为TCP)是一种面向连接的,可靠的,基于字节流的传输层通信协议,由IETF的RFC 793定义。
  • TCP通信需要经过创建连接数据传送终止连接三个步骤。
  • TCP通信模型中,在通信开始之前,一定要建立相关的连接,才能发送数据,类似于打电话

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bgeEc1CV-1592061841529)(assets/.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JfujcbRR-1592061841531)(assets/.jpg)]

  • TCP特点
  1. 面向连接

    通信双方必须先建立连接才能进行数据的传输,双方都必须为该链接分配必要的系统内核资源,以管理连接的状态和连接上的传输。

    双方间的数据传输都可以通过这一个连接进行。

    完成数据交互后,双方必须断开连接,以释放系统资源

    这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议

  2. 可靠传输
    1),TCP采用发送应答机制

    TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功

    2),超时重传

    发送端发出一个报文段之后就启动定时器,如果在定时器时间内没有收到应答就重新发送这个报文段

    TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收,然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的返时延(RTT)内未确认,那么对应的数据包就被假设为已丢失将会被进行重传

    3),错误效验

    TCP用一个效验和函数来检验数据是否有错误;在发送和接收时都要计算效验和

    4),流量控制和阻塞管理

    流量控制用来避免主机发送的过快而使接收方来不及完全收下

    TCP与UDP的不同点

    • 面向连接(确认有创建三方交握,创建已创建才作传输)
    • 有序数据传输
    • 重发丢失的数据包
    • 舍弃重复的数据包
    • 无差错的数据传输
    • 阻塞/流量控制

    UDP通信模型

    通信的过程中会被别人截胡或者意外丢失,而且发送方也不知道接收方有没有收到,不安全,类似发信件一样,发信以后不知道对方会不会收到,信在途中会不会丢失

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DbxjZlIa-1592061841533)(assets/.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w6pMBDO0-1592061841535)(assets/.png)]

    TCP通信模型

    TCP 通信之前必须建立要相关的连接,才能发送数据,类似打电话,双发一定要有联系才可以发送数据,是确保数据能100%接收,比udp安全,数据完整

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T8I9Z3eN-1592061841538)(assets/.png)]

2.2,TCP客户端(重要)

一共四步,创建tcp_socket,绑定端口和ip,发送数据,关闭socket

  • 原始版本

     # !/usr/bin/env python
     # _*_ coding:utf-8 _*_
     # author:满怀心 2019/7/30 12:00
     """
     # code is far away from bugs with the god animal protecting
         I love animals. They taste delicious.
                   ┏┓      ┏┓
                 ┏┛┻━━━┛┻┓
                 ┃      ☃      ┃
                 ┃  ┳┛  ┗┳  ┃
                 ┃      ┻      ┃
                 ┗━┓      ┏━┛
                     ┃      ┗━━━┓
                     ┃  神兽保佑    ┣┓
                     ┃ 永无BUG!   ┏┛
                     ┗┓┓┏━┳┓┏┛
                       ┃┫┫  ┃┫┫
                       ┗┻┛  ┗┻┛
     """
     from socket import *
     
     
     def main():
         # 1. 创建tcp套接字,注意type类型是 SOCK_STREAM
         tcp_socket = socket(AF_INET, SOCK_STREAM)
     
         # 2. 连接服务器
         server_ip = input('请输入要连接的服务器的ip:\t')
         server_port = input('请输入要连接的服务器的port:\t')
         server_addr = (server_ip, int(server_port))
         tcp_socket.connect(server_addr)
     
         # 3. 发送数据/接收数据
         send_data = input('请输入你要发送的数据:\t')
         tcp_socket.send(send_data.encode('utf-8'))
     
         # 4. 关闭套接字
         tcp_socket.close()
     
     
     if __name__ == '__main__':
         main()
    
-客户端更新版本1
  # !/usr/bin/env python
  # _*_ coding:utf-8 _*_
  # author:满怀心 2019/7/30 12:00
  """
  # code is far away from bugs with the god animal protecting
      I love animals. They taste delicious.
                ┏┓      ┏┓
              ┏┛┻━━━┛┻┓
              ┃      ☃      ┃
              ┃  ┳┛  ┗┳  ┃
              ┃      ┻      ┃
              ┗━┓      ┏━┛
                  ┃      ┗━━━┓
                  ┃  神兽保佑    ┣┓
                  ┃ 永无BUG!   ┏┛
                  ┗┓┓┏━┳┓┏┛
                    ┃┫┫  ┃┫┫
                    ┗┻┛  ┗┻┛
  """
  from socket import  *
  
  
  def main():
      # 1. 创建tcp套接字
      tcp_socket = socket(AF_INET, SOCK_STREAM)
      tcp_socket.bind(('', 5566))
  
      # 2. 绑定服务器的ip和端口
      server_ip = input('请输入对方服务器的ip地址:\t')
      server_port = input('请输入对方服务器的port端口:\t')
      server_addr = (server_ip, int(server_port))
      tcp_socket.connect(server_addr)
  
      # 3. 发/收数据
      while True:
          # 使用try捕捉 ctrl + c
          try:
              send_data = input('请输入要发送的数据:\t')
  
              # 发送数据
              tcp_socket.send(send_data.encode('utf-8'))
  
              # 接收服务器信息
              recv_data = tcp_socket.recv(1024)
              print(recv_data.decode('utf-8'))
          # 跳出循环,关闭socket
          except KeyboardInterrupt:
              tcp_socket.send('quit()'.encode('utf-8'))
              break
  
      # 4. 关闭套接字
      tcp_socket.close()
  
  
  if __name__ == '__main__':
      main()
-客户端更新版本2(try-except)
 # !/usr/bin/env python
 # _*_ coding:utf-8 _*_
 # author:满怀心 2019/7/30 22:03
 """
 # code is far away from bugs with the god animal protecting
     I love animals. They taste delicious.
               ┏┓      ┏┓
             ┏┛┻━━━┛┻┓
             ┃      ☃      ┃
             ┃  ┳┛  ┗┳  ┃
             ┃      ┻      ┃
             ┗━┓      ┏━┛
                 ┃      ┗━━━┓
                 ┃  神兽保佑    ┣┓
                 ┃ 永无BUG!   ┏┛
                 ┗┓┓┏━┳┓┏┛
                   ┃┫┫  ┃┫┫
                   ┗┻┛  ┗┻┛
 """
 from socket import *
 import sys
 import argparse
 # argparse 获取命令行参数,能后转换成函数内部的数据
 
 
 def text():
     parser = argparse.ArgumentParser(description='Socket Error Examples')
     parser.add_argument('--host', action='store', dest='host', required=False)
     parser.add_argument('--port', action='store', dest='port', type=int, required=False)
     parser.add_argument('--file', action='store', dest='file', required=False)
     given_args = parser.parse_args()
     host = given_args.host
     port = given_args.port
     filename = given_args.file
 
     # First try-except block  =>  creating socket
     try:
         tcp_socket = socket(AF_INET, SOCK_STREAM)
     except error as e:
         print('Error creating socket : {}'.format(e))
         sys.exit(1)     # 退出
 
     try:
         # Second try-except block  =>  connecting to given host/port
         try:
             tcp_socket.connect((host, port))
         except gaierror as e:
             print('Address-related error connecting to server : {}'.format(e))
             sys.exit(1)
         except error as e:
             print('Connection error : {}'.format(e))
             sys.exit(1)
 
         while True:
             # Third try-except block  =>  sending data
             try:
                 dest_data = input('请输入要传输的数据:\t').encode('utf-8')
                 tcp_socket.send(dest_data)
             except error as e:
                 print('Error sending data : {}'.format(e))
                 sys.exit(1)
 
             # Fourth try-except block  =>  waiting to receive from remote host
             try:
                 recv_data = tcp_socket.recv(2048)
             except error as e:
                 print('Error receive data : {}'.format(e))
                 sys.exit(1)
             if not recv_data:
                 break
             else:
                 print(recv_data.decode('utf-8'))
 
     except KeyboardInterrupt:
         tcp_socket.close()
 
 
 if __name__ == '__main__':
     text()

2.3,TCP服务端(重要)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CMWywAxU-1592061841539)(assets/.png)]

# !/usr/bin/env python
# _*_ coding:utf-8 _*_
# author:满怀心 2019/7/30 12:36
"""
# code is far away from bugs with the god animal protecting
    I love animals. They taste delicious.
              ┏┓      ┏┓
            ┏┛┻━━━┛┻┓
            ┃      ☃      ┃
            ┃  ┳┛  ┗┳  ┃
            ┃      ┻      ┃
            ┗━┓      ┏━┛
                ┃      ┗━━━┓
                ┃  神兽保佑    ┣┓
                ┃ 永无BUG!   ┏┛
                ┗┓┓┏━┳┓┏┛
                  ┃┫┫  ┃┫┫
                  ┗┻┛  ┗┻┛
"""
from socket import *


def main():
    # 1. 买个手机(创建套接字 socket)
    tcp_server_socket = socket(AF_INET, SOCK_STREAM)

    # 2. 插入手机卡(绑定本地信息 bind)
    tcp_server_socket.bind(('', 7891))

    # 3. 将手机设置为正常的响铃模式(让默认的套接字由主动变成被动 listen)
    tcp_server_socket.listen(128)

    # 4. 等待别人的电话到来(accept)
    print('-----等待接收-----')
    new_client_socket,client_addr = tcp_server_socket.accept()
    # accept方法生成一个元组,元组里是一个新的socket客户端,用来和接收到的客户端通信,并且这个新生成的socket客户端会带有接收到的socket客户端的信息来和你通信,就像拨打10086会给你接通到客服服务,但是10086还是处于被动接收状态,处理客户端的不是服务端,而是服务端产生的新的客户端,服务端只负责接收新的客户端请求并转接给新的客户端进行服务
    print('-----接收成功-----')
    
    print(client_addr)	# 打印客户端的信息


    # 接收客户端发过来的请求
    recv_data = new_client_socket.recv(1024)
    print(recv_data.decode('utf-8')) # 这个新的客户端带有请求的客户端的信息,不需要像元组一样

    # 回送数据给客户端
    new_client_socket.send('----------ok----------'.encode('utf-8'))

    # 关闭套接字
    tcp_server_socket.close()
    new_client_socket.close()


if __name__ == '__main__':
    main()
-服务器更新版本
# !/usr/bin/env python
# _*_ coding:utf-8 _*_
# author:满怀心 2019/7/30 12:36
"""
# code is far away from bugs with the god animal protecting
    I love animals. They taste delicious.
              ┏┓      ┏┓
            ┏┛┻━━━┛┻┓
            ┃      ☃      ┃
            ┃  ┳┛  ┗┳  ┃
            ┃      ┻      ┃
            ┗━┓      ┏━┛
                ┃      ┗━━━┓
                ┃  神兽保佑    ┣┓
                ┃ 永无BUG!   ┏┛
                ┗┓┓┏━┳┓┏┛
                  ┃┫┫  ┃┫┫
                  ┗┻┛  ┗┻┛
"""
from socket import *


def main():
    # 1. 买个手机(创建套接字 socket)
    tcp_server_socket = socket(AF_INET, SOCK_STREAM)

    # 2. 插入手机卡(绑定本地信息 bind)
    tcp_server_socket.bind(('', 2345))

    # 3. 将手机设置为正常的响铃模式(让默认的套接字由主动变成被动 listen)
    tcp_server_socket.listen(128)

    try:
        while True:
            # 4. 等待别人的电话到来(accept)
            print('----------等待客户端连接连接----------')
            # 监听服务器接收客户端请求,创造一个socket与其进行通信
            new_client_socket,client_addr = tcp_server_socket.accept()
            print('客户端 {}:{} 已经成功连接'.format(client_addr[0], client_addr[1]))

            while True:
                # 接收客户端发过来的请求
                recv_data = new_client_socket.recv(1024).decode('utf-8')
                print('接收数据 : {}'.format( recv_data))
                # recv_data 解堵塞:
                # 1. 客户端发送过来数据
                # 2. 客户端断开连接解堵塞

                # 客户端发送数据,解堵塞
                if recv_data:
                    # 回送数据给客户端
                    new_client_socket.send('----------ok,接收成功----------'.encode('utf-8'))
                # 客户端断开连接,解堵塞
                else:
                    break
            print('客户端 {}:{} 已经成功断开\n'.format(client_addr[0], client_addr[1]))
            # 关闭套接字
            new_client_socket.close()
    except KeyboardInterrupt:
        # 5. 关闭服务器
        tcp_server_socket.close()


if __name__ == '__main__':
    main()

2.4,TCP注意点

  1. tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器
  2. tcp客户端一般不绑定(可以在多个多开,不用担心端口被恶意程序霸占),因为是主动连接服务器,所以只要确定好服务器的ip,port等信息就好,本地客户端可以随机
  3. tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的
  4. 当客户端需要连接服务器时,就需要使用connect进行连接,udp是不需要连接的而是直接发送,但是tcp必须连接,只有连接成功才能通信
  5. 当一个tcp客户端连接服务器时,服务器会有1个新的套接字,这个套接字是用来标记这个客户端的,单独为这个客户端服务
  6. listen后的套接字是被动套接字,用来接收新的客户端的连接请求的,而accept返回的新套接字是标记这个新客户端的
  7. 关闭listen后的套接字就意味着被动套接字关闭了,会导致新的客户端不能够连接服务器,但是之前已经连接成功的客户端能够正常通信
  8. 关闭accept返回的套接字意味着这个客户端已经服务关闭
  9. 当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值