java网络通信不止UDP,TCP
预备知识
多线程
实现多线程
线程池
IO流
核心功能就是读和写
扩展功能对什么读写,怎么读写,如何优化读写
网络基础
IP
IP规定网络上所有的设备都必须有一个独一无二的IP地址,就好比是邮件上都必须注明收件人地址,邮递员才能将邮件送到。同理,每个IP信息包都必须包含有目的设备的IP地址,信息包才可以正确地送到目的地。同一设备不可以拥有多个IP地址,所有使用IP的网络设备至少有一个唯一的IP地址
OSI模型
TCP/IP模型
网络通信
基本思路:
- 存在着两台主机client,和server
- 两方通过ip和端口号确定对方位置
- 通过socket传递信息
TCP
TCP协议全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的RFC 793定义。TCP 是面向连接的、可靠的流协议。,
• 它充分地实现了数据传输时各种控制功能,可以进行丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。而这些在 UDP 中都没有。
• 此外,TCP 作为一种面向有连接的协议,只有在确认通信对端存在时才会发送数据,从而可以控制通信流量的浪费。
• 根据 TCP 的这些机制,在 IP 这种无连接的网络上也能够实现高可靠性的通信( 主要通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现)客户端创建socket,服务端接受socket,然后再通过socket传递信息
• 用到的主要的类:
• Socket
• ServerSocket
• I/O相关
• 类之间的关系
• InetSocketAddress继承SocketAddress,作用是存储ip地址和端口号
• Inet4Address与Inet6Address继承InetAddress,作用存储p地址
创建
private static class InetSocketAddressHolder {
// The hostname of the Socket Address
private String hostname;
// The IP address of the Socket Address
private InetAddress addr;
// The port number of the Socket Address
private int port;
}
//要端口和ip地址,InetAddress提供ip地址,port提供端口
分析
Socket构造方法做了些什么:
- 创建一个SocksSocketImpl实例
- 调用bind()绑定本地端口和地址(如果不为空)实际上是调用的SocksSocketImpl的bind方法
- 调用connect()连接远程主机和端口
大多数会调用两个私有的构造函数,空实现以及传入Proxy构造对象时需要手动调用connect(),进行连接
package-private一般是为了功能的实现
ServerSocket构造方法做了些什么 - 创建一个SocksSocketImpl实例
- 调用bind()绑定本地监听端口(必须绑定)和地址以及确定backlog(无法绑定抛出异常)
- 参数port指定服务器要绑定的端口(即服务器要监听的端口,注意端口号为零时自动分配),参数backlog指定客户连接请求队列的长度,参数bindAddr指定服务器要绑定的IP地址。
- 默认构造方法:允许服务器在绑定到特定端口之前,先设置ServerSocket的一些选项。因为一旦服务器与特定端口绑定,有些选项就不能再改变了。
public ServerSocket(int port, int backlog, InetAddress bindAddr)
设置Socket选项
·TCP_NODELAY:表示立即发送数据。
·SO_RESUSEADDR:表示是否允许重用Socket所绑定的本地地址。
·SO_TIMEOUT:表示接收数据时的等待超时时间。
·SO_LINGER:表示当执行Socket的close()方法时,是否立即关闭底层的Socket。
·SO_SNDBUF:表示发送数据的缓冲区的大小。
·SO_RCVBUF:表示接收数据的缓冲区的大小。
·SO_KEEPALIVE:表示对于长时间处于空闲状态的Socket,是否要自动把它关闭。
·OOBINLINE:表示是否支持发送1字节的TCP紧急数据。
设置ServerSocket选项
·SO_TIMEOUT:表示等待客户连接的超时时间。
·SO_REUSEADDR:表示是否允许重用服务器所绑定的地址。
·SO_RCVBUF:表示接收数据的缓冲区的大小
服务
• 客户端:对象创建成功并且连接到远程主机后可以获取一些信息,已经通过getOutPutStream()与getInputStream()获取I/o流对象读入和写入信息
• 服务器:监听接口接受客户端的socket对象,再从中获取信息
关闭
可以以一个标志作为是否结束的判断(比如输入bye)
半关闭:
调用Socket的close()方法关闭Socket后,它的输出流和输入流也都被关闭。有的时候,可能仅仅希望关闭输出流或输入流之一
shutdownInput():关闭输入流。shutdownOutput():关闭输出流。
完全关闭:
为了确保关闭,要将socket.close放在finally中(实现了java.lang.Auto Closable接口。这意味着如果在try代码块中打开或创建了这些类的实例,那么即使程序没有显式地关闭它们,Java虚拟机也会在退出try代码块时自动关闭它们,释放相关的资源)
服务器同时与多个客户端连接
方式1:为每个客户分配一个工作线程。
方式2:创建一个线程池,由其中的工作线程来为客户服务.
方式3:利用JDK的Java类库中现成的线程池,由它的工作线程来为客户服务。
单独分配线程
- 主线程负责接收客户端的连接,创造工作线程负责通信
- 每个线程负责一个客户端
自定义线程池
使用jdk内置线程池
java.util.concurrent
线程池的缺点
1.死锁
2.系统资源不足
3.并发错误
4.线程泄漏
5.任务过载
UDP
• UDP 不提供复杂的控制机制,利用 IP 提供面向无连接的通信服务。
• 并且它是将应用程序发来的数据在收到的那一刻,立即按照原样发送到网络上的一种机制。即使是出现网络拥堵的情况,UDP 也无法进行流量控制等避免网络拥塞行为。
• 此外,传输途中出现丢包,UDP 也不负责重发。
• 甚至当包的到达顺序出现乱序时也没有纠正的功能。
• 如果需要以上的细节控制,不得不交由采用 UDP 的应用程序去处理。
• UDP 常用于一下几个方面:1.包总量较少的通信(DNS、SNMP等);2.视频、音频等多媒体通信(即时通信);3.限定于 LAN 等特定网络中的应用通信;4.广播通信(广播、多播)。
• 客户端通过DataPacket保存信息,DataSocket发送packet信息,服务端通过监听端口接受Packet
DataPacket
• 发送信息的packet必须要指定地址和端口,接受信息的packet则不必指定
• 数据报中只能存放字节形式的数据,在发送方,需要把其他格式的数据转换为字节序列,在接收方,需要把字节序列转换为原来格式的数据(可以利用ByteArrayOutputStream和DataOuputStream)
• 可以利用ByteArrayInputStream和DataInputStream来把字节序列转换为原来格式的数据
DataSocket
DatagramSocket类负责接收和发送数据报。在客户程序中,一般由操作系统为DatagramSocket类分配本地端口,这种端口也被称为匿名端口。在服务器程序中,一般由程序显式地为DatagramSocket类指定本地端口。
• 每个DatagramSocket对象都会与一个本地端口绑定(在构造方法中就自动绑定,不提供则随机分配),在此端口监听发送过来的数据报。在recieve()方法中还会进一步检查.
• connect()是可选的 ,但与TCP的连接不一样,前者不建立TCP意义上的连接,而是限制当前DatagramSocket或DatagramChannel只对参数指定的远程主机和UDP端口收发数据报。
组播
单播:提供点对点的通信。发送者每次发送的数据有着唯一的目的地址,只被一个接收者接收。前面介绍的TCP Socket和UDP Socket都只支持单播。
组播:发送者每次发送的数据可以被小组内的所有接收者接收。
广播:发送者每次发送的数据可以被传播范围内的所有接收者接收
原理
组播地址:
组播组内的所有主机共享同一个地址,这种地址被称为组播地址,组播地址是范围在224.0.0.0 ~239.255.255.255之间的IP地址。此范围内的所有地址的前4个二进制位都是“1110”。组播地址也被称为D类IP地址,
使用UDP:
大多数组播数据为音频或视频,这些数据一般都很大,即便部分数据在传输途中被丢失,接收方也仍然能识别信号。因此组播数据通过UDP发送,虽然不可靠,但比面向连接的TCP的传输速度快3倍以上。
TTL(Time to live)
这也是与单播UDP不同的地方
好处
如图:在从主机1到路由器2的途中数据只需要传输一次,(单播对数据进行了不必要的复制,浪费了许多网络带宽。如果采用组播,则可以大大提高传输效率,路由器会动态决定组播数据的路由,只在必要时才复制数据)。
实现
java.net.MulticastSocket具有组播的功能,它是DatagramSocket的子类
非阻塞通信
什么是阻塞
放弃CPU,暂停运行,只有等到导致阻塞的原因消除,才能恢复运行;或者被其他线程中断,该线程会退出阻塞状态,并且抛出InterruptedException(比如烧水,一直等水烧开后再做其他事)
阻塞的原因
线程阻塞
1.线程执行了Thread.sleep(int n)方法,线程放弃CPU,睡眠n ms,然后恢复运行。
2.线程要执行一段同步代码,由于无法获得相关的同步锁,只好进入阻塞状态,等到获得了同步锁,才能恢复运行。
3. 线程执行了一个对象的wait()方法,进入阻塞状态,只有等到其他线程执行了该对象的notify()或notifyAll() 方法,才可能将其唤醒。
4. 线程执行I/O操作或进行远程通信时,会因为等待相关的资源而进入阻塞状态。例如当线程执行System.in.read() 方法时,如果用户没有向控制台输入数据,则该线程会一直等读到了用户的输入数据才从read()方法返回。
客户端阻塞
- 请求与服务器建立连接时,即当线程执行Socket的带参数的构造方法,或执行Socket的connect()方法时,会进 入阻塞状态,直到连接成功,此线程才从Socket的构造方法或connect()方法返回。
- 线程从Socket的输入流读入数据时,如果没有足够的数据,就会进入阻塞状态,直到读到了足够的数据,或者到达 输入流的末尾,或者出现了异常,才从输入流的read()方法返回或异常中断.
- 输入流中有多少数据才算足够呢?这要看线程执行的read()方法的类。
(1)int read():只要输入流中有1字节,就算足够。
(2)int read(byte[] buff):只要输入流中的字节数目与参数buff数组的长度相同,就算足够。
(3)String readLine():只要输入流中有1行字符串,就算足够。 - 线程向Socket的输出流写一批数据时,可能会进入阻塞状态,等到输出了所有的数据,或者出现异常,才从输出流 的write()方法返回或异常中断。
- 如果调用Socket的setSoLinger()方法设置了关闭Socket的延迟时间,那么当线程执行Socket的close()方法 时,会进入阻塞状态,直到底层Socket发送完所有剩余数据,或者超过了setSoLinger()方法设置的延迟时间,才从close()方法返回。
服务端阻塞
- 线程执行ServerSocket的accept()方法,等待客户的连接,直到接收到了客户连接,才从accept()方法返回。
- 线程从Socket的输入流读入数据时,如果输入流没有足够的数据,就会进入阻塞状态。
- 线程向Socket的输出流写一批数据时,可能会进入阻塞状态,等到输出了所有的数据,或者出现异常,才从输出流 的write()方法返回或异常中断。
实现
java.nio.channels包提供了支持非阻塞通信的类
·ServerSocketChannel:ServerSocket的替代类,支持阻塞通信与非阻塞通信。
·SocketChannel:Socket的替代类,支持阻塞通信与非阻塞通信。
·Selector:为ServerSocketChannel监控接收连接就绪事件,为 SocketChannel监控连接就绪、读就绪和写就绪事件。
·SelectionKey:代表ServerSocketChannel以及SocketChannel向Selector注册事件的句柄。当一个SelectionKey对象位于Selector对象的selected-keys集合中,就表示与这个SelectionKey对象相关的事件发生了。
最新文章
- C语言学习 第十一次作业总结
- 选中多个<;ul>;中的第一个<;li>;方法
- 用java下载hdfs文件报NullPointerException
- Leetcode: Count The Repetitions
- bzoj1904: Musical Water-fence
- 计算2的n次方的三种方法(C语言实现)
- 【boost】使用lambda表达式和generate_n生成顺序序列
- 记录一次Android交叉编译ffmpeg排查错误
- SQL语句操作文件
- C# 多线程详解
- 【USACO 1.5.2】回文质数
- TCP/IP学习笔记
- shutdown函数
- IntelliJ IDEA 常用设置
- A1051. Pop Sequence
- 一次线上FullGC问题记录
- 网络通信 &; 初识socket
- Java操作Kafka
- 把旧系统迁移到.Net Core 2.0 日记(9) -- T4 Template
- Redis集群模式部署