카테고리 없음
네트워크(2) / 소켓
Heidong
2021. 9. 9. 22:30
반응형
소켓은 전화기와 같음 나도 전화기가 있어야하고 상대방도 전화기가 있어야 통화가 가능한 것처럼
소켓도 주고받아야할 대상 모두 가지고 있어야함.
클라이언트와 서버의 통신 과정
package netEx;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ChatForm extends JFrame implements ActionListener, Runnable {
private static final long serialVersionUID = 1l;
private JTextField tf = new JTextField();
private JTextArea ta = new JTextArea();
public ChatForm() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ta.setEditable(false);
JScrollPane sp = new JScrollPane(ta);
add(sp, BorderLayout.CENTER);
// JTextField에서 엔터를 누르면 ActionEvent가 발생되므로
// ActionEvent를 등록하여 ActionEvent 처리 - actionPerformed() 메소드에서 처리
tf.addActionListener(this);
add(tf, BorderLayout.SOUTH);
setTitle("채팅");
setSize(500, 550);
setVisible(true);
tf.requestFocus();
}
public static void main(String[] args) {
new ChatForm();
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == tf) {
String s = tf.getText().trim();
if(s.length() == 0)
return;
try {
ta.append("보냄] " + s + "\n");
tf.setText("");
tf.requestFocus(); // 포커스를 JTextField에
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
@Override
public void run() {
}
}
- 클라이언트와 서버의 소켓을 통한 채팅 프로그램 작성을 위해 먼저 채팅 폼을 만들어 준다.
- 폼은 JFrame으로 만들고, ActionEvent를 통해서 엔터를 누르면 입력이 되게 한다.
채팅을 위한 서버를 먼저 구현 해보자
위 소스에서 조금만 더 추가하면 된다.
그대로 가져오자
package netEx;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
// 1:1 채팅
public class ChatServer extends JFrame implements ActionListener, Runnable {
private static final long serialVersionUID = 1l;
private JTextField tf = new JTextField();
private JTextArea ta = new JTextArea();
private ServerSocket ss = null;
private int port = 8000;
private Socket sc = null;
public ChatServer() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ta.setEditable(false);
JScrollPane sp = new JScrollPane(ta);
add(sp, BorderLayout.CENTER);
tf.addActionListener(this);
add(tf, BorderLayout.SOUTH);
setTitle("채팅-서버");
setSize(500, 550);
setVisible(true);
tf.requestFocus();
}
public void serverStart() {
try {
ss = new ServerSocket(port);
ta.setText("서버 시작 !!!\n");
sc = ss.accept(); // 클라이언트의 접속을 기다린다.
// 클라이언트가 접속하면 클라이언트와 통신을 할 수 있는 Socket 객체를 반환 받는다.
// 스레드 실행
Thread t = new Thread(this);
t.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new ChatServer().serverStart();
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == tf) {
String s = tf.getText().trim();
if(s.length() == 0)
return;
try {
if(sc == null) {
return;
}
// 클라이언트에게 정보를 전송 한다.
PrintStream out = new PrintStream(sc.getOutputStream());
// OutputStream을 PrintStream으로 변환
out.println("서버] " + s);
ta.append("보냄] " + s + "\n");
ta.setCaretPosition(ta.getDocument().getLength());
tf.setText("");
tf.requestFocus(); // 포커스를 JTextField에
} catch (IOException e2) {
// e2.printStackTrace();
ta.append("클라이언트가 접속을 해제 했습니다.\n");
sc = null;
}
}
}
@Override
public void run() {
String str;
String ip = "";
if(sc == null) {
return;
}
try {
// 접속한 클라이언트가 보낸 정보를 입력 스트림으로 받는다
BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream()));
// 접속한 클라이언트의 ip 주소
ip = sc.getInetAddress().getHostAddress();
ta.append("["+ip+"] 접속 ...\n");
// 클라이언트가 보낸 정보를 받는다.
while((str = br.readLine()) != null) {
ta.append(str+"\n");
ta.setCaretPosition(ta.getDocument().getLength());
}
} catch (IOException e) {
str = "["+ip+"] 접속 해제 !!!";
ta.append(str+"\n");
sc = null;
}
}
}
(서버 코드)
- 클래스에 Jframe 상속받고 ActionListener와 Runnable 인터페이스를 상속 받는다.
-
(클라이언트 코드)
package netEx;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ChatClient extends JFrame implements ActionListener, Runnable {
private static final long serialVersionUID = 1l;
private JTextField tf = new JTextField();
private JTextArea ta = new JTextArea();
private Socket sc = null;
private String host = "127.0.0.1";
private int port = 8000;
private String nickName = "스프링";
public ChatClient() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ta.setEditable(false);
JScrollPane sp = new JScrollPane(ta);
add(sp, BorderLayout.CENTER);
tf.addActionListener(this);
add(tf, BorderLayout.SOUTH);
setTitle("채팅-클라이언트");
setSize(500, 550);
setVisible(true);
tf.requestFocus();
}
public void connect() {
// 서버에 연결
try {
sc = new Socket(host, port);
ta.setText("서버에 접속...\n");
Thread t = new Thread(this);
t.start();
} catch (IOException e) {
// 서버가 실행되지 않은 경우
sc = null;
e.printStackTrace();
}
}
public static void main(String[] args) {
new ChatClient().connect();
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == tf) {
String s = tf.getText().trim();
if(s.length() == 0)
return;
try {
if(sc == null) {
return;
}
// 서버에 정보를 전송하기 위한 출력 스트림
PrintStream out = new PrintStream(sc.getOutputStream());
out.println(nickName+"] " + s); // 서버에게 문자열 전송
ta.append("보냄] " + s + "\n");
ta.setCaretPosition(ta.getDocument().getLength());
tf.setText("");
tf.requestFocus(); // 포커스를 JTextField에
} catch (IOException e2) {
ta.append("서버가 종료 됨 !!!\n");
sc = null;
// e2.printStackTrace();
}
}
}
@Override
public void run() {
String s;
try {
if(sc == null) {
return;
}
// 서버가 보낸 정보를 받기 위한 입력 스트림
BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream()));
while((s = br.readLine())!=null) {
ta.append(s+"\n");
ta.setCaretPosition(ta.getDocument().getLength());
}
} catch (IOException e) {
s = "서버가 종료 되었습니다.\n";
ta.append(s);
sc = null;
// e.printStackTrace();
}
}
}
1대 다 채팅
package netEx;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
// 1:다 채팅 프로그램
public class ChatServer2 {
// 접속한 클라이언트를 저장하기 위한 벡터 객체
private Vector<Socket> client = new Vector<>();
private ServerSocket ss = null;
private int port = 8000;
// 내부 스레드 클래스
class WorkerThread extends Thread {
private Socket sc = null;
public WorkerThread(Socket sc) {
this.sc = sc;
}
@Override
public void run() {
String ip = null;
String msg;
try {
// 클라이언트가 보낸 정보를 받기 위한 입력 스트림
BufferedReader br = new BufferedReader(new InputStreamReader(sc.getInputStream()));
ip = sc.getInetAddress().getHostAddress();
// 접속한 클라이언트 소켓을 벡터에 저장
client.add(sc);
// 다른 클라이언트에게 접속 사실을 알림
msg = "["+ip+"] 에서 접속 !!!";
sendMsg(msg);
System.out.println(msg);
// 클라이언트가 보낸 정보를 받아 다른 클라이언트에게 보냄
while((msg = br.readLine())!=null) {
sendMsg(msg);
}
} catch (IOException e) {
// 클라이언트가 채팅방을 나간 경우
msg = "["+ip+"] 퇴장...";
sendMsg(msg);
client.remove(sc);
sc = null;
System.out.println(msg);
}
}
// 다른 모든 클라이언트에게 메시지 전송
public void sendMsg(String msg) {
for(Socket s : client) {
try {
if( s == sc ) {
continue; // 자기 자신
}
PrintStream out = new PrintStream(s.getOutputStream());
out.println(msg);
} catch (Exception e) {
}
}
}
} // WorkerThread_end
public void serverStart() {
try {
ss = new ServerSocket(port);
System.out.println("서버 시작 !!!");
while(true) {
// 클라이언트 접속을 기다린다.
Socket sc = ss.accept();
// 클라이언트가 접속하면 클라이언트가 보낸 정보를 받을 스레드 객체를 생성하고 스레드를 실행한다.
WorkerThread t = new WorkerThread(sc);
t.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ChatServer2 obj = new ChatServer2();
obj.serverStart();
}
}
반응형