TCP和UDP是两个传输层协议,广泛应用于网络中不同主机之间传输数据。对任何程序员来说,熟悉TCP和UDP的工作方式都是至关重要的。这就是为什么TCP和UDP是一个流行的Java编程面试问题。
Java开发人员应该理解这两个协议的理由是, java广泛用于编写多线程、并行以及可伸缩的服务器程序。Java还提供了丰富的基于TCP和UDP套接字编程API。
什是TCP?
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP是面向连接的,可靠的,缓慢的,可靠交付以及保证消息顺序的。
所谓千言不胜一图。
TCP充分实现了数据传输时各种控制功能,可以进行丢包的重发控制,还可以对次序乱掉的分包进行顺序控制。而这些在UDP中都没有。此外,TCP作为一种面向有连接的协议,只有在确认通信对端存在时才会发送数据,从而可以控制通信流量的浪费。TCP通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输。
什么是UDP?
UDP(User Datagram Protocol 用户数据报协议)是一种面向无连接的、不可靠的、基于报文的传输层通信协议。
UDP是无连接的,不可靠的,没有序列保证,但是一个快速传输的协议。
既然有了保证可靠传输的TCP协议,为什么还要非可靠传输的UDP协议呢?
主要的原因有两个。
(1)可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。
(2)在许多应用中并不需要保证严格的传输可靠性,比如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些.
TCP和UDP的异同点:
它俩都是处于计算机网络OSI模型的第四层(传输层)中。
TCP层是位于IP层之上。同样,对于UPD也是位于IP层之上。
如果你不想丢失任何消息,使用TCP协议,而UDP能够高速传输数据,并且丢失少量的数据包是可以接受的,如视频流或在线多玩家游戏。
我将从十大点UDP和TCP之间的差异,例如,连接步骤,排序,速度,可靠性,开销,头大小,拥塞控制,应用以及基于TCP和UDP协议不同,它们如何传输数据等方面进行深入剖析。
(1)基于连接vs无连接
它们之间的第一点并且最重要的区别是:TCP是面向连接的协议,而UDP是无连接的协议。
对于TCP,这意味着当一个客户端和一个服务器通过TCP发送数据之前,必须先建立连接,他们可以通过TCP发送数据。建立连接的过程也被称为TCP握手,它通过控制消息在客户端和服务器之间互换来实现。下面的图形象描述了TCP握手过程。客户端,它也是TCP连接的发起者,发送一个SYN消息给服务器,该服务器端正在监听某个TCP端口。服务器接收该消息并发送一个SYN-ACK消息,客户端接受到该消息之后会再回一个ACK消息。一旦服务器收到ACK消息,TCP连接就建立成功,准备数据传输了。
另一方面,UDP是无连接的协议,和点对点连接之前不需要发送消息。这就是为什么,UDP更加适合消息的多播发布,从单个点向多个点传输消息。
TCP握手的过程图
(2)可靠性
TCP提供交付保证,这意味着一个使用TCP协议发送的消息是保证交付给客户端的。如果消息在传输过程中丢失,那么它将重发,这是由TCP协议本身控制的。
另一方面,UDP是不可靠的,它不提供任何交付的保证。一个数据报包在运输途中可能会丢失,这就是为什么UDP是不适合保证交付的项目。
(3)有序性
除了提供交付保证,为TCP也保证了消息的有序性。该消息将以从服务器端发出的同样的顺序发送到客户端,尽管这些消息到网络的另一端时可能是无序的。TCP协议将会为你排好序。UDP不提供任何有序性或序列性的保证。数据包将以任何可能的顺序到达。这就是为什么TCP是适合需要顺序交付方式的应用,尽管有基于UDP的协议通过使用序列号和重传来提供有序和可靠性的应用,如TIBCO Rendezvous,它实际上就是一个基于UDP的应用。
(4)数据边界
TCP不保存数据的边界,而UDP保证。TCP在传输控制协议,数据以字节流的形式发送,并没有明显的标志表明传输信号消息(段)的边界。
在UDP中,数据包单独发送的,只有当它们到达时,才会再次集成。包有明确的界限来哪些包已经收到,这意味着在消息发送后,在接收器接口将会有一个读操作,来生成一个完整的消息。虽然TCP也将在收集所有字节之后生成一个完整的消息,但是这些信息在传给传输给接受端之前将储存在TCP缓冲区,以确保更好的使用网络带宽。
(5)速度
总而言之,TCP速度比较慢,而UDP速度比较快,因为TCP必须创建连接,以保证消息的可靠交付和有序性,他需要做比UDP多的多的事。这就是为什么UDP更适用于对速度比较敏感的应用,例如:在线视频媒体,电视广播和多人在线游戏。
(6)重量级vs轻量级
由于上述的开销,TCP被认为是重量级的协议,而与之相比,UDP协议则是一个轻量级的协议。因为UDP传输的信息中不承担任何间接创造连接,保证交货或秩序的的信息。这也反映在用于承载元数据的头的大小。
(7)头大小
TCP具有比UDP更大的头。一个TCP数据包报头的大小是20字节,UDP数据报报头是8个字节。TCP报头中包含序列号,ACK号,数据偏移量,保留,控制位,窗口,紧急指针,可选项,填充项,校验位,源端口和目的端口。而UDP报头只包含长度,源端口号,目的端口,和校验和。下图是TCP和UDP头:
(8)拥塞控制和流量控制
这里,我先谈及拥塞控制和流量控制的区别。
a. 拥塞控制和流量控制的区别
流量控制是在一对给定的发送方和接收方之间点对点通信量的控制。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
拥塞控制是一个全局性的过程,涉及到网络中所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。
b. 拥塞控制和流量控制的联系
流量控制限制了进入网络中的信息总量,可以在一定程度上减缓拥塞的作用。
TCP有流量控制。在任何用户数据可以被发送之前,TCP需要三数据包来设置一个套接字连接。TCP处理的可靠性和拥塞控制。另一方面,UDP不能进行流量控制。
以下可以简单地说明,为什么TCP有流量控制,UDP没有流量控制。
(1)可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间.和网络的带宽,因此TCP传输的效率不如UDP高。
(2)在许多应用中并不需要保证严格的传输可靠性,比如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。
(9)使用和应用
在互联网中,TCP和UDP都运行在哪些环境中了?在了解了TCP和UDP之间的关键差异之后,我们可以很容易地得出结论,哪种情况适合他们。
由于TCP提供可靠交付和有序性的保证,它是最适合需要高可靠并且对传输时间要求不高的应用。
UDP是更适合的应用程序需要快速,高效的传输的应用,如游戏、视频流或在线多玩家游戏、传输视频等。
UDP是无状态的性质,在服务器端需要对大量客户端产生的少量请求进行应答的应用中是非常有用的。在实践中,TCP被用于金融领域,如FIX协议是一种基于TCP的协议,而UDP是大量使用在游戏和娱乐场所。
(10)基于TCP和UDP的协议
基于TCP协议的最好例子是HTTP协议和HTTPS协议,他们几乎存在于互联网的任何地方,实际上,绝大多数你所熟悉的通常协议,都是基于TCP的,例如:Telnet,FTP以及SMTP协议。
更具体,可见
牛客网Java刷题知识点之UDP协议是否支持HTTP和HTTPS协议?为什么?TCP协议支持吗?
UDP协议没有TCP协议那么受欢迎,但是也被广泛应用,比如DHCP以及DNS协议,其他还有一些基于UDP的协议如SNMP,TFTP,BOOTP以及NFS(早期版本)。
TCP和UDP典型的应用(笔试面试常考)
TCP的三次握手
UDP提供服务的应用:RPC、RTP。
由于TCP提供可靠交付和有序性的保证,它是最适合需要高可靠并且对传输时间要求不高的应用。
UDP是更适合的应用程序需要快速,高效的传输的应用,如游戏、视频流或在线多玩家游戏、传输视频等。
UDP是无状态的性质,在服务器端需要对大量客户端产生的少量请求进行应答的应用中是非常有用的。在实践中,TCP被用于金融领域,如FIX协议是一种基于TCP的协议,而UDP是大量使用在游戏和娱乐场所。
腾讯QQ在传输消息有采用UDP,当时好奇UDP传输协议不可靠为啥腾讯要用这个协议,联想到我们在用qq过程中会发现qq消息其实算很快的,所以可能是想应用UDP协议效率高,速度快,占用资源少的特点吧,但是其实QQ也不完全是纯粹的UDP协议,至于这个这里就不讨论了。
QQ 为什么以 UDP 协议为主,以 TCP 协议为辅?具体,大家可以见http://blog.csdn.net/chenlycly/article/details/52344582
socket是什么?(笔试面试常考)
这是为了实现以上的通信过程而建立成来的通信管道,其真实的代表是客户端和服务器端的一个通信进程,双方进程通过socket进行通信,而通信的规则采用指定的协议。
socket只是一种连接模式,不是协议,socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。
通过Socket,我们才能使用TCP/IP协议。tcp、udp,简单的说(虽然不准确)是两个最基本的协议。
很多其它协议都是基于这两个协议。如http就是基于tcp的,用socket可以创建tcp连接,也可以创建udp连接。
这意味着,用socket可以创建任何协议的连接,因为其它协议都是基于此的。
一个Socket实例由一个IP地址和一个端口号唯一确定。
Socket是一种用于网络通信的低层开发接口,借助于通信两端的应用程序(实际上是Socket)可以利用输入输出流完成数据交换。
Socket 传输的优点:
1) 传输数据为字节级,传输数据可自定义,数据量小(对于手机应用讲:费用低);
2)传输数据时间短,性能高;
3)适合于客户端和服务器端之间信息实时交互;
4)可以加密,数据安全性强。
Socket 传输的缺点:
1)需对传输的数据进行解析,转化成应用级的数据;
2)对开发人员的开发水平要求高;
3)相对于Http协议传输,增加了开发量。
Java中的Socket编程接口介绍
Java为Socket编程封装了几个重要的类。
1.1 Socket类
Socket类实现了一个客户端socket,作为两台机器通信的终端,默认采用的传输层协议为TCP,是一个可靠传输的协议。Socket类除了构造函数返回一个socket外,还提供了connect, getOutputStream, getInputStream和close方法。connect方法用于请求一个socket连接,getOutputStream用于获得写socket的输出流,getInputStream用于获得读socket的输入流,close方法用于关闭一个流。
1.2 DatagramSocket类
DatagramSocket类实现了一个发送和接收数据报的socket,传输层协议使用UDP,不能保证数据报的可靠传输。DataGramSocket主要有send, receive和close三个方法。send用于发送一个数据报,Java提供了DatagramPacket对象用来表达一个数据报。receive用于接收一个数据报,调用该方法后,一直阻塞接收到直到数据报或者超时。close是关闭一个socket。
1.3 ServerSocket类
ServerSocket类实现了一个服务器socket,一个服务器socket等待客户端网络请求,然后基于这些请求执行操作,并返回给请求者一个结果。ServerSocket提供了bind、accept和close三个方法。bind方法为ServerSocket绑定一个IP地址和端口,并开始监听该端口。accept方法为ServerSocket接受请求并返回一个Socket对象,accept方法调用后,将一直阻塞直到有请求到达。close方法关闭一个ServerSocket对象。
1.4 SocketAddress
SocketAddress提供了一个socket地址,不关心传输层协议。这是一个虚类,由子类来具体实现功能、绑定传输协议。它提供了一个不可变的对象,被socket用来绑定、连接或者返回数值。
1.5 InetSocketAddress
InetSocketAddress实现了IP地址的SocketAddress,也就是有IP地址和端口号表达Socket地址。如果不制定具体的IP地址和端口号,那么IP地址默认为本机地址,端口号随机选择一个。
1.6. DatagramPacket
DatagramSocket是面向数据报socket通信的一个可选通道。数据报通道不是对网络数据报socket通信的完全抽象。socket通信的控制由DatagramSocket对象实现。DatagramPacket需要与DatagramSocket配合使用才能完成基于数据报的socket通信。
TCP的网络编程(笔试面试常考)
使用TCP套接字编程可以实现基于TCP/IP协议的面向连接的通信,它分为服务器端和客户端两部分,其主要实现过程如下:
TCP编程的客户端一般步骤
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置要连接的对方的IP地址和端口等属性;
5、连接服务器,用函数connect();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
TCP编程的服务器端一般步骤
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt(); * 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、开启监听,用函数listen();
5、接收客户端上来的连接,用函数accept();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
8、关闭监听;
下面是基于TCP连接的Socket通信。是Server端等待从Client端接收一条消息,然后再给客户端发送一个消息。
基于TCP服务端的思路:服务器端首先实例化ServerSocket对象,然后为其绑定一个本机地址,并开始监听。一直阻塞状态下等待客户端请求,当获得客户端连接请求后,返回一个socket对象。然后用这个socket接收一条消息,并发送一条消息。
代码如下:
package server.socket.java; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; public class SocketTcp { static private String TAG = "SocketTcp: "; public static void main(String[] args){ try { ServerSocket server = new ServerSocket(); SocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 10001); server.bind(address); System.out.println("==waiting for being connected..."); Socket client = server.accept(); System.out.println("==connected with " + client.getRemoteSocketAddress() ); PrintWriter socketOut = new PrintWriter(client.getOutputStream()); System.out.println("==waiting message from client..."); byte buf[] = new byte[1024]; if ( client.getInputStream().read(buf) > 0 ) { System.out.println("Receive Message: " + new String(buf)); } System.out.println("==sending message to client..."); String sendStr = "This is the message for client."; socketOut.write(sendStr); socketOut.flush(); socketOut.close(); client.close(); server.close(); } catch (IOException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } } }
基于TCP的客户端思路:客户端首先实例化一个socket对象,用这个对象连接服务器端。连接成功后,发送一条消息,然后等待接收一条消息。代码如下:
package client.socket.java; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; public class SocketTcp { static private String TAG = "SocketTcp: "; public static void main(String[] args){ try { final Socket socket = new Socket(); SocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 10001); System.out.println("==connecting to server ..."); socket.connect(address); PrintWriter socketOut = new PrintWriter(socket.getOutputStream()); BufferedReader socketIn = new BufferedReader( new InputStreamReader(socket.getInputStream()) ); String sendStr = "This is the message for server."; System.out.println("==sending message to server ..."); socketOut.write(sendStr); socketOut.flush(); System.out.println("==waiting message from server ..."); String receiveStr = socketIn.readLine(); System.out.println("Receive Message: " + receiveStr); socketOut.close(); socketIn.close(); socket.close(); } catch (IOException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } finally { } } }
UDP的网络编程(笔试面试常考)
使用UDP套接字编程可以实现基于UDP/IP协议的面向连接的通信,它分为服务器端和客户端两部分,其主要实现过程如下:
UDP编程的客户端一般步骤
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置对方的IP地址和端口等属性;
5、发送数据,用函数sendto();
6、关闭网络连接;
UDP编程的服务器端一般步骤
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、循环接收数据,用函数recvfrom();
5、关闭网络连接;
基于UDP的Socket编程与基于TCP的socket编程稍有不同,socket server和client都用DatagramSocket实现。
下面例子是Server端等待从Client端接收一条消息,然后再给客户端发送一个消息。
基于UDP的服务端思路:服务器端首先实例化DatagramSocket对象,然后为其绑定一个本机地址,并开始监听。一直阻塞状态下等待从客户端接收数据报。然后从数据报中获取数据报的源地址,然后用这个源地址作为目的地址打包一个数据报,然后发送出去。
代码如下:
package server.socket.java; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.SocketException; import java.net.UnknownHostException; public class SocketUdp { final private static String TAG = "SocketUdp: "; public static void main(String args[]) { DatagramSocket socket = null; DatagramPacket datapacket = null; InetSocketAddress address = null; try { address = new InetSocketAddress(InetAddress.getLocalHost(), 7778); socket = new DatagramSocket(address); // socket.bind(address); byte buf[] = new byte[1024]; datapacket = new DatagramPacket(buf, buf.length); System.out.println("==block for receive messages..."); socket.receive(datapacket); buf = datapacket.getData(); InetAddress addr = datapacket.getAddress(); int port = datapacket.getPort(); System.out.println("Message Content: " + new String(buf) ); System.out.println("Receive From " + addr + ":" + port); SocketAddress toAddress = datapacket.getSocketAddress(); String sendStr = "I'm Server, this is the message for client."; buf = sendStr.getBytes(); datapacket = new DatagramPacket(buf, buf.length); datapacket.setSocketAddress(toAddress); socket.send(datapacket); System.out.println("==message sended"); } catch (UnknownHostException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } catch (SocketException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } catch (IOException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } } }
基于UDP的客户端思路: 客户端首先实例化一个DatagramSocket对象。利用服务器地址和端口号作为目的地址打包一个数据报,并发送。然后等待从服务器回复的数据报。
代码如下:
package client.socket.java; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; import java.net.UnknownHostException; public class SocketUdp { final private static String TAG = "SocketUdp: "; public static void main(String args[]) { try { DatagramSocket getSocket = new DatagramSocket(); DatagramPacket datapacket = null; InetSocketAddress toAddress = new InetSocketAddress(InetAddress.getLocalHost(), 7778); String sendStr = "I'm client, this is the message for server."; byte buf[] = sendStr.getBytes(); datapacket = new DatagramPacket(buf, buf.length); datapacket.setSocketAddress(toAddress); getSocket.send(datapacket); System.out.println("==message sended"); System.out.println("==block for receive messages..."); getSocket.receive(datapacket); buf = datapacket.getData(); System.out.println("Message Content: " + new String(buf)); } catch (SocketException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } catch (UnknownHostException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); } catch (IOException e) { System.out.println(TAG + e.getMessage()); e.printStackTrace(); }
本文转自大数据躺过的坑博客园博客,原文链接:http://www.cnblogs.com/zlslch/p/7617242.html,如需转载请自行联系原作者