다중의 클라이언트끼리 채팅하는 프로그램
- 각각 클라이언트마다 서버가 1:1로 연결되어 있으나 서버는 입력,출력하는 역활을 하지 않고 클라이언트가 입력한 값을 받아서 서버에 연결되어 있는 모든 클라이언트에게 전송하는 쓰레드를 만들고, 관리하는 역활만 함
TCP 서버
package ex06multiuser2;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
//tcp 서버 클래스
class ServerClass {
private ArrayList<ThreadServerClass> threadList = new ArrayList<>();
public ServerClass(int port) throws IOException {
Socket s = null;
ServerSocket ss = new ServerSocket(port);
System.out.println("서버 가동");
while(true) {
s = ss.accept();
System.out.println("접속주소: "+s.getInetAddress()+", 접속포트: "+s.getPort());
ThreadServerClass tServer = new ThreadServerClass(s, threadList);
tServer.start();//새로운 클라이언트가 접속할때마다 서버쓰레드 생성
threadList.add(tServer); //실행된 쓰레드를 리스트에 add
System.out.println("접속자 수: " + threadList.size());
}
}
}
//메인 클래스
public class TcpMulServer {
public static void main(String[] args) throws IOException {
if(args.length!=1) {
System.out.println("사용법: java [패키지명].[파일명] [포트번호]");
System.exit(1);
}
new ServerClass(Integer.parseInt(args[0]));
//서버는 채팅하지 않고 들어오는 사람들에게 뿌려주는 역활만 수행
}
}
서버에서 클라이언트에게 받아온 값을 모든 클라이언트에게 다시 전달하는 역활을 하는 쓰레드
package ex06multiuser2;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
class ThreadServerClass extends Thread {
private Socket socket;
private DataInputStream inputStream;
private DataOutputStream outputStream;
private ArrayList<ThreadServerClass> threadList;
public ThreadServerClass(Socket s, ArrayList<ThreadServerClass> threadList) throws IOException {
socket = s;
inputStream = new DataInputStream(s.getInputStream());
outputStream = new DataOutputStream(s.getOutputStream());
this.threadList = threadList;
}
public void sendChat(String chat) throws IOException {
for(int i=0;i<threadList.size();i++) {
threadList.get(i).outputStream.writeUTF(chat);
}
}
@Override
public void run() {
String nickname = "";
try {
if(inputStream != null) {
nickname = inputStream.readUTF();
sendChat(nickname + " 님이 채팅방에 입장하셨습니다.");
}
while(inputStream != null) {
sendChat(inputStream.readUTF());
//클라이언트가 보낸 채팅 내용을 접속한 모두에게 보냄
}
//while문 때문에 try문에서는 finally로 갈 일이 없음
} catch (IOException e) { //에러가 생겼을때 -> 유저가 나갔을때
// e.printStackTrace(); //주석시키면 에러메세지 안나옴
} finally {//try문에서는 절대 finally로 안넘어옴, catch문에서만 finally로 옴
//나간 쓰레드의 인덱스 찾기
for(int i=0;i<threadList.size();i++) {
if(socket.equals(threadList.get(i).socket)) {
threadList.remove(i);
try {
sendChat(nickname + " 님이 채팅방에서 나가셨습니다.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("접속자수: " + threadList.size() + " 명");
}
}
}
TCP 클라이언트(클라이언트 GUI 클래스를 생성 후 자기의 소캣 정보를 보냄)
package ex06multiuser2;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class TcpMulClient {
public static void main(String[] args) {
if(args.length!=3) {
System.out.println("사용법: java [패키지명].[파일명] [ip주소] [포트번호] [닉네임]");
System.exit(1);
}
try {
// ip 주소 포트번호
Socket s = new Socket(args[0], Integer.parseInt(args[1])); //해당 ip주소에 해당 포트번호로 연결하겠다
System.out.println("서버에 연결");//connect
//i/o network stream: readUTF(), writeUTF() 사용을 위해 준비
DataOutputStream outputStream = new DataOutputStream(s.getOutputStream());
DataInputStream inputStream = new DataInputStream(s.getInputStream());
outputStream.writeUTF("##" + args[2]);
//gui를 담당하는 .java 파일 하나 만들기, 거기에 보낼 인자를 준비
new ClientGUI(outputStream, inputStream, args[2]);
} catch (Exception e) {
e.printStackTrace(); //주석을 달아야 에러가 화면에 안나타남
}
}
}
클라이언트 GUI
package ex06multiuser2;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ClientGUI extends JFrame implements Runnable, ActionListener {
//넘어오는 io스트림이랑 닉네임을 받을 필드들
DataOutputStream outputStream;
DataInputStream inputStream;
String nickname;
//gui
JLabel label1 = new JLabel("*** 채팅창 ***");
JTextArea area1 = new JTextArea();
JTextField field1 = new JTextField();
JScrollPane scroll = new JScrollPane(area1);
public ClientGUI(DataOutputStream outputStream, DataInputStream inputStream, String nickname) {
this.outputStream = outputStream;
this.inputStream = inputStream;
this.nickname = nickname;
setLayout(new BorderLayout());
// North, South, West, East, Center
//라벨
label1.setFont(new Font("굴림", Font.BOLD, 22));
add("North", label1);
//채팅 문자열이 출력되는 곳
area1.setBackground(Color.yellow);
area1.setForeground(Color.BLUE);
area1.setFont(new Font("굴림", Font.BOLD, 22));
area1.setEditable(false); //편집 못하게 하기, 한번 올라간 텍스트는 수정할 수 없음
add("Center", scroll); //scroll이랑 area1이랑 연결되어 있음
//채팅 치는곳
field1.setBackground(Color.white);
field1.setForeground(Color.black);
field1.setFont(new Font("굴림", Font.BOLD, 25));
add("South", field1);
field1.addActionListener(this);//이벤트 등록
setSize(800, 800);
setVisible(true);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
dispose();
System.exit(0);//이걸 해주어야 퇴장이 됨
setVisible(false);
}
});
Thread th = new Thread(this); //리시브 쓰레드, 메인 쓰레드는 보내는 용도
th.start();
}//생성자 end
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == field1) {//field1에서 이벤트가 발생하면(채팅을 치면)
try {
outputStream.writeUTF(nickname + ">>> " + field1.getText()); //닉네임과 방금 친 메세지를 전송
} catch (IOException e2) {
// e2.printStackTrace();
}
field1.setText(""); //칸 비우기
}
}
Toolkit tk = Toolkit.getDefaultToolkit();
//채팅 올때마다 소리 들리게 하는 것
@Override
public void run() {//출력용 쓰레드
try {
while(true) {
String strServer = inputStream.readUTF();//서버로부터
if(strServer==null) {
area1.append("\n"+"종료");
return;
}
area1.append("\n" + strServer); //서버가 전달해준 문자열 채팅창(area1)에 추가
//커서를 맨 뒤로 나오게 하기
int endText = area1.getText().length();
area1.setCaretPosition(endText); //커서를 맨 뒤로(setCaretPosition: 커서 위치 변경)
tk.beep(); //채팅 전달될 때마다 소리 울림
}
} catch (Exception e3) {
area1.append("\n" + e3);
}
}
}
'[back-end] > 자바-소켓' 카테고리의 다른 글
소켓프로그래밍 - 양방향 소켓 (0) | 2021.01.01 |
---|---|
소켓프로그래밍 - 단방향 소켓 프로그램 (0) | 2021.01.01 |
program, process, thread (0) | 2020.12.28 |
소켓 프로그램 (0) | 2020.12.24 |
웹 프로그램 (0) | 2020.12.24 |