Java - network (TCP 통신)

2025. 2. 11. 17:57·Java

1. 1:1 통신

  • Server : 10000
  • Client : 상대IP, 접속하는 포트
    1. Client => Server 메세지를 전송
    2. Server => Client 메세지를 전송
    3. 1,2를 지속적으로 반복 

서버

package net;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

//Server : 접속하는 서버 환경 - TCP 
public class net5 {
	public static void main(String[] args) throws Exception {
		new tcp_server().server();
	}
}

class tcp_server{
	int port = 10000;	//서버에서 사용할 임시 port
	Scanner sc = new Scanner(System.in);
	ServerSocket ss = null;	//서버의 포트를 오픈하는 라이브러리 
	Socket sk = null;	//해당 포트로 접속을 유지시키는 라이브러리 
	InputStream is = null;	//클라이언트에서 전송된 메세지를 받는 역할(byte)
	OutputStream os = null;	//서버에서 클라이언트로 전송하는 역할(byte)
	
	public void server() throws Exception {
		try {
			this.ss = new ServerSocket(port);	//서버소켓은 반복문 밖에 있어야함 
			//1:1로 메세지 전송
			while(true) {	//무한반복문으로 네트워크를 유지 
				this.sk = this.ss.accept();
				
				System.out.println("호스트 통신 연결 성공");
				
				this.is = this.sk.getInputStream();	//클라이언트쪽 메세지 
//				byte msg[] = new byte[this.is.available()];	//네트워크로 파일전송(Binary)
				byte msg[] = new byte[2048];	//받을수있는 최대 바이트수 (네트워크로 문자전송) 
				this.is.read(msg);	//클라이언트에서 보낸 메세지를 byte로 읽음
				String message = new String(msg);
				System.out.println("전송된 메세지 : " + message);
				
				//서버에서 메세지를 클라이언트로 전송 
				System.out.println("전송할 메세지 : ");
				String cms = this.sc.nextLine();
				this.os = this.sk.getOutputStream();	//byte로 전송
				byte call[] = cms.getBytes();	//문자를 byte로 변환 
				this.os.write(call);	//String 변환된 byte를 네트워크로 전송 
				this.os.flush();
				
				this.os.close();			
				this.is.close();
			}
			
			/* //client의 접속 ip 및 가상 포트를 확인 
			InetSocketAddress ia = (InetSocketAddress)this.sk.getRemoteSocketAddress();
			System.out.println(ia.getAddress());
			String notip = ia.getAddress().toString();
			if(notip.equals("172.30.1.22")) {
				System.out.println("해당 아이피는 차단시킵니다.");
			}
			*/			
		}catch (Exception e) {
			System.out.println("동일한 포트가 충돌 발생하여 서버를 가동하지 못합니다.");
		}
	}
}

클라이언트

package net;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

//Client : 해당 서버로 접속하는 클라이언트 프로그램 - TCP
public class net6 {
	public static void main(String[] args) {
		new tcp_client().client();
	}
}

class tcp_client{
	Scanner sc = new Scanner(System.in);
	Socket sk = null;
	int port = 10000;
	String ip = "172.30.1.44";	//외부 서버 IP
	InputStream is = null;	//서버에서 전송된 메세지 
	OutputStream os = null;	//서버로 전송할 메세지  
	
	public void client() {
		try {
			System.out.println("서버에 접속 준비중입니다...");
			
			int count = 0;	//최초 접속 카운팅 
			while(true) {	//서버에 접속 후 해당 포트를 지속적으로 유지 
				this.sk = new Socket(this.ip,this.port);	//서버 접속이 안되면 해당 줄에서 멈춰있다가 바로 catch로 빠짐 ->이 줄아래에 조건문 걸어도 무의미함 
				
				// 서버로 메세지를 전송하는 코드
				System.out.println("전송할 메세지를 입력하세요 : ");
				String usermsg = this.sc.nextLine();
				this.os = this.sk.getOutputStream(); // byte로 전송
				byte[] msg = usermsg.getBytes(); // String을 byte로 변환
				this.os.write(msg); // 내용을 메모리로 저장하여 전송

				// 서버에서 전송한 메세지를 받는 역할
				this.is = this.sk.getInputStream(); // byte로 메세지 내용을 받음
				byte[] call = new byte[2048]; // 2048 byte 단위로 메세지를 나눠서 받음
				this.is.read(call); // 전송된 byte 전체를 읽어들임
				String result = new String(call); // byte의 내용을 문자로 임시변환 (정상변환은 isr사용해서 br로 읽기)
				System.out.println("서버로부터 받은 메세지 : " + result);

				this.os.flush();
				this.os.close();
				this.is.close();
				System.out.println(this.sk);
				count++;
			}
		}catch (Exception e) {	//this.sk가 지속적으로 접속이 되지 않을 경우 
			System.out.println("상대방 서버가 접속이 차단되었습니다.");
		}
		
	}
}

 

2. 1:n 통신 -Thread 사용 

서버

package net;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

//Thread 활용한 네트워크 - Server
public class net7 {
	public static void main(String[] args) {
		new net7().open();
	}
	
	public void open() {
		ServerSocket ss = null;
		Socket sk = null;
		try {
			ss = new ServerSocket(9999);
			//Thread = new Thread 이거는 소켓 여러개쓸때 사용 (포트 여러개써서 다른 채팅채널을 만들때)
			
			System.out.println("[채팅 서버 오픈]");
			while(true) {		//반복문으로 해당 포트를 지속적으로 유지시킴 
				sk = ss.accept();	//9999포트 접속 
				net7_Server ns = new net7_Server(sk);	//외부 클래스로 해당 소켓을 전달
				ns.start();		//Thread를 작업 활성화
				
				//관리자 전용 class
				new net7_admin().start();
			}
			
			/*
			accept 사용하여 포트를 무한으로 반복시 
			해당 코드 아래에 신규 class 및 메소드를 사용하지 못함
			while문 안에 넣어줘야함   
			*/
			 
		}catch (Exception e) {
			System.out.println("서버 포트 충돌로 인하여 서버 가동을 중지합니다.");
		}finally {
			if(ss != null) {	//해당 포트가 작동하고 있을 경우 
				try {
					ss.close();	//해당 포트 정지
					System.out.println("서버를 강제 종료합니다.");
				}catch (Exception e) {
					System.out.println("서버 포트가 완전 오류 발생으로 프로그램 자체 종료합니다.");
					System.exit(0);	//해당 java 프로세서를 강제 종료 
				}
			}
		}
	}
}

//관리자 전용 메세지 전송 - 관리자가 여러명이 아니어서 쓰레드 안써도됨 
class net7_admin extends Thread{
	Scanner sc = new Scanner(System.in);
	String admin_msg = "";

	@Override
	public void run() {
		try {
			//반복문으로 지속적으로 메세지를 전송할 수 있도록 적용 
			while(true) {
				System.out.println("관리자 메세지 : ");
				this.admin_msg = this.sc.nextLine();
				// 재귀 사용시 scanner 입력안받으면 여기서 다 정지됨
				new net7_Server("[관리자] : " + this.admin_msg); // 해당 즉시실행메소드에 관리자 메세지를 전송
			}
		} catch (Exception e) {
			System.out.println("관리자쪽 포트 오류 발생");
		}
	}
}


class net7_Server extends Thread{
	//서버에 접속한 모든 사용자 리스트를 메모리에 저장시키는 빈 배열 (관리자 미포함)
	static List<PrintWriter> list = new ArrayList<PrintWriter>();
	
	//client에게 받는 역할 
	InputStream is = null;
	InputStreamReader isr = null;
	BufferedReader br = null;
	
	//client에 보내는 역할 
	OutputStream os = null;
	PrintWriter pw = null;	//Stream -> Writer 변환 (byte->String)
	
	Socket socket = null;
	String mid = "";
	Scanner sc = null;
	
	//배열에 있는 모든 사용자에게 동일한 메세지를 전달 
	public void message(String msg) {
		System.out.println(msg);	//사용자별 메세지 내용 서버에서 확인
//		System.out.println(this.list);	//배열 내용을 확인 (암호화되어있음)
		for(PrintWriter pw : this.list) {
			pw.println(msg);	//각 사용자별 발송 메세지  
			pw.flush();			//채팅 메세지 메모리를 비움 
		}
	}
	
	@Override
	public void run() {	//Thread가 가동되는 메소드 
		try {
			//최초 한번만 작동되는 코드 
			this.mid = this.br.readLine();
			String hello = "["+this.mid+"]님 입장하셨습니다.";
			System.out.println(hello);	//서버에서 확인
			this.message(hello);	//배열에 등록된 모든 사용자에게 발송
			while(this.br != null) {	//메세지를 입력하면 지속적으로 반복되는 코드 
				hello = this.br.readLine().intern();	//연산기호쓰려고 intern사용 -> 안쓰면 밑에 조건에 equals사용해야함
				if(hello == "나가기") {	//클라이언트에서 나가기 라는 단어를 입력하면 반복문 종료
					break;	//해당 Thread만 정지 => finally 작동 
				}else {	//채팅 메세지를 지속적으로 배열에 적용된 모든 사용자에게 발송 
					this.message(this.mid + ":" + hello);					
				}
			}	
		}catch (Exception e) {	//무조건 오류발생시 발동 
			System.out.println("사용자가 접속 오류로 인하여 접속이 해제되었습니다.");
		}finally{	//퇴장한 사용자의 아이디와 메세지를 전체 사용자에게 전송 
			this.message(this.mid + "님 퇴장하였습니다.");
			this.list.remove(this.pw);	//배열에 해당 사용자를 삭제 
			try {
				this.socket.close();	//포트 종료 (각각의 사용자에 한해 해당 포트가 종료)
			}catch (Exception e) {
				System.out.println("채팅 서버 종료");
			}
			System.out.println(this.mid + "퇴장");	//서버에서 확인
		}
	}
	
	//관리자가 입력한 메세지를 받는 역할을 하는 즉시실행메소드 
	public net7_Server(String msg) {
		this.message(msg);	//사용자 전체 리스트에 해당 관리자 메세지를 전송 
	}
	
	
	public net7_Server(Socket s) {	//즉시 실행 메소드 (main에서 Socket을 전달받음)
		this.socket = s;
		try {
			//접속에 대한 정보를 list 배열에 추가
			this.os = this.socket.getOutputStream();
			this.pw = new PrintWriter(this.os);
			this.list.add(this.pw);
			System.out.println("채팅 참여자 총 "+this.list.size()+"명 입니다.");
			
			//클라이언트 접속에 따른 메세지 및 정보값을 가져옴 
			this.is = this.socket.getInputStream();	
			this.isr = new InputStreamReader(this.is);
			this.br = new BufferedReader(this.isr);
			//최초 접속시 상대방 접속 IP
			System.out.println(this.socket.getInetAddress());
			//getKeepAlive : true, false(기본) -> 지금은 죽어있어야 핸들링 가능 
			//false : 사용하지 않는 port -> 소켓 죽어있음
			//true : 사용하는 port -> 소켓 살아있음
			System.out.println(this.socket.getKeepAlive());
		}catch (Exception e) {
			System.out.println("채팅 통신 오류 발생!");
		}
	}
}

클라이언트

package net;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

//Thread 활용한 네트워크 - Client
public class net8 {
	public static void main(String[] args) {
		new net8().open();
	}
	public void open() {	//서버 접속 정보 및 Thread를 활성화하는 메소드 
		String ip = "172.30.1.34";	//server ip
		int port = 9999;	//server port
		try {
			Socket sk = new Socket(ip,port);	//연결 정보 환경설정 셋팅 
			System.out.println("[채팅서버 접속 완료]");
			Scanner sc = new Scanner(System.in);
			System.out.println("생성할 아이디를 입력하세요 : ");
			String mid = sc.nextLine();	//생성된 id를 이용하여 채팅 
			
			//서버에서 메세지를 받는 역할
			Thread th = new net8_client(sk, mid);	//반복문 안에 넣으면 쓰레드가 계속 늘어남 절대 x
			th.start();
			
			//접속 입장시 서버에서 오는 메세지를 받는 역할 
			InputStream is = sk.getInputStream();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			
			//수신
			while(br != null) {
				//서버에서 받은 내용을 변수로 이관함 
				String chat_msg = br.readLine();
				
				//퇴장
				if(chat_msg.equals(mid + "님 퇴장하였습니다.")) {
					System.out.println("채팅이 종료되었습니다.");
					System.exit(0);					
				}
				
				System.out.println(chat_msg);
			}
//			br.close();
//			isr.close();
//			is.close();
			sc.close();
			sk.close();
		}catch (Exception e) {
			System.out.println("서버에 접속이 원활하지 않습니다.");
		}
	}
}

//Thread를 이용하여 메세지를 전송 
class net8_client extends Thread{
	Socket sk = null;
	String mid = null;
	Scanner sc = null;
	
	OutputStream os = null;	//byte
//	PrintWriter pw = null;	//String
	PrintStream ps = null;	//String -> byte
		
	//즉시 실행 메소드에 서버 정보 및 사용자 아이디를 세팅 
	public net8_client(Socket s, String mid) {
		this.sk = s;	//접속 서버 정보 
		this.mid = mid;	//사용자 아이디
		this.sc = new Scanner(System.in);	//입력창 활성화 	
	}
	
	@Override
	public void run() {	//Thread로 메세지를 전송 
		try {
			//최초 접속시 접속 아이디를 전송 (첫번째 메세지)
			this.os = this.sk.getOutputStream();
//			this.pw = new PrintWriter(this.os);
			this.ps = new PrintStream(this.os);
			this.ps.println(this.mid);
			this.ps.flush();
			
			//사용자가 입력하는 메세지를 서버로 전송 
			while(true) {
				System.out.println("채팅 메세지를 입력하세요 : ");
				String msg = this.sc.nextLine();
				this.ps.println(msg);
				this.ps.flush();
			}
		}catch (Exception e) {
			System.out.println("Server 접속 문제로 채팅이 중단됩니다.");
		}
	}
}

 

3. 여러개의 서버 오픈 - 멀티포트 오픈

  • Server 파트
    • 1. Thread(PORT별로 나누는 작업) => 특정 포트만 죽이려면 static List화 시켜서 죽이기
    • 2. Thread(각 PORT별 Client 송수신 작업) => static List화 
  • Client 파트
    • 1. 서버 IP
    • 2. 서버 포트(channel)

 

ex)

A게임 - 점검중 => 무조건 모든 점검 (하나의 서버에 여러개의 Thread(PORT))

B게임 - 점검중 => 특정 거버만 점검중 (여러개의 서버가 작동)

 

서버

package net;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

//멀티 포트로 서버 오픈하기  
public class net9 {
	public static void main(String[] args) {
		new server_open();
	}
}

class server_open{	//해당 port를 이용하여 하나의 서버에서 여러개의 채널을 분리 (쓰레드 이용)
	public server_open() {
		int port[] = {9001, 9002, 9003};	//오픈할 포트 원시배열
		int w = 0;
		while(w<port.length) {	//반복문으로 Thread 클래스를 이용하여 실행 
			//배열 순서에 맞게 Thread 활성화 
//			server_port sp = new server_port(port[w]);
//			sp.start();		//Thread 가동 
			
			//배열의 순서와 관계없이 먼저 포트가 작동되는 순서부터 OPEN
			Thread th = new server_port(port[w]);
			th.start();		//Thread 가동 
			
			w++;
		}
	}
}

//각각의 포트별로 서버를 오픈 (각 포트별 Thread -> 각 포트별 채널을 나누는 작업)
class server_port extends Thread{
	int port = 0;	//순차적으로 포트를 오픈하기위한 전역변수 
	ServerSocket sk = null;
	Scanner sc = null;
	
	public server_port(int p) {
		this.port = p;
	}
	
	@Override
	public void run() {	//Thread로 각각의 PORT를 작동시키는 메소드 
		try {
			this.sk = new ServerSocket(this.port);	//해당 포트 오픈
			System.out.println(this.port + "서버 오픈");
			while(true) {
				Socket s = this.sk.accept();	//해당 포트를 유지 
				room rm = new room(s);
				rm.start();
			}
		}catch (Exception e) {
			System.out.println("중복된 PORT가 존재하므로 서버를 종료합니다.");
		}
	}
}

//송신, 수신 역할 클래스 (Thread -> 해당 room에서 여러 Client가 송수신하는 역할)
class room extends Thread{
	Socket s = null;
	
	@Override
	public void run() {
		try {
			InputStream is = this.s.getInputStream();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			System.out.println(br.readLine());			
		}catch (Exception e) {

		}
	}

	public room(Socket s) {
		try {
			this.s = s;
			PrintWriter pw = new PrintWriter(s.getOutputStream());
			System.out.println(pw.toString());		
		}catch (Exception e) {
			System.out.println("채팅서버 클라이언트 통신 오류");
		}
	}
}

클라이언트

package net;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Arrays;
import java.util.Scanner;

//멀티 포트로 클라이언트를 접속하기  
public class net10 {
	public static void main(String[] args) {
		int port = 9000;
		Scanner sc = new Scanner(System.in);
		String ch[] = {"1. JAVA채널", "2. JS채널", "3. 디자인채널"};
		System.out.println(Arrays.toString(ch) + "\n채널을 선택해주세요 : ");
		int no = port + sc.nextInt();	//9000+사용자가 입력한 숫자 
		new client_port(no);
		sc.close();
	}
}

class client_port{
	Socket sk = null;	//소켓을 설정하는 변수 
	String serverip = "172.30.1.44";	//서버 접속 IP
	int serverport = 0;	//서버 접속 port
	
	public client_port(int no) {
		this.serverport = no;
		try {
			this.sk = new Socket(this.serverip,this.serverport);
			System.out.println(this.serverport + "채널 입장!");
			Scanner sc = new Scanner(System.in);
			System.out.println("채팅에서 사용할 닉네임을 입력하세요 : ");
			String mid = sc.nextLine();
			
			//Thread로 값을 이관(서버에 메세지를 송신) - 보내는 내용 Thread로 핸들링함
			Thread th = new chat_clients(this.sk, mid, this.serverport);
			th.start();
			
		}catch (Exception e) {
			System.out.println("해당 서버에 채널이 존재하지 않습니다.");
		}
	}
}

//송신 역할을 하는 Thread
class chat_clients extends Thread{
	Socket sk = null;	//서버 소켓 정보 
	String mid = null;	//사용자 아이디	 
	Scanner sc = null;	//채팅 메세지를 입력하는 객체
	int port = 0;		//접속한 port 번호 
	
	public chat_clients(Socket s, String id, int p) {
		this.sk = s;
		this.mid = id;
		this.sc = new Scanner(System.in);
		this.port = p;
	}
	
	@Override
	public void run() {
		try {
			//접속한 아이디 및 port를 서버에 전송 
			PrintStream ps = new PrintStream(this.sk.getOutputStream());
			ps.println("아이디 : " + this.mid + "\n접속 포트 : " + this.port); 
			ps.flush();			
		}catch (Exception e) {
			System.out.println("서버 포트 통신오류로 채팅이 중지됩니다.");
		}
	}
}

주저리주저리

1. 단방향 통신 
Server -> Client -> Server -> Client

2. 양방향 통신 (Tread) - 2개의 Tread
Server -> Client (Thread)
Client -> Server (Thread)
[Network] - TCP, UDP, HTTP(웹소켓통신), FTP(파일), SMTP(메일)
1. Server - PORT 통신 규격 (TCP, UDP)
2. Client - Server 정보 및 PORT (TCP, UDP)
3. Server Start  
4. Client Start
5. 단방향(Thread 필요x), 양방향(Thread 필요) 정하기
6. 문자형태의 통신, File 형태의 통신 정하기
[Network 부가사항 통신방법]
- Voip(음성 데이터 통신) - 인터넷 전화 
- VPN (가상 사설망) - 가상으로 개인 전용 통신 IP 사용하는 기술 
[Network - error]
1. Server - PORT 충돌 (TCP, UDP 각각의 포트 존재 -> 같은 포트 사용 가능)
2. Client - Server 정보가 올바르지 않을 경우 
3. byte, String 코드 오류
저작자표시 비영리 변경금지 (새창열림)
'Java' 카테고리의 다른 글
  • Java - network (UDP)
  • Java - network (FTP)
  • Java - network
  • 복습11 - IO
9na0
9na0
응애
  • 9na0
    구나딩
    9na0
  • 전체
    오늘
    어제
    • 분류 전체보기 (210)
      • Web (118)
      • Java (28)
      • 데이터베이스 (14)
      • 세팅 (12)
      • 과제 (3)
      • 쪽지시험 (2)
      • 정보처리기사 (4)
      • 서버 (24)
  • 블로그 메뉴

    • 링크

      • 포폴
      • 구깃
    • 공지사항

    • 인기 글

    • 태그

      Oracle
      re_java10
      file24
      ab1
      java_io1~10
      macbook pro m4
      net4
      net2
      noticewriteok
      io_dto
      re2
      spring-boot
      file25
      file25_t
      net3
      exam1_1~10
      notice_writer
      datalist
      net1
      net5~10
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    9na0
    Java - network (TCP 통신)
    상단으로

    티스토리툴바