안녕하세요 J.sieun 입니다.
이번글은 자바 중에서 좀 까다로운 Socket 이 되겠습니다.
우선 데이터를 어떻게 주고 받는지에 대해 설명해 보겠습니다.
용어 설명:
-클라이언트 프로그램(Client Program) : 연결을 요청하는 통신 프로그램.
-서버 프로그램(Server Program) : 연결 요청을 기다리는 통신 프로그램.
클라이언트 프로그램과 서버 프로그램의 통신 과정
간단하게 저런 식으로 정보를 주고 받습니다.
Java 에서는 소켓(Socket) 이라고 하는 것을 지원 합니다.
-서버 소켓
서버 프로그램에서만 사용되는 소켓
연결 요청을 기다리다가, 연결 요청이 오면 연결을 맺고 또 다른 소켓을 생성
-클라이언트 소켓
클라이언트 프로그램과 서버 프로그램에서 모두 사용되는 소켓
실제 데이터 전송에서 사용되는 것은 이 소켓임
서버 프로그램에서는 서버 소켓에 의해 생성됨
클라이언트 프로그램에서는 직접 생성해야함
간단히 Swing 으로 한 채팅 프로그램 소스를 보도록 하겠습니다.
서버 부분
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
public class ServerChat {
static JFrame frame;
static JPanel panel;
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
frame = new JFrame();
frame.setTitle("서버");
frame.setSize(300, 600);
frame.setLocation(500, 400);
frame.setResizable(false);
Container contentPane = frame.getContentPane();
contentPane.setLayout(null);
JPanel panel = new JPanel();
panel.setLocation(300, 10);
panel.setSize(500, 500);
contentPane.add(panel);
JTextArea ta = new JTextArea();
ta.setSize(286, 400);
ta.setLocation(0, 0);
ta.setEditable(false);
JScrollPane scroll = new JScrollPane(ta);
scroll.setSize(286, 400);
scroll.setLocation(0, 0);
contentPane.add(scroll);
JLabel ipLabel = new JLabel("IP주소");
ipLabel.setLocation(20, 415);
ipLabel.setSize(50, 15);
contentPane.add(ipLabel);
JTextField ipField = new JTextField();
ipField.setLocation(78, 410);
ipField.setSize(167, 25);
contentPane.add(ipField);
JLabel portLabel = new JLabel("포트번호");
portLabel.setLocation(20, 460);
portLabel.setSize(60, 15);
contentPane.add(portLabel);
JTextField portField = new JTextField();
portField.setLocation(78, 455);
portField.setSize(50, 25);
contentPane.add(portField);
JButton start = new JButton("서버실행");
start.setLocation(150, 455);
start.setSize(95, 25);
contentPane.add(start);
JTextField chatField = new JTextField(" ");
chatField.setLocation(20, 500);
chatField.setSize(160, 25);
contentPane.add(chatField);
final JButton send = new JButton("전송");
send.setLocation(185, 500);
send.setSize(60, 25);
contentPane.add(send);
chatField.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
send.doClick();
}
}
});
start.addActionListener(new ServerStartActionListener(ta, serverSocket,
socket, portField, send, chatField, scroll, panel));
try {
InetAddress add = InetAddress.getLocalHost();
String strAddress = add.getHostAddress();
ta.append("서버의 IP주소는 : " + strAddress + "\n"
+ "채팅에 사용할 포트 번호를 입력하세요.\n");
ipField.setText(strAddress);
//테스트
//portField.setText("9999");
} catch (Exception e) {
System.out.println(e.getMessage());
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
panel.setFocusable(true);
panel.requestFocus();
}
}
class ServerStartActionListener implements ActionListener {
JTextArea ta;
ServerSocket serverSocket;
Socket socket;
JTextField portField;
JTextField chatField;
JButton send;
JScrollPane scroll;
JPanel panel;
ServerStartActionListener(JTextArea ta, ServerSocket serverSocket,
Socket socket, JTextField portField, JButton send,
JTextField chatField, JScrollPane scroll, JPanel panel) {
this.ta = ta;
this.serverSocket = serverSocket;
this.socket = socket;
this.portField = portField;
this.send = send;
this.chatField = chatField;
this.scroll = scroll;
this.panel = panel;
}
public void actionPerformed(ActionEvent e) {
try {
int port = Integer.parseInt(portField.getText());
serverSocket = new ServerSocket(port);
socket = serverSocket.accept();
Thread thread2 = new ServerReceiverThread(socket, ta, scroll, panel);
thread2.start();
send.addActionListener(new ServerSendActionListener(ta, socket, chatField, serverSocket, scroll, panel));
} catch (Exception e1) {
ta.append("서버생성 오류");
} finally {
ta.append("채팅을 시작하세요.\n");
}
}
}
class ServerSendActionListener implements ActionListener {
JTextArea ta;
Socket socket;
JTextField chatField;
ServerSocket serverSocket;
JScrollPane scroll;
JPanel panel;
JLabel laServer;
ServerSendActionListener(final JTextArea ta, final Socket socket, JTextField chatField,
ServerSocket serverSocket, JScrollPane scroll, final JPanel panel) {
this.ta = ta;
this.chatField = chatField;
this.serverSocket = serverSocket;
this.socket = socket;
this.scroll = scroll;
this.panel = panel;
panel.addKeyListener( new KeyAdapter() {
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
System.out.println("keyCode = " + keyCode);
switch(keyCode) {
case KeyEvent.VK_UP:
laServer.setLocation(laServer.getX(), laServer.getY()-10);
break;
case KeyEvent.VK_DOWN:
laServer.setLocation(laServer.getX(), laServer.getY()+ 10);
break;
case KeyEvent.VK_LEFT:
laServer.setLocation(laServer.getX()- 10, laServer.getY());
break;
case KeyEvent.VK_RIGHT:
laServer.setLocation(laServer.getX()+ 10, laServer.getY());
break;
}
try {
ta.append("송신>" + "#" + keyCode + "\n");
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.println("#" + keyCode);
writer.flush();
} catch (Exception e1) {
ta.append("메세지 전송실패 : " + e1.toString());
}
}
} );
panel.setFocusable(true);
panel.requestFocus();
}
public void actionPerformed(ActionEvent e) {
try {
PrintWriter writer = new PrintWriter(socket.getOutputStream());
String str = chatField.getText();
ta.append("송신>" + str + "\n");
scroll.getVerticalScrollBar().setValue(
scroll.getVerticalScrollBar().getMaximum());
//chatField.requestFocus();
chatField.setText(" ");
writer.println(str);
writer.flush();
panel.requestFocus();
} catch (Exception e1) {
ta.append("메세지 전송실패 : " + e1.toString());
}
}
}
class ServerReceiverThread extends Thread {
Socket socket;
JTextArea ta;
JScrollPane scroll;
JPanel panel;
int keyCode;
ServerReceiverThread(Socket socket, JTextArea ta, JScrollPane scroll, JPanel panel) {
this.ta = ta;
this.socket = socket;
this.scroll = scroll;
this.panel = panel;
}
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
while (true) {
String str = reader.readLine();
ta.append("수신>" + str + "\n");
scroll.getVerticalScrollBar().setValue(
scroll.getVerticalScrollBar().getMaximum());
if ( str.charAt(0) == '#') {
String number = str.substring(1,3);
keyCode = Integer.parseInt(number);
switch(keyCode) {
}
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
socket.close();
} catch (Exception ignored) {
}
}
}
}
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
public class ClientChat {
static JFrame frame;
static JPanel panel;
public static void main(String[] args) {
final Socket socket = null;
frame = new JFrame();
frame.setTitle("클라이언트");
frame.setSize(300, 600);
frame.setLocation(500, 400);
frame.setResizable(false);
Container contentPane = frame.getContentPane();
contentPane.setLayout(null);
panel = new JPanel();
panel.setLocation(300, 10);
panel.setSize(300, 500);
contentPane.add(panel);
final JTextArea ta = new JTextArea();
ta.setSize(286, 400);
ta.setLocation(0, 0);
ta.setEditable(false);
JScrollPane scroll = new JScrollPane(ta);
scroll.setSize(286, 400);
scroll.setLocation(0, 0);
contentPane.add(scroll);
JLabel ipLabel = new JLabel("IP주소");
ipLabel.setLocation(20, 415);
ipLabel.setSize(50, 15);
contentPane.add(ipLabel);
JTextField ipField = new JTextField();
ipField.setLocation(78, 410);
ipField.setSize(167, 25);
contentPane.add(ipField);
JLabel portLabel = new JLabel("포트번호");
portLabel.setLocation(20, 460);
portLabel.setSize(60, 15);
contentPane.add(portLabel);
JTextField portField = new JTextField();
portField.setLocation(78, 455);
portField.setSize(50, 25);
contentPane.add(portField);
JButton start = new JButton("서버접속");
start.setLocation(150, 455);
start.setSize(95, 25);
contentPane.add(start);
JTextField chatField = new JTextField(" ");
chatField.setLocation(20, 500);
chatField.setSize(160, 25);
contentPane.add(chatField);
final JButton send = new JButton("전송");
send.setLocation(185, 500);
send.setSize(60, 25);
contentPane.add(send);
chatField.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
send.doClick();
}
}
});
start.addActionListener(new ClientStartActionListener(ta, socket,
portField, send, chatField, scroll, ipField, panel));
try {
ta.append("서버의 IP주소와 포트번호를 입력해주세요.\n");
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
} catch (Exception e) {
System.out.println(e.getMessage());
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
panel.setFocusable(true);
panel.requestFocus();
}
}
class ClientStartActionListener implements ActionListener {
JTextArea ta;
Socket socket;
JTextField portField;
JTextField chatField;
JButton send;
JScrollPane scroll;
JTextField ipField;
JPanel panel;
ClientStartActionListener(JTextArea ta, Socket socket,
JTextField portField, JButton send, JTextField chatField,
JScrollPane scroll, JTextField ipField, JPanel panel) {
this.ta = ta;
this.socket = socket;
this.portField = portField;
this.send = send;
this.chatField = chatField;
this.scroll = scroll;
this.ipField = ipField;
this.panel = panel;
}
public void actionPerformed(ActionEvent e) {
try {
int port = Integer.parseInt(portField.getText());
String ip = ipField.getText();
socket = new Socket(ip, port);
Thread thread2 = new ClientReceiverThread(socket, ta, scroll, panel);
thread2.start();
send.addActionListener(new ClientSendActionListener(ta, socket, chatField, scroll, panel));
} catch (Exception e1) {
ta.append("서버접속 오류");
} finally {
ta.append("채팅을 시작하세요.\n");
}
}
}
class ClientSendActionListener implements ActionListener {
JTextArea ta;
Socket socket;
JTextField chatField;
JScrollPane scroll;
JPanel panel;
ClientSendActionListener(final JTextArea ta, final Socket socket, JTextField chatField,
JScrollPane scroll, final JPanel panel) {
this.ta = ta;
this.chatField = chatField;
this.socket = socket;
this.scroll = scroll;
this.panel = panel;
panel.addKeyListener( new KeyAdapter() {
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
try {
ta.append("송신>" + "#" + keyCode + "\n");
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.println("#" + keyCode);
writer.flush();
} catch (Exception e1) {
ta.append("메세지 전송실패 : " + e1.toString());
}
}
} );
panel.setFocusable(true);
panel.requestFocus();
}
public void actionPerformed(ActionEvent e) {
try {
PrintWriter writer = new PrintWriter(socket.getOutputStream());
String str = chatField.getText();
ta.append("송신>" + str + "\n");
scroll.getVerticalScrollBar().setValue(
scroll.getVerticalScrollBar().getMaximum());
chatField.requestFocus();
chatField.setText(" ");
writer.println(str);
writer.flush();
panel.requestFocus();
} catch (Exception e1) {
ta.append("메세지 전송실패 : " + e1.toString());
}
}
}
class ClientReceiverThread extends Thread {
Socket socket;
JTextArea ta;
JScrollPane scroll;
JPanel panel;
int x, y;
int keyCode;
ClientReceiverThread(Socket socket, JTextArea ta, JScrollPane scroll, JPanel panel) {
this.ta = ta;
this.socket = socket;
this.scroll = scroll;
this.panel = panel;
}
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
while (true) {
String str = reader.readLine();
if (str.equals("bye"))
break;
ta.append("수신>" + str + "\n");
scroll.getVerticalScrollBar().setValue(
scroll.getVerticalScrollBar().getMaximum());
}
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
socket.close();
} catch (Exception ignored) {
}
}
}
}
결과 화면
서버 소켓의 기본적인 알고리즘이 되겠습니다.
참고로 자바 Swing 부분은 해석에서 제외 하도록 하겠습니다.
소스 해석:
서버 쪽부터 해석 하겠습니다.
try {
InetAddress add = InetAddress.getLocalHost();
String strAddress = add.getHostAddress();
ta.append("서버의 IP주소는 : " + strAddress + "\n"
+ "채팅에 사용할 포트 번호를 입력하세요.\n");
ipField.setText(strAddress);
//테스트
//portField.setText("9999");
} catch (Exception e) {
System.out.println(e.getMessage());
}
이 부분 서버 IP주소를 Swing 창에 띄움으로서 클라이언트가 서버에 원활히 접속할수 있게 도와주는 부분입니다.
start.addActionListener(new ServerStartActionListener(ta, serverSocket,
socket, portField, send, chatField, scroll, panel));
이부분은 서버 실행이라는 버튼을 클릭시 ServerStartActionListener 이 함수를 호출할것이다 라는 뜻입니다.
public void actionPerformed(ActionEvent e) {
try {
int port = Integer.parseInt(portField.getText());
String ip = ipField.getText();
socket = new Socket(ip, port);
Thread thread2 = new ClientReceiverThread(socket, ta, scroll, panel);
thread2.start();
send.addActionListener(new ClientSendActionListener(ta, socket, chatField, scroll, panel));
} catch (Exception e1) {
ta.append("서버접속 오류");
} finally {
ta.append("채팅을 시작하세요.\n");
}
}
이 부분은 서버쪽에서 포트번호를 입력한후 서버 실행 버튼을 클릭해서 포트번호에 맞게 접속하는 부분입니다.
그후 클라이언트와 연결이 되면
class ServerReceiverThread extends Thread {
이 클래스가 실행이 됩니다 그리고
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
while (true) {
String str = reader.readLine();
ta.append("수신>" + str + "\n");
scroll.getVerticalScrollBar().setValue(
scroll.getVerticalScrollBar().getMaximum());
if ( str.charAt(0) == '#') {
String number = str.substring(1,3);
keyCode = Integer.parseInt(number);
switch(keyCode) {
}
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
socket.close();
} catch (Exception ignored) {
}
}
Thread 가 실행이 됩니다.
클라이언트 부분
start.addActionListener(new ClientStartActionListener(ta, socket,
portField, send, chatField, scroll, ipField, panel));
public void actionPerformed(ActionEvent e) {
try {
int port = Integer.parseInt(portField.getText());
String ip = ipField.getText();
socket = new Socket(ip, port);
Thread thread2 = new ClientReceiverThread(socket, ta, scroll, panel);
thread2.start();
send.addActionListener(new ClientSendActionListener(ta, socket, chatField, scroll, panel));
} catch (Exception e1) {
ta.append("서버접속 오류");
} finally {
ta.append("채팅을 시작하세요.\n");
}
}
서버쪽 부분과 거의 비슷하지만 약간 다릅니다. IP주소와 포트번호를 개설이 아닌 접속을 합니다.
class ClientSendActionListener implements ActionListener {
이부분은 클라이언트가 서버쪽에게 메시지를 보내는 부분이 되겠습니다.
소켓의 가장 기본적인 소스이므로 이부분을 이해 잘하셔야 이것들을 가지고 응용을 하실수 있습니다.
간단히 소스해석을 해보았습니다.
좀더 궁금하신 분은 댓글 남겨주시거나
j.sieun73@gmail.com 여기로 메일주시면 친절히 답변해드리도록 하겠습니다.
이것말고도 제가 개발한 교차 소켓 알고리즘이 있는데 그것에 대해 알고싶다면
http://www.jaenung.net/?mid=view&no=5905
여기로 문의 주시면 알려드리도록 하겠습니다 :D
(ps.공짜는 아니됩니다.ㅜㅜ제가 노력한게 있기 때문에..)
'java' 카테고리의 다른 글
쉽지만 어려운 #Java #자바 #성공적 9. Scanner(스캐너)를 이용한 FileWriter(입력) (1) | 2016.07.09 |
---|---|
쉽지만 어려운 #Java #자바 #성공적 8. Socket.io 의 Serializable(직렬화) (0) | 2016.06.26 |
쉽지만 어려운 #Java #자바 #성공적 7.Mysql (JDBC) 연동하기 2부 (2) | 2016.06.19 |
쉽지만 어려운 #Java #자바 #성공적 7.Mysql (JDBC) 연동하기 1부 (0) | 2016.06.03 |
쉽지만 어려운 #Java #자바 #성공적 6.Swing(GUI) 2부 (0) | 2016.05.27 |