15. 채팅프로그램 순천향대학교컴퓨터학부이상정 1 학습내용 사용자인터페이스 프로그램구성 TCP 연결설정프로그램 서버연결설정 클라이언트연결설정 TCP 데이터송수신 순천향대학교컴퓨터학부이상정 2
사용자인터페이스, Form 클래스 순천향대학교컴퓨터학부이상정 3 1:1 채팅프로그램 한프로그램이동시에서버와클라이언트로동작 프로그램시작시서버로동작 서버소켓생성하고상대방접속요청대기 채팅을위한연결요청시클라이언트로동작 다른쪽은상대방이열어놓은서버소켓에접속 Network 클래스를추가하여통신처리를전담 프로젝트이름 : MainChat 컨트롤을배치하고속성을설정 순천향대학교정보기술공학부이상정 IP 네트워크 4
실행결과 순천향대학교컴퓨터학부이상정 5 실행결과 순천향대학교컴퓨터학부이상정 6
사용자인터페이스 컨트롤을배치하고속성을설정 컨트롤속성값 Name Form1 Form Text 1:1 채팅프로그램 Ver 1.0 FormBorderStyle FixedSingle TextBox Name txt_ ip Button Name btn_connect Text 연결 Name txt_info TextBox Multiline True ReadOnly True TextBox Name txt_input 순천향대학교컴퓨터학부이상정 7 Form1.cs: 네임스페이스, 변수추가 Form1.cs 소스파일열고 (Form 선택후더블클릭 ) 네임스페이스와멤버변수추가 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Threading; namespace MainChat public partial class Form1 : Form // 채팅처리를전담하는 Network 클래스객체변수선언 private Network net = null; private Thread server_th = null; // 채팅서버스레드선언 private string my_ip = null; // 자신의 ip 주소를기록할변수 public Form1() InitializeComponent(); net = new Network(this); // 네트워크클래스객체생성 순천향대학교컴퓨터학부이상정 8
Form1.cs: 이벤트처리기추가 (1) Form1 의이벤트처리기추가 Form을선택한후이벤트처리기에서 Load와 FormClosed 이벤트처리기추가 Load 이벤트처리기 채팅서버로동작하게하는쓰레드를생성하고시작 FormClosed 이벤트처리기 생성된쓰레드를종료 순천향대학교컴퓨터학부이상정 9 Form1.cs: 이벤트처리기추가 // 채팅창이출력되기직전에실행되는이벤트 private void Form1_Load(object sender, EventArgs e) my_ip = net.get_myip(); // 자신의 ip 정보알아내기 // 채팅서버를시작시키는쓰레드를생성 server_th = new Thread(new ThreadStart(net.ServerStart)); server_th.start(); // 채팅서버시작 // 채팅창이닫힌후발생하는이벤트 ( 프로그램이종료될때 ) private void Form1_FormClosed(object sender, FormClosedEventArgs e) if (btn_connect.text == " 접속중...") // 클라이언트로수행중 net.disconnect(); // 채팅서버와연결되어있으면연결끊기 else // 서버로수행중 net.serverstop(); // 채팅서버실행중지 if (server_th.isalive) server_th.abort(); //ServerStart 스레드를종료 catch(exception ex) MessageBox.Show(ex.Message); Message); // 예외메시지출력 순천향대학교컴퓨터학부이상정 10
Form1.cs: 버튼이벤트처리기 [ 연결 ] 버튼에대한 [Click] 이벤트처리기를작성 프로그램이클라이언트로동작 private void btn_connect_click(object Click(object sender, EventArgs e) if (btn_connect.text == " 연결 ") // 채팅서버에접속할경우 string ip = txt_ip.text.trim(); // 접속할서버 ip 주소가져오기 if (ip == "") // 접속할채팅서버의 ip 주소를입력하지않았다면 MessageBox.Show(" 아이피번호를입력하세요!"); // 에러메시지출력 return ; if (!net.connect(ip)) // 채팅서버에접속시도 MessageBox.Show(" 서버아이피번호가틀리거나 r n 서버가작동중이지않습니다."); else btn_connect.text =" 접속중..."; // 접속이성공하면버튼문자열변경 else // 채팅을종료할경우 net.disconnect(); // 채팅서버와연결을끊음 btn_connect.text = " 연결 "; // 버튼문자열을 접속중.. 에서 연결 로변경 순천향대학교컴퓨터학부이상정 11 Form1.cs: txt_input 이벤트처리기 txt_input 텍스트박스의 KeyDown 이벤트처리기추가 채팅메시지를입력하고엔터를치면상대컴퓨터로입력한메시지를전송 Form1 클래스에멤버메서드인 Add_MSG 를추가 채팅문자열을텍스트박스 txt_info 에추가 private void txt_input_keydown(object sender, KeyEventArgs e) // 엔터키가눌리면문자열메시지를상대방에게전송 if ( e.keycode == Keys.Enter ) string msg = txt_input.text.trim(); Add_MSG("[ 본인 ] " + msg); net.send(msg); // 메시지전송 txt_input.text = ""; // 다음메시지입력을위해비워둔다. txt_input.focus(); public void Add_MSG(string msg) txt_info.appendtext(msg info.appendtext(msg+" r n"); // 채팅문자열출력 txt_info.scrolltocaret(); // 텍스트박스의내용을현재캐럿위치까지스크롤 txt_input.focus(); //txt_input 텍스트박스에초점맞춤 순천향대학교컴퓨터학부이상정 12
소켓통신, Network 클래스 순천향대학교컴퓨터학부이상정 13 Network 클래스 소켓통신을전담처리하는 Network 클래스작성 [ 솔루션탐색기 ] 에서 MainChat 프로젝트를선택하고마우스오른쪽버튼을클릭한후 [ 추가 ]-[ 새항목추가..] 를선택 새항목추가창에서 [ 클래스 ] 템플릿을선택하고이름에 Network.cs 라고입력한후 [ 열기 ] 버튼을클릭하고프로그램작성 Network 클래스실행동작 채팅서버로실행된다면 ServerStart 메서드와 ServerStop 메서드를사용 클라이언트로사용된다면 Connect 메서드와 DisConnect 메서드를사용 모든데이터는 Receive, Send 메서드를통해바이트단위로수신 / 송신 순천향대학교컴퓨터학부이상정 14
프로그램구성도 순천향대학교컴퓨터학부이상정 15 using System; using System.Net; using System.Net.Sockets; using System.Threading; using System.Text; // 네트워크처리 // 소켓처리 // 쓰레드처리 // 문자열처리 namespace MainChat public class Network Form1 wnd = null; // 채팅창객체변수 Socket server = null; // 채팅서버 ( 접속을받는소켓 ) 로사용할소켓 Socket client = null; // 서버일경우통신소켓, 클라이언트일경우접속및통신용소켓 Thread th = null; // 쓰레드처리 public Network(Form1 wnd) // 생성자 this.wnd = wnd; // NetWork 클래스에서 Form1의멤버를사용하도록허용 // 채팅서버시작 : 클라이언트접속을받고메시지를수신 public void ServerStart() // 서버포트번호를 7000번으로지정 IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 7000); server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); p) server.bind(ipep); // 소켓과서버 ip, 포트번호를바인드 server.listen(10); // 클라이언트접속을대기 wnd.add_msg(" 채팅서버시작..."); 순천향대학교컴퓨터학부이상정 16
client = server.accept(); // 클라이언트가접속할때활성화 // 접속한클라이언트 ip 주소를출력 IPEndPoint ip = (IPEndPoint)client.RemoteEndPoint; wnd.add_msg(ip.address + " 접속..."); //Receive 메서드를스레드로생성하고시작 th = new Thread(new ThreadStart(Receive)); th.start(); // 채팅서버에서예외가발생하면 wnd.add_msg(ex.message); // 예외메시지 txt_info에출력 // 채팅서버중지 public void ServerStop() if (client!= null) // 클라이언트가접속된상태라면 if (client.connected) client.close(); // 통신소켓을닫습니다. if (th.isalive) // Receive 스레드가실행중이라면 th.abort(); // 쓰레드종료 server.close(); // 서버소켓을닫습니다. 순천향대학교컴퓨터학부이상정 17 // 예외메시지출력 wnd.add_msg(ex.message); // 채팅서버와연결 public bool Connect(string ip) // 접속할채팅서버 ip 주소와포트번호를지정 IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ip), 7000); client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); client.connect(ipep); wnd.add_msg(ip + " 서버에접속성공..."); // 채팅문자열을수신하는메서드를스레드로생성하고시작 th = new Thread(new ThreadStart(Receive)); th.start(); return true; wnd.add_msg(ex.message); return false; // 접속성공하면 true 값을반환 // 채팅서버에접속을시도 // 채팅서버접속에실패하면예외메시지를출력 // 접속실패했으면 false 값을반환 순천향대학교컴퓨터학부이상정 18
// 채팅서버와연결종료 public void DisConnect() if (client!= null) if (client.connected) client.close(); // 채팅서버와연결되어있다면 // 채팅서버와의연결을단절 if (th.isalive) th.abort(); wnd.add_msg(" 채팅서버연결종료!"); //Receive 메서드쓰레드를중지 // 채팅서버연결해제와쓰레드종료시예외가발생하면 wnd.add_msg(ex.message); // 예외메시지출력 순천향대학교컴퓨터학부이상정 19 // 상대방에게데이터송신 public void Send(string msg) if (client.connected) // 상대방과연결되어있으면 // 문자열을바이트배열형태로변경합니다. byte[] data = Encoding.Default.GetBytes(msg); SendData(data); // 바이트배열을상대방에전송 else // 상대방과연결되어있지않다면 wnd.add_msg(" 메시지전송실패!"); wnd.add_ MSG(ex.Message); private void SendData(byte[] data) int offset = 0; int size = data.length; int left_data = size; int send_data = 0; 순천향대학교컴퓨터학부이상정 20 // 버퍼내위치 // 전송할바이트배열의크기 // 남은데이터량 // 전송된데이터크기
// 전송할실제데이터의크기전달 byte[] data_size = new byte[4]; // 정수형태로데이터크기전송 data_size = BitConverter.GetBytes(size); send_data data = client.send(data_size); // 실제데이터전송 while (offset < size) send_data = client.send(data, offset, left_data, SocketFlags.None); offset += send_data; left_data -= send_data; // 데이터전송중예외가발생하면에러메시지출력 wnd.add_msg(ex.message); // 상대방의데이터수신 public void Receive() // 상대방과연결되었다면 while (client!= null && client.connected) //ReceiveData 메서드를사용해바이트단위로데이터를읽어옵니다. byte[] data = ReceiveData(); wnd.add_msg("[ 상대방 ]" + Encoding.Default.GetString(data)); 순천향대학교컴퓨터학부이상정 21 wnd.add_msg(ex.message); private byte[] ReceiveData() int offset = 0; // 버퍼내위치 int size = 0; // 수신할데이터크기 int left_data = 0; // 남은데이터크기 int recv_data = 0; // 수신한데이터크기 // 수신할데이터크기알아내기 byte[] data_size = new byte[4]; recv_data = client.receive(data_size, 0, 4, SocketFlags.None); size = BitConverter.ToInt32(data_size, 0); left_ data = size; byte[] data = new byte[size]; // 바이트배열생성 // 서버에서전송한실제데이터수신 while (offset < size) // 상대방이전송한데이터를읽어옴 recv_data = client.receive(data, offset, left_data, SocketFlags.None); if (recv_data == 0) break; offset += recv_data; left_data -= recv_data; return data; 순천향대학교컴퓨터학부이상정 22
// 데이터수신중예외가발생하면에러메시지출력 wnd.add_msg(ex.message); return null; // IP 주소구하기 public string Get_MyIP() IPHostEn host = Dns.Resolve(Dns.GetHostName()); // 첫번째 ip 주소를사용 string myip = host.addresslist[0].tostring(); return myip; 순천향대학교컴퓨터학부이상정 23 과제 앞의채팅프로그램실행시루프백주소가아닌두개의 PC 사이에서채팅실습 순천향대학교컴퓨터학부이상정 24