IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 网络编程基础 socket编程 第二讲 TCP发送与接收数据,客户端和服务端的构建 -> 正文阅读

[网络协议]网络编程基础 socket编程 第二讲 TCP发送与接收数据,客户端和服务端的构建

第二讲 TCP发送与接收数据

一、TCP协议介绍

当应用程序希望通过TCP与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址。在双方“握手”之后,TCP将在两个应用程序之间建立一个全双工(full-duplex)的通信。

这个全双工的通信将占用两个计算机之间的通信路线,直到它被一方或双方关闭为止。

1.三次握手

下图为初版,四次握手:
在这里插入图片描述

进化后成了三次握手,server应答和发送请求可以变成一条:
在这里插入图片描述

2. 数据传输

以下图为例,TCP协议是一个全双工通信,发送方和接收方建立了信息发送和接收渠道:
在这里插入图片描述
在一方发送消息后,另一方一定会回复信息,表明信息已接收,TCP协议的安全性体现在这里。

3.四次挥手

在这里插入图片描述
断开连接为何是四次而不是三次,可以查一下资料,这里有一篇详解,由于不是计算机专业所以还看不懂,关于TCP三次握手和四次挥手

4.TCP特点

  1. 面向连接
  2. 可靠传输
    ? TCP采用发送应答机制
    ? 超时重传
    ? 错误校验
    ? 流量控制和阻塞管理

二、UDP协议介绍

当应用程序希望通过UDP与一个应用程序通信时,传输数据之前源端和终端不建立连接。

当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。

在这里插入图片描述

三、TCP与UDP之间的区别

  1. TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接。
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。
  3. UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
  4. 每一条TCP连接只能是点到点的:UDP支持一对一,一对多和多对一和多对多的交互通信。
  5. TCP对系统资源要求较多,UDP对系统资源要求较少。

四、UDP通信

在这里插入图片描述
其顺序如下图所示:
在这里插入图片描述

五、TCP通信

在这里插入图片描述

六、TCP客户端构建流程

  1. 创建socket
  2. 链接服务器
  3. 接收/发送数据
  4. 关闭套接字

我们通过一段简短的代码来看一下。
首先将网络调试助手调成TCP server的角色:
在这里插入图片描述
然后编写代码:

import socket

def main():
    # 1.创建套接字,注意type为TCP流式套接字
    tcp_client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
    tcp_client.bind(('',8000))  # 绑定端口
    # 2.连接服务器
    tcp_client.connect(('192.168.59.1',8080))
    # 3.发送数据
    send_data = input('你想要发送的数据:')
    tcp_client.send(send_data.encode('gbk'))    # 已建立TCP连接,使用send方法即可
    # 4.接收数据,到这里会产生阻塞等待
    rec_data = tcp_client.recv(1024*3)	# 表示最大接收数据大小,单位为字节
    print(rec_data.decode('gbk'))
    # 5.关闭套接字
    tcp_client.close()

if __name__ == '__main__':
    main()

七、TCP服务端构建流程

  1. socket创建套接字
  2. bind绑定IP和port
  3. listen使套接字变为可以被动链接
  4. accept等待客户端的链接
  5. recv/send接收发送数据
import socket

def main():
    # 1.创建套接字
    tcp_server = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    
    # 2.绑定IP、端口
    tcp_server.bind(('192.168.59.1',8000))
    
    # 3.listen使套接字变为可以被动链接,别人可以接上服务器
    #   其中listen方法需要传入bcaklog参数,为整型
    #   backlog表示的是服务器拒绝连接之前,操作系统可以挂起的最大连接数量,可以理解为排队的个数
    tcp_server.listen(1)    # 这里暂定为1
    
    # 4.accept等待客户端连接,发生阻塞,与客户端连接则解阻塞
    #   这里需要说明,accept方法返回新的套接字以及客户端的地址信息,旧套接字将被代替
    server_new_socket,client_addr = tcp_server.accept()
    #   可以打印查看accept返回的值
    print(server_new_socket,client_addr)
    
    # 5.接收数据,采用新套接字
    rec_info = server_new_socket.recv(1024)
    print(rec_info.decode('gbk'))
    
    # 6.发送数据
    send_data = input('你想要回复的信息:')
    server_new_socket.send(send_data.encode('gbk'))


if __name__ == '__main__':
    main()

八、TCP的相关练习

1. 练习1

需求:

  1. 实现TCP服务端为多个客户端服务
  2. 实现为客户端多次服务

为了实现需求1,我们可以采用while循环来实现:

import socket

def main():
    tcp_server = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    tcp_server.bind(('192.168.59.1',8000))
    while True:
        tcp_server.listen(128)
        server_new_socket,client_addr = tcp_server.accept()
        rec_info = server_new_socket.recv(1024)
        print(rec_info.decode('gbk'))
        send_data = input('你想要回复的信息:')
        server_new_socket.send(send_data.encode('gbk'))
    tcp_server.close()

if __name__ == '__main__':
    main()

但是上述代码仍旧存在一些不足之处,虽然是可以为多个客户端服务,但实际测试当中会有多出阻塞之处,并且需要完成某一客户端的全部服务后才能进入对下一个客户端的服务。

现在我们来看一下需求2。

在研究之前,我们首先要看到前一小节TCP服务端在构建后的一些服务局限性。

我们上一节构建的TCP服务端所能够提供的服务就是发送和接收消息,当使用模拟的网络助手(作为TCP客户端)建立起链接并完成一次服务后,会自动将该TCP客户端纳入等待序列(不是断开连接),因而无法进行多次服务

为了解决这个问题,我们同样要使用while循环:

import socket

def main():
    tcp_server = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    tcp_server.bind(('192.168.59.1',8000))
    '''
    这一步while循环允许多个客户端连接
    '''
    while True:
        tcp_server.listen(128)
        server_new_socket,client_addr = tcp_server.accept()
        '''
        这一步while循环允许为同一个客户端服务多次
        '''
        while True:
            rec_info = server_new_socket.recv(1024)
            '''
            使用网络助手主动断开连接时,会发送一个空字符串,因而做如下判断
            '''
            if rec_info.decode('gbk') != '':
                print(rec_info.decode('gbk'))
                send_data = input('你想要回复的信息:')
                server_new_socket.send(send_data.encode('gbk'))

            elif rec_info.decode('gbk') == '':
                break
            else:
                break
        server_new_socket.close()

    tcp_server.close()

if __name__ == '__main__':
    main()

上述代码有一些缺陷,比如如何在0客户端的时候跳出循环,还不知道怎么做;另外一个就是当前代码只允许为一个客户端服务多次,不能同时对多个客户端服务多次,这牵扯到之后的多线程学习。

2. 练习2 文件下载器

需求如下图所示:
在这里插入图片描述

我们首先看一下客户端的代码:

import socket

def main():
	# 1.创建套接字
    tcp_client_socket = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    # 2.链接服务器
    tcp_client_socket.connect(('192.168.59.1',7890))
    # 3.输入下载文件的名称,并发送给服务端
    file_name = input('你想下载的文件名:')
    tcp_client_socket.send(file_name.encode('gbk'))
    # 4.等待服务端返回的文件内容
    content = tcp_client_socket.recv(1024*1024).decode('gbk')
    # 5.使用上下文管理器创建文件并保存内容
    with open('附件'+file_name,'w') as f:
        f.write(content)
    # 6.关闭套接字
    tcp_client_socket.close()

if __name__ == '__main__':
    main()

接下来构建服务端代码:

import socket

# 将发送文件内容的功能打包成独立函数
def server_send(file_name,new_socket):
    with open(file_name,mode='r') as f:
        content = f.read()
    new_socket.send(content.encode('gbk'))

def main():
	# 1.创建套接字
    tcp_server = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    # 2.绑定ip,port
    tcp_server.bind(('192.168.59.1',7890))
    # 3.变为可被动连接
    tcp_server.listen(128)
    # 4.等待接收信息
    new_socket,client_addr = tcp_server.accept()
    # 5.接收客户端想要读取的文件名称
    file_name = new_socket.recv(1024).decode('gbk')
    # 6.发送相应文件内容
    server_send(file_name,new_socket)
    # 7.关闭套接字
    tcp_server.close()

if __name__ == '__main__':
    main()
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-07-13 17:51:11  更:2021-07-13 17:53:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 17:30:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码