TCP 연결
TCP의 기본은 Socket입니다. TCP 연결에서는 클라이언트와 서버가 연결을 설정한 후 나머지는 기본적으로 I/O를 제어합니다.
먼저 클라이언트와 서버로 구분되는 간단한 TCP 통신을 살펴보겠습니다.
클라이언트 코드는 다음과 같습니다.
public static void main(String[] args)에서 IOException이 발생합니다.
{
소켓 소켓 = null;
BufferedReader br = null;
PrintWriter 비밀번호 = null;
BufferedReader brTemp = null;
노력하다
{
소켓 = new Socket(InetAddress.getLocalHost(), 5678);
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
brTemp = new BufferedReader(new InputStreamReader(System.in));
동안(사실)
{
문자열 라인 = brTemp.readLine();
pw.println(라인);
pw.flush();
if (line.equals("end")) break;
System.out.println(br.readLine());
}
}
catch(예외예외)
{
System.err.println(ex.getMessage());
}
마지막으로
{
if (소켓 != null) 소켓.close();
if (br != null) br.close();
if (brTemp != null) brTemp.close();
if (pw != null) pw.close();
}
}
}
public static void main(String[] args)에서 IOException이 발생합니다.
{
ServerSocket 서버 = null;
소켓 클라이언트 = null;
BufferedReader br = null;
PrintWriter 비밀번호 = null;
노력하다
{
서버 = 새로운 ServerSocket(5678);
클라이언트 = server.accept();
br = new BufferedReader(new InputStreamReader(client.getInputStream()));
pw = new PrintWriter(client.getOutputStream());
동안(사실)
{
문자열 라인 = br.readLine();
pw.println("응답:" + line);
pw.flush();
if (line.equals("end")) break;
}
}
catch(예외예외)
{
System.err.println(ex.getMessage());
}
마지막으로
{
if (서버 != null) server.close();
if (클라이언트 != null) client.close();
if (br != null) br.close();
if (pw != null) pw.close();
}
}
}
위 코드는 기본적으로 TCP 통신 프로세스 중 클라이언트와 서버의 주요 프레임워크를 설명합니다. 위 코드에서 서버는 언제든지 클라이언트의 요청을 하나만 처리할 수 있다는 것을 알 수 있습니다. 이는 우리 인상의 서버 처리 방법과 동일하지 않습니다. 클라이언트 요청이 들어오면 해당 요청을 처리하기 위해 서버에 다중 스레드를 추가할 수 있습니다.
개선된 서버측 코드는 다음과 같습니다.
클래스 ServerThread는 스레드를 확장합니다.
{
개인 소켓 소켓 = null;
공용 ServerThread(소켓 소켓)
{
this.socket = 소켓;
}
공개 무효 실행() {
BufferedReader br = null;
PrintWriter 비밀번호 = null;
노력하다
{
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
동안(사실)
{
문자열 라인 = br.readLine();
pw.println("응답:" + line);
pw.flush();
if (line.equals("end")) break;
}
}
catch(예외예외)
{
System.err.println(ex.getMessage());
}
마지막으로
{
if (소켓 != null)
노력하다 {
소켓.닫기();
} 잡기(IOException e1) {
e1.printStackTrace();
}
if (br != null)
노력하다 {
br.닫기();
} 잡기(IOException e) {
e.printStackTrace();
}
if (pw != null) pw.close();
}
}
}
프로그래밍 과정에서 "리소스"라는 개념이 있습니다. 예를 들어 데이터베이스 연결은 성능을 향상시키기 위해 일반적으로 데이터베이스 연결을 직접 파괴하지 않고 데이터베이스 연결 풀을 사용하여 여러 개를 관리합니다. 데이터베이스 연결이 관리되고 재사용됩니다. 소켓 연결의 경우 이는 리소스이기도 합니다. 프로그램에 많은 수의 소켓 연결이 필요한 경우 각 연결을 다시 설정해야 하는 경우 매우 비효율적인 접근 방식이 됩니다.
데이터베이스 연결 풀과 마찬가지로 TCP 연결 풀도 설계할 수 있습니다. 여기서 아이디어는 배열을 사용하여 여러 소켓 연결을 유지하고 또 다른 상태 배열을 사용하여 프로그램이 필요할 때 각 소켓 연결이 사용 중인지 여부를 설명한다는 것입니다. 소켓 연결, 상태 배열을 순회하고 사용되지 않은 첫 번째 소켓 연결을 제거합니다. 모든 연결이 사용 중이면 예외가 발생합니다. 이는 매우 직관적이고 간단한 "스케줄링 전략"입니다. 많은 오픈 소스 또는 상용 프레임워크(Apache/Tomcat)에는 유사한 "리소스 풀"이 있습니다.
TCP 연결 풀의 코드는 다음과 같습니다.
개인 InetAddress 주소 = null;
개인용 int 포트;
개인 소켓[] arrSockets = null;
개인 부울[] arrStatus = null;
개인 정수 개수;
공개 TcpConnectionPool(InetAddress 주소, int 포트, int 개수)
{
this.address = 주소;
this.port = 포트;
이 .count = 개수;
arrSockets = 새 소켓[개수];
arrStatus = 새로운 부울[개수];
초기화();
}
개인 무효 초기화()
{
노력하다
{
for (int i = 0; i < count; i++)
{
arrSockets[i] = new Socket(address.getHostAddress(), 포트);
arrStatus[i] = 거짓;
}
}
catch(예외예외)
{
System.err.println(ex.getMessage());
}
}
공용 소켓 getConnection()
{
if (arrSockets == null) init();
int i = 0;
for(i = 0; i < 개수; i++)
{
if (arrStatus[i] == false)
{
arrStatus[i] = 참;
부서지다;
}
}
if (i == count) throw new RuntimeException("현재 사용 가능한 연결이 없습니다.");
arrSockets[i]를 반환합니다.
}
공개 무효 releaseConnection(소켓 소켓)
{
if (arrSockets == null) init();
for (int i = 0; i < count; i++)
{
if (arrSockets[i] == 소켓)
{
arrStatus[i] = 거짓;
부서지다;
}
}
}
공개 무효 재빌드()
{
초기화();
}
공공 무효 파괴()
{
if (arrSockets == null) 반환;
for(int i = 0; i < count; i++)
{
노력하다
{
arrSockets[i].close();
}
catch(예외예외)
{
System.err.println(ex.getMessage());
계속하다;
}
}
}
}
UDP는 TCP와는 다른 연결 방법으로, 온라인 비디오와 같이 높은 실시간 성능과 낮은 정확도 요구 사항이 필요한 상황에서 일반적으로 사용됩니다. UDP에는 "패킷 손실"이 발생합니다. TCP에서는 서버가 시작되지 않으면 클라이언트가 메시지를 보낼 때 예외가 보고되지만 UDP의 경우 예외가 생성되지 않습니다.
UDP 통신에 사용되는 두 가지 클래스는 DatagramSocket과 DatagramPacket이며, DatagramPacket은 통신 내용을 저장합니다.
다음은 간단한 UDP 통신 예입니다. TCP와 마찬가지로 클라이언트와 서버의 두 부분으로 나뉩니다.
공개 정적 무효 메인(문자열[] 인수)
{
노력하다
{
InetAddress 호스트 = InetAddress.getLocalHost();
int 포트 = 5678;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
동안(사실)
{
문자열 라인 = br.readLine();
byte[] 메시지 = line.getBytes();
DatagramPacket 패킷 = new DatagramPacket(메시지, 메시지.길이, 호스트, 포트);
DatagramSocket 소켓 = new DatagramSocket();
소켓.전송(패킷);
소켓.닫기();
if (line.equals("end")) break;
}
br.닫기();
}
catch(예외예외)
{
System.err.println(ex.getMessage());
}
}
}
공개 정적 무효 메인(문자열[] 인수)
{
노력하다
{
int 포트 = 5678;
DatagramSocket dsSocket = new DatagramSocket(포트);
바이트[] 버퍼 = 새 바이트[1024];
DatagramPacket 패킷 = new DatagramPacket(buffer, buffer.length);
동안(사실)
{
dsSocket.receive(패킷);
문자열 메시지 = new String(buffer, 0, packet.getLength());
System.out.println(packet.getAddress().getHostName() + ":" + 메시지);
if (message.equals("end")) break;
packet.setLength(buffer.length);
}
dsSocket.close();
}
catch(예외예외)
{
System.err.println(ex.getMessage());
}
}
}
멀티캐스트는 UDP와 유사한 방법을 사용합니다. 클래스 D IP 주소와 표준 UDP 포트 번호를 사용합니다. 클래스 D IP 주소는 224.0.0.0을 제외한 224.0.0.0에서 239.255.255.255 사이의 주소를 나타냅니다.
멀티캐스트에 사용되는 클래스는 MulticastSocket이며, 여기에는 주의해야 할 두 가지 메소드(joinGroup 및 LeaveGroup)가 있습니다.
다음은 멀티캐스트의 예입니다. 클라이언트 코드는 다음과 같습니다.
공개 정적 무효 메인(문자열[] 인수)
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
노력하다
{
InetAddress 주소 = InetAddress.getByName("230.0.0.1");
int 포트 = 5678;
동안(사실)
{
문자열 라인 = br.readLine();
byte[] 메시지 = line.getBytes();
DatagramPacket 패킷 = new DatagramPacket(메시지, 메시지.길이, 주소, 포트);
MulticastSocket multicastSocket = new MulticastSocket();
multicastSocket.send(패킷);
if (line.equals("end")) break;
}
br.닫기();
}
catch(예외예외)
{
System.err.println(ex.getMessage());
}
}
}
공개 정적 무효 메인(문자열[] 인수)
{
int 포트 = 5678;
노력하다
{
MulticastSocket multicastSocket = new MulticastSocket(포트);
InetAddress 주소 = InetAddress.getByName("230.0.0.1");
multicastSocket.joinGroup(주소);
바이트[] 버퍼 = 새 바이트[1024];
DatagramPacket 패킷 = new DatagramPacket(buffer, buffer.length);
동안(사실)
{
multicastSocket.receive(패킷);
문자열 메시지 = new String(buffer, packet.getLength());
System.out.println(packet.getAddress().getHostName() + ":" + 메시지);
if (message.equals("end")) break;
packet.setLength(buffer.length);
}
multicastSocket.close();
}
catch(예외예외)
{
System.err.println(ex.getMessage());
}
}
}
NIO는 JDK1.4에 도입된 새로운 IO API 세트입니다. 버퍼 관리, 네트워크 통신, 파일 액세스 및 문자 세트 작업에 대한 새로운 디자인이 있습니다. 네트워크 통신을 위해 NIO는 버퍼와 채널의 개념을 사용합니다.
다음은 위에서 언급한 코딩 스타일과 매우 다른 NIO의 예입니다.
공개 정적 무효 메인(문자열[] 인수)
{
문자열 호스트="127.0.0.1";
int 포트 = 5678;
SocketChannel 채널 = null;
노력하다
{
InetSocketAddress 주소 = 새 InetSocketAddress(호스트, 포트);
Charset charset = Charset.forName("UTF-8");
CharsetDecoder 디코더 = charset.newDecoder();
CharsetEncoder 인코더 = charset.newEncoder();
ByteBuffer 버퍼 = ByteBuffer.allocate(1024);
CharBuffer charBuffer = CharBuffer.allocate(1024);
채널 = SocketChannel.open();
채널.연결(주소);
문자열 요청 = "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(예외예외)
{
System.err.println(ex.getMessage());
}
마지막으로
{
if (채널 != null)
노력하다 {
채널.닫기();
} 잡기(IOException e) {
// TODO 자동 생성된 캐치 블록
e.printStackTrace();
}
}
}
}