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 코드 오류