Java网络
Telnet关键字
telnet是一种用于网络编程调试的强大工具,通过命令
telnet [IP地址] [端口号]
可与相应IP地址的服务器上相应端口号的程序建立连接。比如我们在操作系统命令行中输入:
telnet 129.6.15.28 13
我们就可以获得如下响应,获取了英国格林威治时间。
59245 21-01-31 10:16:38 00 0 0 799.9 UTC(NIST) *
实现客户端
我们在使用telnet关键字时,其实就相当于在操作系统上建立了基础的客户端与我们将要连接的服务器端建立了连接。
当然我们也可以通过程序实现客户端,在Java中,如下代码将会建立起一个与相应服务器的连接套接字:
Socket socket = new Socket(String host, int port);
客户端存在的目的之一是从服务器端获取数据,如果我们要从该套接字中获取数据,先获取输入流,再调用读方法即可:
InputStream in = socket.getInputStream();
int result = in.read();
当然,有获取数据就有向服务器传送数据:
OutputStream out = socket.getOutputStream();
out.write(10);
套接字中传送的数据会优先存在于目标操作系统的内核缓冲层中,直到对应套接字调用读方法,才将内核缓冲层中的数据读入程序当中加以应用。
注意:在调用读方法时,如果程序没有从相应套接字中获取到有效数据,那么程序会阻塞在此处,直至有数据可以获取时才继续运行。但由于底层操作系统的限制,长时间阻塞的程序有可能被强制终止。此时我们可以通过设置超时时间来进行处理:
socket.setSoTimeout(10000);
如若在设置时长(上述为10s)内还未获取到数据,程序将会抛出SocketTimeOutException异常,我们可以就这个异常进行捕获处理。
实现服务器端
有客户端套接字,那么就有服务器端套接字:
ServerSocket serversocket = new ServerSocket(int port);
上述代码在本机相应端口上建立了服务器套接字,客户端就可以从此处获取数据了。当然,服务器端也要与相应客户端进行对接。
Socket Client = serversocket.accept();
在有客户端连接到此服务器之前,该服务器程序都将会阻塞等待,直至有客户端连接才继续运行。
可中断套接字
当连接到套接字和从套接字获取数据时,程序都会阻塞直至获取到响应为止,如若我们需要在获取数据的过程时进行中断操作,由于程序阻塞我们很难对线程进行interrupt操作。当然,多线程进行收发数据,获取客户端不失为一种方法,但Java为我们提供了另一种方案:SocketChannel
SocketChannel socketchannel = new SocketChannel(new InetSocketAddress(host, port);
SocketChannel可以通过缓冲区进行read和write方法。
也可以不通过缓冲区进行输出输入:
Scanner scanner = new Scanner(socketchannel, "UTF-8");
OutputStream out = Channels.newOutputStream(socketchannel);
远程视频项目
通过以上知识,我们可以建立一个在服务器端和客户端进行视频通信的迷你项目。先配置本机摄像头数据获取的JAR(笔者通过Javacv进行手动构建)
该项目的难点在于网络中数据的传输模式,我们需要明白网络中的数据底层传输都是二进制实现的,无论是TCP协议,或是HTTP协议都是如此。
如若我们自己构建协议,企图一次将一张有上万个像素点的图片RGB信息直接发送给对应客户机,那么实现实时的视频通信将会变得很困难,这时:我们可以利用Java的高度封装性,利用输出输入中封装好的ImageIO类进行数据传输。(在这个类中实现的相应传输方法与图片编码格式有关)
关键代码如下:
public BufferedImage GetImage(byte[] imagebyte) {
//根据jpg编码格式解码的方法
try {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imagebyte);
return ImageIO.read(byteArrayInputStream);
}catch (IOException e){
return null;
}
}
public byte[] GetImageByte(BufferedImage bufferedImage) throws IOException {
//编码获取图像字节的方法
byte[] imagebyte = null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream);
imagebyte = byteArrayOutputStream.toByteArray();
return imagebyte;
}
客户机和服务器通过上述两个关键代码,对获取到的图像进行编码传输读取,就可以实现实时的视频通信了。
当然,考虑到效率问题,用TCP协议进行实时视频通信并不是一个好的选择,因为TCP协议规定发送端会反复确认是否收到了数据,实现较低丢包率,这对于实时视频所要求的高效性是不符合的。因此,我们可以利用UDP协议进行视频通信,在UDP协议中,发送端不会反复确认,发送数据之后就结束了,这对于实时视频的高效性来说是可以接受的,因为在一大批图像中,丢失一帧两帧的数据其实问题并不是很大。
同时,我们也可以利用图像压缩的方法进行数据传输,把数据传输量降下来,网络压力自然就降低了。