Woopii Vyeolog

[Java] Socket 통신, Tcp Server 샘플 본문

java

[Java] Socket 통신, Tcp Server 샘플

WooPii 2022. 4. 2. 01:11

## Github 주소 : https://github.com/leewoopyo/tcp_sample

 

GitHub - leewoopyo/tcp_sample

Contribute to leewoopyo/tcp_sample development by creating an account on GitHub.

github.com

 

 

1. 소켓이란?

소켓은 네트워크 상에서 돌아가는 두 개의 프로그램 간 양방향 통신의 엔드 포인트.

 

2. 앤드 포인트

여기에서의 앤드 포인트는 아이피 주소와 포트 번호의 조합을 의미함. 모든 TCP 연결은 2개의 앤드 포인트로 유일하게 식별되어질 수 있습니다.

 

3. 소켓 통신

소켓 통신이란 서버와 클라이언트 양방향 연결이 이루어지는 통신.

 

 

4. TCP (스트림 소켓)

  • 연결형 (Connection Oriented) 서비스로 연결이 성공해야 통신이 가능하다.
  • 데이터의 경계를 구분하지 않는다. (바이트 스트림)
  • 데이터의 전송 순서를 보장한다. (데이터의 순서 유지를 위해 각 바이트마다 번호를 부여)
  • 신뢰성 있는 데이터를 전송한다. (Sequence Number, Ack Number를 통한 신뢰성 보장)
  • 데이터 흐름 제어 (수신자 버퍼 오버플로우 방지) 및 혼잡 제어 (패킷 수가 과도하게 증가하는 현상 방지)
  • 연결의 설정 (3-way handshaking) 와 해제(4-way handshaking)
  • 전이중 (Full-Duplex), 점대점 (point to point) 서비스
  • UDP 보다 전송속도가 느리다.

 

5. TCP Server 

byte의 길이를 표현하는 방식에 따라서 2가지 lengthType을 설정

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * TcpServer
 */
public class TcpServer {

    // 서버 포트
    private static final int TCP_SVR_PORT = 11111 ;

    // 이너 클래스, worker thread
    private class Worker extends Thread {
		private Socket socket;

		public Worker(Socket sock) {
			this.socket = sock;
		}
        // thread 실행 로직
		public void run() {

            boolean flag = true;
            // try ~ catch ~ resource
			try (					
                OutputStream out = this.socket.getOutputStream();
                DataOutputStream dos = new DataOutputStream(out);
            
                InputStream in = this.socket.getInputStream();
                DataInputStream dis = new DataInputStream(in);
                ) 
            {
				while(flag) {
                    // lengthType을 client에서 받음 
                    byte[] byteLengthType = new byte[6];
                    dis.readFully(byteLengthType, 0, 6);
                    String lengthType = new String(byteLengthType);
                    System.out.println("[Tcp_Server] 0. length_type : [" + lengthType + "]");

                    // 1. lengthType이 'String'일 때 
                    if ("String".equals(lengthType)) {
                        // 1-1. length 부분 추출 : 빈 4바이트 생성 후 길이부 read
                        byte[] byteLength = new byte[4];
                        dis.readFully(byteLength, 0, byteLength.length);
                        int length = Integer.parseInt(new String(byteLength));
                        System.out.println("[Tcp_Server] 1. read_length : [" + length + "]");

                        // 1-2. readMsg 추출 : client에서 보낸 바이트 데이터를 read
                        byte readMsg[]  = new byte[length];
                        dis.readFully(readMsg, 0, length);
                        System.out.println("[Tcp_Server] 2. read_msg : [" + new String(readMsg) + "]");

                        // 1-3. echoMsg 구성 : arraycopy를 사용해서 echoMsg를 구성
                        byte[] echoMsg = new byte[4 + length];
                        System.arraycopy(byteLength, 0, echoMsg, 0, 4);
                        System.arraycopy(readMsg, 0, echoMsg, 4, length);
                        System.out.println("[Tcp_Server] 3. echo_msg : [" + new String(echoMsg) + "]");

                        // 1-4. echoMsg write
                        dos.write(echoMsg, 0, echoMsg.length);
                        dos.flush();
                    }

                    // #2 lengthType이 'Binary'일 때 
                    if ("Binary".equals(lengthType)) {
                        // 2-1. length 부분 추출 : readInt 메소드로 길이부 추출
                        int  length = dis.readInt();
                        System.out.println("[Tcp_Server] 1. read_length : [" + length + "]");

                        // 2-2. readMsg 추출 : client에서 보낸 바이트 데이터를 read
                        byte readMsg[]  = new byte[length];
                        dis.readFully(readMsg, 0, length);
                        System.out.println("[Tcp_Server] 2. read_msg : [" + new String(readMsg) + "]");

                        // 2-3. echoMsg 구성 : arraycopy를 사용해서 echoMsg를 구성
                        byte[] echoMsg = new byte[length];
                        System.arraycopy(readMsg, 0, echoMsg, 0, length);
                        System.out.println("[Tcp_Server] 3. echo_msg : [" + new String(echoMsg) + "]");

                        // 1-4. echoMsg write
                        dos.writeInt(echoMsg.length);
                        dos.write(echoMsg, 0, echoMsg.length);
                        dos.flush();
                    }

			    	System.out.println("[Tcp_Server] Tcp Server Response Echo Message Ok........");
				}
            // EOFException 발생 시
			} catch (EOFException eofe) {
				//e.printStackTrace();
                flag = false;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
				try { 
					if (this.socket != null) {
						System.out.println("[Tcp_Server] disconnected :"+this.socket.getInetAddress().getHostAddress()+"/"+this.socket.getPort());
						this.socket.close(); 
					}
				} catch(Exception e) {
                    e.printStackTrace();
                }
			}
		}
	}

    // 생성자 메소드 에서 서버 소켓 생성
    public TcpServer () throws Exception {
        // 소켓 인스턴스 생성
        ServerSocket serverSocket = new ServerSocket();
        // serReuseAddress 설정을 해줌 (TCP 접속을 닫은 후 일정시간동안 타임아웃 상태일 시 동일 포트를 바인딩 못하는데, 이를 가능하게 하는 옵션)
        serverSocket.setReuseAddress(true);

        try {
            // 소켓에 포트 바인딩
            serverSocket.bind(new InetSocketAddress(TCP_SVR_PORT));
            System.out.println("starting tcp server : "+TCP_SVR_PORT);

            while (true) {
                // 서버에서 요청 accept 대기
                Socket socket = serverSocket.accept();
                System.out.println("connected : "+socket.getLocalPort());

                // accept를 받으면 worker Thread 시작
                Worker w = new Worker(socket);
				w.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
            serverSocket.close();
        }

    }

    /**
     * main 메소드 실행
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("Hello World!!!");
        try {
            //서버 인스턴스 실행
            new TcpServer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

 

 

 

 

## 참고

https://helloworld-88.tistory.com/215#:~:text=SOCKET%20%ED%86%B5%EC%8B%A0%EC%9D%98%20%ED%8A%B9%EC%A7%95,%EA%B2%BD%EC%9A%B0%EC%97%90%20%EC%9E%90%EC%A3%BC%20%EC%82%AC%EC%9A%A9%EB%90%9C%EB%8B%A4.

 

[기본] 소켓(SOCKET)통신 이란?

소켓통신 소켓(SOCKET)이란? 소켓(Socket)은 프로세스가 드넓은 네트워크 세계로 데이터를 내보내거나 혹은 그 세계로부터 데이터를 받기 위한 실제적인 창구 역할을 한다. 그러므로 프로세스가 데

helloworld-88.tistory.com

 

https://coding-factory.tistory.com/614

 

[Network] TCP / UDP의 개념과 특징, 차이점

전송 계층에서 사용되는 프로토콜 (TCP / UDP) TCP와 UDP는 OSI 표준모델과 TCP/IP 모델의 전송계층에서 사용되는 프로토콜입니다. 전송계층은 송신자와 수신자를 연결하는 통신 서비스를 제공하고 IP

coding-factory.tistory.com

 

 

 

'java' 카테고리의 다른 글

[Java] Array 중복 제거  (3) 2022.04.04
[Java] Socket 통신, TCP Client 샘플  (0) 2022.04.02
[디자인 패턴] Build Pattern, 빌더 패턴  (1) 2021.02.24
Vector vs Arraylist  (0) 2020.02.28
String, StringBuffer, StringBuilder  (0) 2020.02.19
Comments