| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> TCP UDP协议要点整理 -> 正文阅读 |
|
[网络协议]TCP UDP协议要点整理 |
概述? ? ? ? TCP和UDP是传输层中的两个非常重要的协议,这两个协议对应的网络通信api(socket api)也有较大差异. ? ? ? ? 简单来说,TCP的特点是:有连接,可靠传输,面向字节流,全双工.而UDP的特点是:无连接,不可靠传输,面向数据报,全双工. ? ? ? ? 有无连接:如果通信的前提是要先建立连接(如打电话),我们称之为有连接;反之(如发微信),我们称之为无连接. ? ? ? ? 是否可靠传输:如果数据发送方能够知道接收方是否受到了其发送的数据,我们称之为可靠传输;反之,称之为不可靠传输.值得一提的是,在网络传输中,是无法保证百分之百的可靠传输的,极端情况下,假如网线断了,这就从物理层面阻止了网络传输. ? ? ? ? 面向字节流/数据报:发送/接受数据以字节为单位,我们称之为面向字节流;以数据报为单位则称之为面向数据报. UDP部分? ? ? ? 基本类? ? ? ? ? ? ? ? UDP协议下主要有两个常用的Socket API. ? ? ? ? ? ? ? ? 1.DatagramSocket ? ? ? ? ? ? ? ? ? ? ? ? 这里需要明确的是,socket类,本质上也是"文件",打开一个socket文件同样会占用进程文件描述符表里的一个位置.socket文件对应到网卡设备.构造一个DatagramSocket对象,就相当于是打开了一个内核中的socket文件. ? ? ? ? ? ? ? ? ? ? ? ? 打开socket的文件之后,我们就可以借此实现端与端之间的数据传输了.有以下三个基本方法: ? ? ? ? ? ? ? ? ? ? ? ? send():发送数据. ? ? ? ? ? ? ? ? ? ? ? ? receive():接收数据. ? ? ? ? ? ? ? ? ? ? ? ? close():关闭socket文件资源. ? ? ? ? ? ? ? ? 2.DatagramPacket ? ? ? ? ? ? ? ? ? ? ? ? DatagramPacket表示一个UDP数据报.UDP协议传输数据就是以这个作为基本单位. ? ? ? ? 回显服务器(echo server)? ? ? ? ? ? ? ? 服务器????????????????????????
? ? ? ? ? ? ? ? 要点详解: ? ? ? ? ? ? ? ? ? ? ? ? 1.
? ? ? ? ? ? ? ? ? ? ? ? 此处手动给服务器绑定一个端口号(port),方便客户端去访问. ? ? ? ? ? ? ? ? ? ? ? ? 一个操作系统上,有很多端口号,范围为0-65535.程序如果需要进行网络通信,就需要获取到一个端口号.端口号相当于网络中用于区分不同进程之间的标识符.(操作系统收到网卡的数据,就可以根据网络数据报中的端口号,来确定要把数据发送给哪个进程). ? ? ? ? ? ? ? ? ? ? ? ? 一个端口在通常情况下只能绑定一个进程,而一个进程可以同时绑定多个端口.如果尝试将一个进程与一个已经被占用的端口绑定,会直接抛出异常. ? ? ? ? ? ? ? ? ? ? ? ? 2.
? ? ? ? ? ? ? ? ? ? ? ? 此部分是UDP服务器接收数据的逻辑.由于UDP协议是面向数据报的协议,而客户端发送过来的数据通常是字符串.所以我们需要人为把客户端发送过来的数据封装成一个数据报.上述代码就是构造了空的数据报,用于将接收过来的字符串封装成一个UDP数据报(我们称这种参数为输出型参数).观察此处构造函数中的参数可以看出,DatagramPacket本质上就是一个字节数组. ? ? ? ? ? ? ? ? ? ? ? ? 此外,若服务器在启动后始终没有接收到客户端发来的数据,那么socket.receive()方法将会阻塞等待. ? ? ? ? ? ? ? ? ? ? ? ? 3.
? ? ? ? ? ? ? ? ? ? ? ? ? ?这部分是数据的解析与发送. ? ? ? ? ? ? ? ? ? ? ? ? ? ?服务器利用receive中获得的数据报.可以重构回客户端发送的字符串数据(即getData()与getLength()).然后通过process()解析函数得到需要发送回给客户端的字符串数据. ? ? ? ? ? ? ? ? ? ? ? ? ? ?然后就到了封装UDP数据报的部分.数据报的内容和长度我们可以通过String类中的方法确定(即getBytes()和getBytes().length).然后我们还需要确定这个数据报需要发送给谁.这部分有关于客户端的信息可以通过UDP数据报的getSocketAddress()方法获得(注意是客户端的).由此,我们就构造好了responsePacket.将其通过send()方法发送出去即可. ????????????????????????? ? 此处还需要注意的一点是,由于是回显服务器(echo server).服务器不需要对客户端发送的请求做任何处理,原本返回即可. ? ? ? ? ? ? ? ? ? ? ? ? 4.
? ? ? ? ? ? ? ? ? ? ? ? ? ?这部分代码是在进程中创建UDP服务器的实例.值得一提的是,我们不推荐给服务器分配号码数小于1024的端口号,因为小于1024的端口号通常是操作系统保留用做他用的. ? ? ? ? ? ? ? ? 客户端? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? 要点详解: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 1.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?注意到客户端DatagramSocket的构造不需要手动传入端口号,而是选择让用户的操作系统自行分配.这是因为作为开发者,我们无法知道用户的机器上有哪些端口是空闲的,让操作系统自行分配可以避免异常.? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 此处是客户端的数据封装与发送.我们可以注意到此处UDP数据报的构造方式与服务器部分的不同:客户端在构造数据报时,需要带上自己的IP地址和服务器的端口号.这两者相当于快递的寄件方和收件方.而服务器在构造数据报时,只需要明确接收方即可.(此处客户端本地的地址获取调用了InetAddress类中的getByName()方法,"127.0.0.1"是自己主机的IP地址) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 然后我们通过send()方法将数据报发送给服务器即可. ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 3.接收数据部分的逻辑与服务器一致,在此不再赘述. TCP部分? ? ? ? 基本类? ? ? ? ? ? ? ? 1.ServerSocket:服务器使用的socket ? ? ? ? ? ? ? ? ?2.Socket服务器和客户端都会使用的socket? ? ? ? ? ? ? ? ? ? ? ?这两者的应用在后面会讲到. ? ? ? ? 回显服务器(echo server)? ? ? ? ? ? ? ? 服务器???
? ? ? ? ? ? ? ? ? ? 要点介绍: ? ? ? ? ? ? ? ? ? ? ? ? 1.TCP服务器的构造与UDP类似,都需要手动指定端口号,方便客户端访问. ? ? ? ? ? ? ? ? ? ? ? ? 2.
? ? ? ? ? ? ? ? ? ? ? ? ? 这部分与UDP服务器的差异较大.在UDP服务器中,服务器无需与客户端建立连接,只需要不断尝试接收客户端可能会发送的信息即可. ? ? ? ? ? ? ? ? ? ? ? ? ? 而在TCP服务器中,客户端和服务器需要建立连接,在建立连接之后才能继续数据通信.这个部分依赖于先前提到的Serversocket类中的accept()方法.该方法相当于获取到了客户端的socket文件,然后直接通过客户端的socket文件进行进一步操作. ? ? ? ? ? ? ? ? ? ? ? ? 3.? ??
? ? ? ? ? ? ? ? ? ? ? ? 这部分是通过获取的客户端的socket文件,进一步获得客户端传输过来的数据.我们前面提到过TCP协议是通过字节流来传输信息的,所以在对应的socket文件里,我们可以获得对应的输入输出流(即getInputStream()和getOutputStream()方法). ? ? ? ? ? ? ? ? ? ? ? ? 为了方便读写数据,我们可以把输入输出流用Scanner和PrintWriter进一步封装.然后,我们可以通过scanner.next()方法将客户端socket文件中的数据读取出来,解析后使用printWriter.println()方法将结果写回给客户端. ? ? ? ? ? ? ? ? ? ? ? ? 此处使用flush()清空缓冲区是防止返回的数据进入了缓冲区,没有及时发送给客户端. ? ? ? ? ? ? ? ? 客户端? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ??要点介绍: ? ? ? ? ? ? ? ? ? ? ? ? 1.客户端方面,我们可以发现客户端的收发逻辑和服务器几乎完全一致,这也是TCP协议和UDP协议的区别之一. 一些补充? ? ? ? ? ?端口进程查找? ? ? ? ? ? ? ? ? ?当我们发现想使用的某个端口被占用而我们不想换端口号时,我们可以在cmd下使用netstat -ano?| findstr 8000 命令(这是一个管道复合命令)查找出占用了对应端口号的进程id,示例如图: ? ? ? ? ? ? ? ? ? ? 如上图所示,我们可以查找出当前主机下,占用了8000端口进程的进程id是12032. ? ? ? ? ? ? ? 多线程的使用? ? ? ? ? ? ? ? ? ? ? ? 可以注意到在TCP服务器的实现中,我使用了线程池.这是由TCP服务器的实现逻辑决定的.可以看到在TCP服务器的processConnection()方法中,有一个死循环,这个死循环会不断读取客户端发送过来的数据(数据可能是一次大数据,也可能是多次小数据),直到读到EOF(如连接中断时就会读到). ? ? ? ? ? ? ? ? ? ? ? ? 这就导致如果在单线程的情况下,一旦一个客户端-服务器连接建立且不中断,服务器会永远阻塞在processConnection()方法中,无法与其他的客户端建立连接,这显然是无法接受的.对此,我们引入多线程:即每来一个客户端,我们就另起一个线程执行processConnection()方法.这就把accept()方法和后续的数据处理隔绝开来,实现了多客户端.采用线程池的原因是考虑到可能会有较大规模的线程的创建和销毁,线程池能节约系统资源. ? ? ? ? ? ? ? ? ? ? ? ? ?实际上,我们也可以人为的约定数据的传输格式,手动在客户端中调用close()方法(如每发送一个字符串,中断一次)?,这样可以绕开多线程. ? ? ? ? ? ? ? ? ? ? ? ? ?至于UDP协议,由于UDP协议下,传输数据不需要建立连接,所以也就不会存在"服务器等待客户端传输数据从而导致阻塞"这个问题,UDP服务器只需要不断接受数据即可,而服务器处理数据的速度是极快的,基本上可以看成是并发执行,也就没必要使用多线程.? ? ? ?? ? ?? ? ? ? ? ? ? ?? |
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
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 20:32:32- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |