안녕하세요.
간단하게 만들 수 있는 JAVA의 SOCEKT을 활용한 채팅프로그램 소스에요~
클래스는 ChatServer, ChatThread, ChattingClient 세가지로 구성 되어있습니다.
소스에 보시면 주석 달아져 있구요.
Server 실행시에는 Thread 클래스가 필요합니다.
화면은 swing과 awt로 구성되어 있어요.
화면 구성 툴은 eclipse의 windowBuilder 을 사용하면 편해요
아래 링크는 windowBuilder 설치하는 방법과 사용법이니까 가서 확인하세요!
http://felixblog.tistory.com/77
그럼 궁금한 점 있으면 댓글 달아주세요 ~~!
import java.io.IOException;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
public class ChatServer {
Vector clientVector = new Vector(); // 현재 연결된 클라이언트 정보를 유지하고 있는 데이터
HashMap<String, ChatThread> map = new HashMap<String, ChatThread>();
int clientNum = 0; // 접속된 클라이언트의 수
// 접속된 모든 클라이언트에게 메시지 msg 를 보냄
public void broadcast(String msg) throws IOException {
synchronized (clientVector) {
for (int i = 0; i < clientVector.size(); i++) {
ChatThread client = (ChatThread) clientVector.elementAt(i);
synchronized (client) {
client.sendMessage(msg);
}
}
}
}
public void addClientId(String id, ChatThread client) {
map.put(id, client);
System.out.println("add => size:"+map.size());
for (Map.Entry<String, ChatThread> entry : map.entrySet()) {
System.out.println("Key : " + entry.getKey() + " Value : "+ entry.getValue());
}
}
public void removeClientId(String id) {
// map.remove(id);
// System.gc();
System.out.println("id => " + id);
System.out.println("v :: " + map.get(id));
System.out.println("remove => size:"+map.size());
for (Map.Entry<String, ChatThread> entry : map.entrySet()) {
System.out.println("Key : " + entry.getKey() + " Value : "+ entry.getValue());
}
}
public void unicast(String id, String msg) throws IOException {
ChatThread client = map.get(id);
client.sendMessage(msg);
}
// 종료시 clientVector에 저장되어 있는 클라이언트 정보를 제거
public void removeClient(ChatThread client) {
synchronized (clientVector) {
clientVector.removeElement(client);
client = null;
System.gc();
}
}
// 처음 연결되었을 때 clientVector에 해당 클라이언트의 정보를 추가
public void addClient(ChatThread client) {
synchronized (clientVector) {
clientVector.addElement(client);
}
}
// 서버의 시작 메인 메소드
HashMap<String, String> aaa = new HashMap<String, String>();
public static void main(String[] args) {
// 서버 소켓
ServerSocket myServerSocket = null;
// ChatServer 객체 생성
ChatServer myServer = new ChatServer();
try {
// 서버 포트 10129를 가지는 서버 소켓 생성
myServerSocket = new ServerSocket(10129);
} catch (IOException e) {
System.out.println(e.toString());
System.exit(-1);
}
System.out.println("[서버 대기 상태] " + myServerSocket);
try {
// 다수의 클라이언트 접속을 처리하기 위해 반복문으로 구현
while (true) {
// 클라이언트가 접속되었을 경우 이 클라이언트를 처리하기 위한 ChatThread 객체 생성
ChatThread client = new ChatThread(myServer,
myServerSocket.accept());
// 클라이언트에게 서비스를 제공하기 위한 쓰레드 동작
client.start();
// clientVector 에 클라이언트 객체 추가
myServer.addClient(client);
// 접속한 클라이언트의 수 증가
}
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Map;
import java.util.StringTokenizer;
public class ChatThread extends Thread {
ChatServer myServer; // ChatServer 객체
Socket mySocket; // 클라이언트 소켓
String id;
PrintWriter out; // 입출력 스트림
BufferedReader in;
public ChatThread(ChatServer server, Socket socket) // 생성자
{
super("ChatThread");
myServer = server;
mySocket = socket;
out = null;
in = null;
}
public void sendMessage(String msg) throws IOException // 메시지를 전송
{
out.println(msg);
out.flush();
}
public void disconnect() // 연결을 종료
{
try {
out.flush();
in.close();
out.close();
mySocket.close();
myServer.removeClient(this);
} catch (IOException e) {
System.out.println(e.toString());
}
}
public void run() // 쓰레드 시작
{
try {
// 소켓을 이용하여 상대방과 입출력 스트림을 생성
out = new PrintWriter(new OutputStreamWriter(
mySocket.getOutputStream()));
in = new BufferedReader(new InputStreamReader(
mySocket.getInputStream()));
while (true) { // 클라이언트 보낸 메시지를 처리하기 위해 기다림
String inLine = in.readLine(); // 기다리고 있다가 클라이언트가 보낸 메시지가 있는 경우
// 읽어들임
if (!inLine.equals("") && !inLine.equals(null)) {
messageProcess(inLine); // 클라이언트가 보낸 메시지를 확인하여 현재 접속한 모든
// 클라이언트에게 브로드캐스트
}
}
} catch (Exception e) {
disconnect();
}
}
// 클라이언트가 보낸 메시지를 확인한 후 처리
public void messageProcess(String msg) {
StringTokenizer st = new StringTokenizer(msg, "|"); // 규칙에 따라 받은 메시지를
// 분리하여 확인
String command = st.nextToken(); // 메시지의 명령 command 부분
String talk = st.nextToken(); // 메시지의 대화 talk 부분
if (command.equals("LOGIN")) { // 받은 메시지가 LOGIN 이면 처음 접속 메시지이기 때문에 접속 처리
try { // 새로운 클라이언트가 접속하여 추가된 클라이언트 수를 브로드캐스트
myServer.clientNum++;
myServer.map.put(talk, this);
String ids = "";
for (Map.Entry<String, ChatThread> entry : myServer.map.entrySet()) {
ids += entry.getKey()+"*";
}
myServer.broadcast("append|"+ids);
} catch (IOException e) {
System.out.println(e.toString());
}
} else if (command.equals("LOGOUT")) { // 받은 메시지가 LOGOUT 이면 종료 메시지이므로
// 제거된 클라이언트의 수를
try { // 브로드캐스트
myServer.clientNum--;
myServer.map.remove(talk);
myServer.broadcast("[접속 종료] " + talk + " 님이 대화방을 나가셨습니다.");
myServer.broadcast("remove|"+talk);
} catch (IOException e) {
System.out.println(e.toString());
}
disconnect(); // 연결 종료
} else if (command.equals("TALK")) {
try { // LOGIN, LOGOUT 이외의 경우는 일반 메시지로 모든 클라이언트에게 받은 메시지 전송
myServer.broadcast(talk);
} catch (IOException e) {
System.out.println(e.toString());
}
} else if (command.equals("WHISPER")) {
String whis = st.nextToken();
System.out.println(myServer.map.get(talk));
try {
myServer.unicast(talk, whis);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/*System.out.println("size=>"+myServer.map.size());
for (Map.Entry<String, ChatThread> entry : myServer.map.entrySet()) {
System.out.println("Key : " + entry.getKey() + " Value : "+ entry.getValue());
}*/
}
}
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
class ChattingClient extends JFrame implements ActionListener,Runnable {
private final String IP_ADDR = "127.0.0.1";
private JLabel lblChat = new JLabel("Chatting Project");
private JLabel lblUser = new JLabel(" \uC720\uC800 \uBAA9\uB85D ");
private JLabel lblNickname = new JLabel(" Nickname ");
private JPanel contentPane;
private JPanel panelChatSub = new JPanel();
private JPanel panelChat = new JPanel();
private JPanel panelChatMenu = new JPanel();
private JPanel panelList = new JPanel();
private JPanel panelRoom = new JPanel();
private JPanel panelUser = new JPanel();
private JPanel panelNickname = new JPanel();
private final JScrollPane scrollPaneUser = new JScrollPane();
private JTextArea txtProject = new JTextArea();
private JTextArea txtChatting = new JTextArea();
private JScrollPane scrChat = new JScrollPane();
/* 채팅 관련 컴포넌트 선언 */
private JButton btnConnect = new JButton(" Connect ");
private List list = new List();
private JTextField txtChatContent = new JTextField();
private JTextField txtNickName = new JTextField();
Socket mySocket = null;
PrintWriter out = null;
BufferedReader in = null;
Thread clock;
public static String IP = "127.0.0.1";
private final JPanel panel = new JPanel();
private final Choice ch = new Choice();
/**
* UI Setting
*/
public ChattingClient() {
setIconImage(Toolkit.getDefaultToolkit().getImage(
"C:\\Users\\DervelJunNote\\Desktop\\icon-30264_640.png"));
setTitle("Chatting Client Test");
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(350, 50, 670, 670);//(100, 100, 1146, 670); //화면 전체 위치 및 크기(좌,상,가로,세로)
contentPane = new JPanel();
contentPane.setBackground(SystemColor.activeCaption);
contentPane.setForeground(new Color(255, 255, 255));
contentPane.setAutoscrolls(true);
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
setContent();
setLayout();
setComponentDisable();
getAction();
}
private void setContent() {
ch.add("TALK");
ch.add("WHISPER");
//화면 전체
panelChat.setLayout(new BorderLayout(0, 0));
panelChat.setBackground(new Color(102, 153, 204));
panelChat.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
panelChat.setBounds(10, 6, 644, 600);//(490, 6, 644, 632);
//제목
lblChat.setForeground(new Color(255, 255, 255));
lblChat.setHorizontalAlignment(SwingConstants.CENTER);
lblChat.setFont(new Font("휴먼편지체", Font.BOLD, 14));
//좌측 패널
panelChatSub.setLayout(new BorderLayout(0, 0));
panelChatSub.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
//좌측 중간 : 메시지 리스트 박스
txtChatting.setForeground(SystemColor.inactiveCaptionText);
txtChatting.setBackground(new Color(255, 255, 224));
txtChatting.setBorder(new EtchedBorder(EtchedBorder.LOWERED, new Color(250, 250, 210), null));
txtChatting.setWrapStyleWord(true);
txtChatting.setLineWrap(true);
scrChat.setColumnHeaderView(panelChatMenu);
scrChat.setViewportView(txtChatting);
//좌측 상단 : 메뉴 박스
panelChatMenu.setBackground(UIManager.getColor("nimbusFocus"));
panelChatMenu.setLayout(new BorderLayout(0, 0));
panelChatMenu.setBorder(new CompoundBorder());
//좌측 상단 : 버튼
btnConnect.setFont(new Font("함초롬바탕", Font.PLAIN, 12));
//좌측 상단 : 닉네임
panelNickname.setLayout(new BorderLayout(0, 0));
panelNickname.setBackground(UIManager.getColor("nimbusFocus"));
panelChatMenu.add(lblNickname, BorderLayout.CENTER);
lblNickname.setForeground(new Color(105, 105, 105));
lblNickname.setFont(new Font("휴먼편지체", Font.PLAIN, 14));
panelChatMenu.add(txtNickName, BorderLayout.EAST);
txtNickName.setForeground(new Color(248, 248, 255));
txtNickName.setBackground(new Color(210, 180, 140));
txtNickName.setFont(new Font("함초롬바탕", Font.PLAIN, 12));
txtNickName.setColumns(10);
panel.setLayout(new BorderLayout(0, 0));
panel.add(txtChatContent);
//좌측 아래 : 메시지 입력 박스
txtChatContent.setForeground(SystemColor.inactiveCaptionText);
txtChatContent.setBackground(new Color(250, 250, 210));
txtChatContent.setColumns(10);
//우측 상단 : 방 목록
panelRoom.setForeground(new Color(255, 255, 255));
panelRoom.setBackground(SystemColor.inactiveCaption);
panelRoom.setLayout(new BorderLayout(0, 0));
panelRoom.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
//우측 패널
panelList.setForeground(new Color(255, 255, 255));
panelList.setBackground(SystemColor.inactiveCaption);
panelList.setLayout(new GridLayout(0, 1, 0, 0));
//우측하단 : 유저 목록
panelUser.setLayout(new BorderLayout(0, 0));
panelUser.setForeground(new Color(255, 255, 255));
panelUser.setBackground(SystemColor.inactiveCaption);
list.setForeground(SystemColor.inactiveCaptionText);
list.setBackground(new Color(255, 239, 213));
scrollPaneUser.setViewportView(list);
list.setFont(new Font("함초롬바탕", Font.PLAIN, 12));
txtChatting.setFont(new Font("함초롬바탕", Font.PLAIN, 12));
txtChatContent.setFont(new Font("함초롬바탕", Font.PLAIN, 12));
panel.add(ch, BorderLayout.WEST);
txtProject.setFont(new Font("휴먼편지체", Font.PLAIN, 14));
}
private void setLayout() {
contentPane.add(panelChat);
panelChat.add(lblChat, BorderLayout.NORTH);
panelChat.add(panelChatSub, BorderLayout.CENTER);
panelChat.add(panelRoom, BorderLayout.EAST);
panelChatSub.add(scrChat);
panelChatMenu.add(btnConnect, BorderLayout.WEST);
panelChatMenu.add(panelNickname, BorderLayout.SOUTH);
panelChatSub.add(panel, BorderLayout.SOUTH);
panelRoom.add(panelList, BorderLayout.CENTER);
panelList.add(panelUser);
panelUser.add(lblUser, BorderLayout.NORTH);
panelUser.add(scrollPaneUser, BorderLayout.CENTER);
}
private void setComponentDisable() {
txtChatting.setEditable(false);
}
/**
* getAction Method
*/
private void getAction() {
/* 채팅 관련 리스너 */
btnConnect.addActionListener(this);
txtNickName.addActionListener(this);
txtChatContent.addActionListener(this);
}
/**
* actionPerformed Method
*/
public void actionPerformed(ActionEvent e) {
if (clock == null || e.getSource() == btnConnect) {
if (txtNickName.getText().equals("")) {
JOptionPane.showMessageDialog(null, "닉네임을 적어주세요", "닉네임이 없습니다",
JOptionPane.ERROR_MESSAGE);
return;
}
try {
mySocket = new Socket(IP, 10129); // 입력받은 서버 IP
// 주소와 포트번호
// 생성된 소켓을 이용해 서버와의 입출력 스트림을 생성
out = new PrintWriter(new OutputStreamWriter(
mySocket.getOutputStream()));
in = new BufferedReader(new InputStreamReader(
mySocket.getInputStream()));
// 쓰레드를 동작시킴
clock = new Thread(this);
clock.start();
} catch (UnknownHostException ie) {
System.out.println(ie.toString());
} catch (IOException ie) {
System.out.println(ie.toString());
}
} else if (e.getSource() == txtChatContent) { // input 텍스트필드에 엔터가 입력될 경우
String data = txtChatContent.getText(); // input 텍스프필드의 값을 읽어서
if (data.equals("exit")) {
// 모든 스트림과 소켓 연결을 끊음
out.println("LOGOUT|" + txtNickName.getText());
out.flush();
try {
out.close();
in.close();
mySocket.close();
} catch (IOException err) {
txtChatting.append(err.toString() + "\n");
}
System.exit(0);
}
txtChatContent.setText("");
// 형식에 맞춰 서버에 메시지를 전송
if (ch.getSelectedItem().equals("TALK")) {
out.println("TALK|" + "[" + txtNickName.getText() + "]" + " : " + data);
} else if (ch.getSelectedItem().equals("WHISPER")) {
if (list.getSelectedItem() != null) {
out.println("WHISPER|" + list.getSelectedItem() + "|" + "["+ list.getSelectedItem() + "<" + txtNickName.getText()
+ "]" + " : " + data);
txtChatting.append("[" + txtNickName.getText() + ">"
+ list.getSelectedItem() + "]" + data + "\n");
} else {
txtChatting.append("*** 오른쪽 대화명을 선택하여 주세요. ***\n");
}
}
out.flush(); // 버퍼에 있는 출력 메시지를 상대방에게 강제로 전송
}
}
public void run(){
out.println("LOGIN|" + txtNickName.getText()); // 초기 서버에 접속한 후 접속 메시지를 보냄
out.flush();
out.println("TALK|`" + txtNickName.getText() + "`님이 접속 하셨습니다.");
out.flush();
while (true) { // 반복문
String msg;
try {
msg = in.readLine();
if (!msg.equals("") && !msg.equals(null)) {
StringTokenizer st = new StringTokenizer(msg, "|");
String cmd = st.nextToken();
if (cmd.equals("append")) {
list.removeAll();
String ids = st.nextToken();
String[] _ids = ids.split("\\*");
for (int i = 0, len = _ids.length; i < len; i++) {
if (!"".equals(_ids[i])) {
list.add(_ids[i]);
}
}
} else if (cmd.equals("remove")) {
String id = st.nextToken();
list.remove(id);
} else {
txtChatting.append(msg + "\n"); // 내 화면의 memo에 받은 메시지 출력
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // 상대방이 보낸 메시지를 읽어들임
}
}
/**
* Launch the application.
*/
public static void main(String[] args) {
try {
UIManager
.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
ChattingClient ss = new ChattingClient();
ss.setVisible(true);
}
}
'JAVA' 카테고리의 다른 글
webflux r2dbc progres ExceptionFactory$PostgresqlBadGrammarException 오 (0) | 2023.07.30 |
---|