Purpose : 자바에 대한 흥미를 얻고, 다양한 경험을 해보기 위함이며, 더 나아가 세미프로젝트에 적용시켜볼수 있도록 노력해볼 것.
1) 소켓과 소켓 통신이란?
-> 소켓(Socket)은 TCP/IP기반 네트워크 통신에서 데이터 송수신의 마지막 접점을 의미합니다.
소켓통신은 이러한 소켓을 통해 서버-클라이언트간의 데이터를 주고받는 양방향 연결지향성 통신을 의미함.
보통 소켓통신의 지속적으로 연결을 유지해주면서 실시간으로 데이터를 주고받는 경우에 사용함
ex) 라이브 채팅서버, 게임 및 보통 채팅서버.
소켓간 통신을 위해서는 네트워크 상에서 클라이언트와 서버에 해당하는 컴퓨터를 식별하기 위한
IP주소와 해당 컴퓨터내 포트번호를 요구합니다
2) 서버와 클라이언트
소켓통신에서는 서버와 클라이언트가 존재해야하며
서버는 데이터를 제공하는 쪽을 의미하고, 클라이언트는 데이터를 제공받는 쪽을 의미한다.
3) 서버 소켓 구현 로직
MyServer.java
package single;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class MyServer {
public static void main(String[] args) {
BufferedReader in = null;
PrintWriter out = null;
ServerSocket serverSocket = null;
Socket socket = null;
Scanner scanner = new Scanner(System.in);
try {
serverSocket = new ServerSocket(8000);
System.out.println("[Server실행] Client연결대기중...");
socket = serverSocket.accept(); // 연결대기
System.out.println("Client 연결됨.");
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream());
while(true) {
String inputMessage = in.readLine(); // 수신데이터 한줄씩 읽기
if ("quit".equalsIgnoreCase(inputMessage)) break;
System.out.println("From Client: " + inputMessage);
System.out.print("전송하기>>> ");
String outputMessage = scanner.nextLine();
out.println(outputMessage);
out.flush();
if ("quit".equalsIgnoreCase(outputMessage)) break;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
scanner.close(); // Scanner 닫기
socket.close(); // Socket 닫기
serverSocket.close(); // ServerSocket 닫기
System.out.println("연결종료");
} catch (IOException e) {
System.out.println("소켓통신에러");
}
}
}
}
한번 서버의 로직을 간단하게 파헤쳐 보겠습니다.
1) ServerSocket 인스턴스 생성
ServerSocket serversocket = new ServerSocket(8000);
-> 자신이 사용하는 포트를 사용합니다 ex)8000, 8001 ->8080은 비추천
ServerSocket 은 최고 조상을 확인해보니 Autoclosable 인터페이스 인걸로 확인했습니다
ServerSocket Autoclosable 메서드 중 하나인걸로 확인했습니다.
2) 클라이언트 접속 허용 대기
Socket socket = serverSocket.accept();
3) 데이터 송수신 위한 입출력 Stream생성
InputStream in = socket.getInputStream(); // 소켓 객체에서 입력스트림을 가져온다.
// in은 반환된 inputStream을 저장하기 위한 변수이다.
OutputStream out = socket.getOutPutStream(); //소켓 객체에서 출력스트림 가져온다.
// out은 반환된 inputStream을 저장하기 위한 변수이다.
4) inputstream을 통한 데이터 수신(client -> server)
byte[] inputData = new byte[100]; //100byte까지 받을 수있는 배열 생성
int length = in.read(inputData); // length는 inputData를 읽어오는 역할
String inputMessage = new String(InputData,0,length);
//inputMessage에 매개변수 지정을 통한, 정해진 것만 들어올 수 있게함.
5) outputstream을 통한 데이터 송신(server -> client)
String outputMessage = "send msg";
out.write(OutputMessage.getBytes());
// 출력 스트림(out)을 사용하여 데이터를 보내며, outputMessage 문자열을 getBytes() 메소드를 통해 바이트 배열로 변환하고, 그 바이트 배열을 출력 스트림을 통해 전송한다
out.flush(); //flush메서드는 전송하는 역할
6) 통신종료
socket.close();
serversocket.close();
-> scanner , finally 메서드 닫는 것 처럼, 사용한 객체를 그만 사용할때 마무리 하는 작업입니다.
MyClient.java
package single;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class MyClient {
public static void main(String[] args) {
BufferedReader in = null;
PrintWriter out = null;
Socket socket = null;
Scanner scanner = new Scanner(System.in);
try {
socket = new Socket("127.0.0.1", 8000);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream());
while(true) {
System.out.print("전송하기>>> ");
String outputMessage = scanner.nextLine();
out.println(outputMessage);
out.flush();
if ("quit".equalsIgnoreCase(outputMessage)) break;
String inputMessage = in.readLine();
System.out.println("From Server: " + inputMessage);
if ("quit".equalsIgnoreCase(inputMessage)) break;
}
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
try {
scanner.close();
if (socket != null) socket.close();
System.out.println("서버연결종료");
} catch (IOException e) {
System.out.println("소켓통신에러");
}
}
}
}
1) 클라이언트 소켓 생성 후 서버 접속
Socket socket = new Socket("127.0.0.1",8000) //IP주소 , 포트번호 입력
2) 데이터 송수신을 위한 in,output Stream
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutPutStream();
이제는 서버와 반대로 메서드를 수행시키면 끝입니다.
3) outputStream을 통한 데이터 송신 (Client->Server)
String outputMessage = "send msg";
out.write(OutputMessage.getBytes());
out.flush();
4) inputstream을 통한 데이터 수신(client -> server)
byte[] inputData = new byte[100];
int length = in.read(inputData);
String inputMessage = new String(InputData,0,length);
5) 통신종료
socket.close();
실행을 시킬 때 클라이언트부터 실행시키는게 아닌, 서버를 실행시켜서 응답메시지를 전송 한 후에
클라이언트를 실행시켜서, 콘솔창에 입력을 하면은 서버에서 날린 메시지가 클라이언트한테 뜨는 것을
확인할 수 있었습니다.
1) 기본적으로 서버가 활성화 된 후에 클라이언트를 실행시킬 수 있음
그리고 서버를 활성화 시킨후 종료하지않고, 바로 클라이언트 클래스를 실행시켜야지 실행이 됨.
서버를 disconnected한 후 클라이언트 클래스 실행시키면 서버연결종료라는 문구가 나온걸 알수 있었음.
2) 클라이언트 클래스부터 실행시키면 아래처럼,에러는 아니지만 오류메시지가 나옴.
3) 소켓 프로그래밍을 이해하기 위해선, 자세하게 소켓 구조와 동작원리를 알아야함을 깨달음. 다음 기회에 더 공부를 하고
내용을 추가해 보도록 하겠습니다.
#참고
Address already in use: bind 전 맨처음에 이런 오류가 나왔는데 이 오류는
제가 톰캣 서버 구동할때 구동 포트를 8080을 설정해놨서 입니다.
8080포트는 톰캣이 서버 구동할때 사용하는 포트인데, 소켓을 사용할 때 서버 연결 포트를 8080으로 해버리면
오류가 나는 것 입니다.
그러므로 8000포트로 진행하시는걸 추천합니다.
굳이 8080포트를 사용하고 싶으시면
1) CMD를 킨다
2) netstat -lntp 명령어 사용
3) table처럼 생긴 포트번호 표가 나올텐데 제일 오른쪽에 있는 PID번호를 확인 후 , PID가 0000/java이면
kill -9 0000 을 해주면 됩니다.