TCP 연결
TCP의 기본은 소켓입니다. TCP 연결에서는 클라이언트와 서버가 연결을 설정한 후 나머지는 기본적으로 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(); } } }
수정된 서버사이드에서는 클라이언트의 여러 요청을 동시에 처리할 수 있습니다.
프로그래밍 과정에서 우리는 일반적으로 성능 향상을 위해 "리소스"라는 개념을 가지게 됩니다. 데이터베이스 연결 풀을 사용하여 여러 데이터베이스 연결을 관리하는 대신 데이터베이스를 직접 삭제하지 마십시오. 소켓 연결의 경우 이는 리소스이기도 합니다. 프로그램에 많은 수의 소켓 연결이 필요한 경우 각 연결을 다시 설정해야 하는 경우 매우 비효율적인 접근 방식이 됩니다.
데이터베이스 연결 풀과 유사하게 TCP 연결 풀도 설계할 수 있습니다. 여기서 아이디어는 배열을 사용하여 여러 소켓 연결을 유지하고 또 다른 상태 배열을 사용하여 각 소켓 연결이 사용 중인지 여부를 설명한다는 것입니다. 프로그램에 소켓 연결이 필요한 경우 상태 배열을 순회하여 사용되지 않은 첫 번째 소켓 연결을 제거합니다. 모든 연결이 사용 중이면 예외가 발생합니다. 이는 매우 직관적이고 간단한 "스케줄링 전략"입니다. 많은 오픈 소스 또는 상용 프레임워크(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에서는 서버가 시작되지 않으면 클라이언트가 메시지를 보낼 때 예외가 보고되지만 UDP의 경우 예외가 생성되지 않습니다.
UDP 통신에 사용되는 두 가지 클래스는 DatagramSocket과 DatagramPacket이며, DatagramPacket은 통신 내용을 저장합니다.
다음은 간단한 UDP 통신 예입니다. TCP와 마찬가지로 클라이언트와 서버 두 부분으로 나누어집니다. 클라이언트 코드는 다음과 같습니다.
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()); } } }
서버 코드는 다음과 같습니다.
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처럼 클라이언트가 "end" 메시지를 보내면 통신이 끝난 것으로 간주한다고 가정합니다. 그러나 실제로는 그러한 설계가 필요하지 않습니다. 클라이언트는 언제든지 연결을 끊을 수 있으며 서버 상태에 신경 쓸 필요가 없습니다.
멀티캐스트
멀티캐스트는 UDP와 유사한 방법을 사용하며 클래스 D IP 주소와 표준 UDP 포트 번호 사이의 주소를 나타냅니다. 224.0.0.0 및 239.255.255.255(224.0.0.0 제외)
멀티캐스트에 사용되는 클래스는 MulticastSocket인데, 여기에는 JoinGroup과 LeaveGroup이라는 두 가지 주의할 메소드가 있습니다.
다음은 멀티캐스트의 예시입니다. 클라이언트 코드는 다음과 같습니다.
多播通信客户端 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(); } } } }
위 코드는 로컬 URL에 액세스한 다음 해당 내용을 인쇄하려고 시도합니다.
위 내용은 Java 리뷰를 기반으로 한 네트워크 통신 응용 분석입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!