java 基础知识之网路通信总结
在这篇文章里,我们主要讨论如何使用Java实现网络通信,包括TCP通信、UDP通信、多播以及NIO。
TCP连接
TCP的基础是Socket,在TCP连接中,我们会使用ServerSocket和Socket,当客户端和服务器建立连接以后,剩下的基本就是对I/O的控制了。
我们先来看一个简单的TCP通信,它分为客户端和服务器端。
客户端代码如下:
简单的TCP客户端 import java.net.*; import java.io.*; public class SimpleTcpClient { public static void main(String[] args) throws IOException { Socket socket = null; BufferedReader br = null; PrintWriter pw = null; BufferedReader brTemp = null; try { socket = new Socket(InetAddress.getLocalHost(), 5678); br = new BufferedReader(new InputStreamReader(socket.getInputStream())); pw = new PrintWriter(socket.getOutputStream()); brTemp = new BufferedReader(new InputStreamReader(System.in)); while(true) { String line = brTemp.readLine(); pw.println(line); pw.flush(); if (line.equals("end")) break; System.out.println(br.readLine()); } } catch(Exception ex) { System.err.println(ex.getMessage()); } finally { if (socket != null) socket.close(); if (br != null) br.close(); if (brTemp != null) brTemp.close(); if (pw != null) pw.close(); } } }
服务器端代码如下:
简单版本TCP服务器端 import java.net.*; import java.io.*; public class SimpleTcpServer { public static void main(String[] args) throws IOException { ServerSocket server = null; Socket client = null; BufferedReader br = null; PrintWriter pw = null; try { server = new ServerSocket(5678); client = server.accept(); br = new BufferedReader(new InputStreamReader(client.getInputStream())); pw = new PrintWriter(client.getOutputStream()); while(true) { String line = br.readLine(); pw.println("Response:" + line); pw.flush(); if (line.equals("end")) break; } } catch(Exception ex) { System.err.println(ex.getMessage()); } finally { if (server != null) server.close(); if (client != null) client.close(); if (br != null) br.close(); if (pw != null) pw.close(); } } }
这里的服务器的功能非常简单,它接收客户端发来的消息,然后将消息“原封不动”的返回给客户端。当客户端发送“end”时,通信结束。
上面的代码基本上勾勒了TCP通信过程中,客户端和服务器端的主要框架,我们可以发现,上述的代码中,服务器端在任何时刻,都只能处理来自客户端的一个请求,它是串行处理的,不能并行,这和我们印象里的服务器处理方式不太相同,我们可以为服务器添加多线程,当一个客户端的请求进入后,我们就创建一个线程,来处理对应的请求。
改善后的服务器端代码如下:
多线程版本的TCP服务器端 import java.net.*; import java.io.*; public class SmartTcpServer { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(5678); while(true) { Socket client = server.accept(); Thread thread = new ServerThread(client); thread.start(); } } } class ServerThread extends Thread { private Socket socket = null; public ServerThread(Socket socket) { this.socket = socket; } public void run() { BufferedReader br = null; PrintWriter pw = null; try { br = new BufferedReader(new InputStreamReader(socket.getInputStream())); pw = new PrintWriter(socket.getOutputStream()); while(true) { String line = br.readLine(); pw.println("Response:" + line); pw.flush(); if (line.equals("end")) break; } } catch(Exception ex) { System.err.println(ex.getMessage()); } finally { if (socket != null) try { socket.close(); } catch (IOException e1) { e1.printStackTrace(); } if (br != null) try { br.close(); } catch (IOException e) { e.printStackTrace(); } if (pw != null) pw.close(); } } }
修改后的服务器端,就可以同时处理来自客户端的多个请求了。
在编程的过程中,我们会有“资源”的概念,例如数据库连接就是一个典型的资源,为了提升性能,我们通常不会直接销毁数据库连接,而是使用数据库连接池的方式来对多个数据库连接进行管理,已实现重用的目的。对于Socket连接来说,它也是一种资源,当我们的程序需要大量的Socket连接时,如果每个连接都需要重新建立,那么将会是一件非常没有效率的做法。
和数据库连接池类似,我们也可以设计TCP连接池,这里的思路是我们用一个数组来维持多个Socket连接,另外一个状态数组来描述每个Socket连接是否正在使用,当程序需要Socket连接时,我们遍历状态数组,取出第一个没被使用的Socket连接,如果所有连接都在使用,抛出异常。这是一种很直观简单的“调度策略”,在很多开源或者商业的框架中(Apache/Tomcat),都会有类似的“资源池”。
TCP连接池的代码如下:
一个简单的TCP连接池 import java.net.*; import java.io.*; public class TcpConnectionPool { private InetAddress address = null; private int port; private Socket[] arrSockets = null; private boolean[] arrStatus = null; private int count; public TcpConnectionPool(InetAddress address, int port, int count) { this.address = address; this.port = port; this .count = count; arrSockets = new Socket[count]; arrStatus = new boolean[count]; init(); } private void init() { try { for (int i = 0; i < count; i++) { arrSockets[i] = new Socket(address.getHostAddress(), port); arrStatus[i] = false; } } catch(Exception ex) { System.err.println(ex.getMessage()); } } public Socket getConnection() { if (arrSockets == null) init(); int i = 0; for(i = 0; i < count; i++) { if (arrStatus[i] == false) { arrStatus[i] = true; break; } } if (i == count) throw new RuntimeException("have no connection availiable for now."); return arrSockets[i]; } public void releaseConnection(Socket socket) { if (arrSockets == null) init(); for (int i = 0; i < count; i++) { if (arrSockets[i] == socket) { arrStatus[i] = false; break; } } } public void reBuild() { init(); } public void destory() { if (arrSockets == null) return; for(int i = 0; i < count; i++) { try { arrSockets[i].close(); } catch(Exception ex) { System.err.println(ex.getMessage()); continue; } } } }
UDP连接
UDP是一种和TCP不同的连接方式,它通常应用在对实时性要求很高,对准确定要求不高的场合,例如在线视频。UDP会有“丢包”的情况发生,在TCP中,如果Server没有启动,Client发消息时,会报出异常,但对UDP来说,不会产生任何异常。
UDP通信使用的两个类时DatagramSocket和DatagramPacket,后者存放了通信的内容。
下面是一个简单的UDP通信例子,同TCP一样,也分为Client和Server两部分,Client端代码如下:
UDP通信客户端 import java.net.*; import java.io.*; public class UdpClient { public static void main(String[] args) { try { InetAddress host = InetAddress.getLocalHost(); int port = 5678; BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); while(true) { String line = br.readLine(); byte[] message = line.getBytes(); DatagramPacket packet = new DatagramPacket(message, message.length, host, port); DatagramSocket socket = new DatagramSocket(); socket.send(packet); socket.close(); if (line.equals("end")) break; } br.close(); } catch(Exception ex) { System.err.println(ex.getMessage()); } } }
Server端代码如下:
UDP通信服务器端 import java.net.*; import java.io.*; public class UdpServer { public static void main(String[] args) { try { int port = 5678; DatagramSocket dsSocket = new DatagramSocket(port); byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); while(true) { dsSocket.receive(packet); String message = new String(buffer, 0, packet.getLength()); System.out.println(packet.getAddress().getHostName() + ":" + message); if (message.equals("end")) break; packet.setLength(buffer.length); } dsSocket.close(); } catch(Exception ex) { System.err.println(ex.getMessage()); } } }
这里,我们也假设和TCP一样,当Client发出“end”消息时,认为通信结束,但其实这样的设计不是必要的,Client端可以随时断开,并不需要关心Server端状态。
多播(Multicast)
多播采用和UDP类似的方式,它会使用D类IP地址和标准的UDP端口号,D类IP地址是指224.0.0.0到239.255.255.255之间的地址,不包括224.0.0.0。
多播会使用到的类是MulticastSocket,它有两个方法需要关注:joinGroup和leaveGroup。
下面是一个多播的例子,Client端代码如下:
多播通信客户端 import java.net.*; import java.io.*; public class MulticastClient { public static void main(String[] args) { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); try { InetAddress address = InetAddress.getByName("230.0.0.1"); int port = 5678; while(true) { String line = br.readLine(); byte[] message = line.getBytes(); DatagramPacket packet = new DatagramPacket(message, message.length, address, port); MulticastSocket multicastSocket = new MulticastSocket(); multicastSocket.send(packet); if (line.equals("end")) break; } br.close(); } catch(Exception ex) { System.err.println(ex.getMessage()); } } }
服务器端代码如下:
多播通信服务器端 import java.net.*; import java.io.*; public class MulticastServer { public static void main(String[] args) { int port = 5678; try { MulticastSocket multicastSocket = new MulticastSocket(port); InetAddress address = InetAddress.getByName("230.0.0.1"); multicastSocket.joinGroup(address); byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); while(true) { multicastSocket.receive(packet); String message = new String(buffer, packet.getLength()); System.out.println(packet.getAddress().getHostName() + ":" + message); if (message.equals("end")) break; packet.setLength(buffer.length); } multicastSocket.close(); } catch(Exception ex) { System.err.println(ex.getMessage()); } } }
NIO(New IO)
NIO是JDK1.4引入的一套新的IO API,它在缓冲区管理、网络通信、文件存取以及字符集操作方面有了新的设计。对于网络通信来说,NIO使用了缓冲区和通道的概念。
下面是一个NIO的例子,和我们上面提到的代码风格有很大的不同。
NIO例子 import java.io.*; import java.nio.*; import java.nio.channels.*; import java.nio.charset.*; import java.net.*; public class NewIOSample { public static void main(String[] args) { String host="127.0.0.1"; int port = 5678; SocketChannel channel = null; try { InetSocketAddress address = new InetSocketAddress(host,port); Charset charset = Charset.forName("UTF-8"); CharsetDecoder decoder = charset.newDecoder(); CharsetEncoder encoder = charset.newEncoder(); ByteBuffer buffer = ByteBuffer.allocate(1024); CharBuffer charBuffer = CharBuffer.allocate(1024); channel = SocketChannel.open(); channel.connect(address); String request = "GET / \r\n\r\n"; channel.write(encoder.encode(CharBuffer.wrap(request))); while((channel.read(buffer)) != -1) { buffer.flip(); decoder.decode(buffer, charBuffer, false); charBuffer.flip(); System.out.println(charBuffer); buffer.clear(); charBuffer.clear(); } } catch(Exception ex) { System.err.println(ex.getMessage()); } finally { if (channel != null) try { channel.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
上述代码会试图访问一个本地的网址,然后将其内容打印出来。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
OSI七层模型及相关考点 记忆技巧:All people seem to need data processing. All application 应用层 People presentation 表示层 Seem session 会话层 To transport 传输层 need network 网络层 data datalink 数据链路层 processing physical 物理层 传输
提示 视频 PPT 下载 TCP 通信原理 TCP 把连接作为最基本的对象,每一条 TCP 连接都有两个端点,这种端点我们叫作套接字(socket),它的定义为端口号拼接到 IP 地址即构成了套接字,例如,若 IP 地址为 192.3.4.16 而端口号为 80,那么得到的套接字为192.3.4.16:80。IP 协议虽然能把数据报文送到目的主机,但是并没有交付给主机的具体应用进程。而端到端的通信
本文向大家介绍PHP的Socket通信之UDP通信实例,包括了PHP的Socket通信之UDP通信实例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了PHP的Socket通信之UDP通信方法。分享给大家供大家参考。具体如下: 1.创建一简单的UDP服务器 2.简单的客户端 希望本文所述对大家的php程序设计有所帮助。
用途: 提供对网络通信的访问 Addressing, Protocol Families and Socket Types Looking up Hosts on the Network Finding Service Information Looking Up Server Addresses IP Address Representations TCP/IP Client and Serve
我想在Netty nio中创建一个包含两个客户端和一个服务器的通信系统。更具体地说,首先,我希望当两个客户端与服务器连接时,从服务器发送消息,然后能够在两个客户端之间交换数据。我正在使用这个例子中提供的代码。我对代码的修改可以在这里找到:链接 似乎通道读取在服务器处理程序中工作,因此它始终返回 1,但当连接第二个客户端时,它不会更改为 2。当两个客户端都连接到服务器时,如何从服务器正确检查?如何从
这是我的python modbus tcp通信代码,它在这条线路上等待,然后停止连接,这是我的错误: (如果我使用从程序也不工作)在我的客户端,我使用这个代码: 这是主控端: