이전 :
지난 주말에 나는 내가 쓴 간단한 소켓 채팅 프로그램의 초기 디자인과 서버 측 세부 디자인을 녹음하는 데 시간이 걸렸습니다. 나는 마침내 화요일에 졸업하기 전에 취한 소프트 시험 인증서를 기다렸다가 초과 근무 날을 보냈습니다. 오늘은 금요일이되었으며, 이번 주말부터 다른 것들로 바쁠 것이기 때문에 고객과 공통 모듈의 세부적인 디자인을 녹음 할 계획이었습니다.
설계:
클라이언트 설계는 주로 소켓 통신 모듈 디자인과 UI 관련 설계와 같은 두 부분으로 나뉩니다.
클라이언트 소켓 통신 설계 :
여기의 디자인은 실제로 서버 설계와 유사합니다. 차이점은 서버가 하트 비트 패킷을 수신하고 클라이언트는 하트 비트 패킷을 보냅니다. 클라이언트는 하나의 서버 와만 통신하기 때문에 (클라이언트 간의 통신은 서버에서도 배포됩니다) 크기 2의 스레드 풀만이 두 가지를 처리하는 데 사용됩니다 (Newfixedthreadpool (2)). 해당 처리 클래스는 수신자 및 Keepalivedog입니다. 수신자가 초기화되면 클라이언트에 대한 콜백으로 콜백이 서버 메시지를 수신합니다. 콜백의 기본 구현은 DefaultCallback입니다. DefaultCallback은 다른 이벤트에 따라 HF를 통해 다른 핸들러에 배포됩니다. 고객 보유자는 현재 클라이언트 정보를 저장합니다. 디자인은 다음과 같습니다.
소켓 통신 모듈의 특정 구현 :
[client.java]
클라이언트는 서버에 연결하기 위해 클라이언트 입구입니다. 클라이언트를 만들려면 클라이언트가 서버 메시지를 수신 할 때 콜백으로 콜백을 지정해야합니다. 그런 다음 클라이언트의 start () 메소드는 서버 (수신자)의 청취를 시작합니다. 수신자가 서버에서 전송 된 데이터를 수신하면 콜백 (콜백) 메서드가 호출되어 처리됩니다. 동시에 클라이언트는 서버에 여전히 서버에 연결되어 있음을 알리기 위해 하트 비트 패킷을 보내야합니다. 하트 비트 패킷은 클라이언트가 보관합니다. Alive ()는 keepalivedog에 의해 시작되고 구현됩니다. 이 두 단계는 고정 크기 2 인 NewFixedThreadpool (2)의 스레드 풀에 의해 실행됩니다. 하트 비트 패키지가 정기적으로 전송되기 때문에 NewFixedThreadpool (1) 및 NewScheduledThreadpool (1)을 사용하는 것이 더 합리적 일 수 있습니다. 이는 서버가이를 구현하는 방법입니다 (이 후속 조정). 클라이언트의 특정 코드는 다음과 같습니다 (다른 두 가지 방법은 소켓과 현재 소켓이 속한 사용자를 얻기 위해 여기에 노출됩니다).
/** * 클라이언트 * @author yaolin */public class client {개인 최종 소켓 소켓; 개인 문자열; 개인 최종 ExecutorService 풀; 개인 최종 콜백 콜백; Public Client (Callback Callback) IoException {this.socket = new Socket (constantValue.server_ip, constantValue.server_port); this.pool = executors.newfixedthreadpool (2); this.callback = 콜백; } public void start () {pool.Execute (New recevenistener (소켓, 콜백)); } public void keepalive (string from) {this.from = from; pool.Execute (New Keepalivedog (소켓, From)); } public socket getSocket () {리턴 소켓; } public String getFrom () {return from; }}[Keepalivedog.java]
클라이언트가 서버와 연결을 설정 한 후 (이 프로그램은 로그인이 성공한 후에 클라이언트의 소켓이 서버의 양말에 의해 관리되기 때문에이 프로그램의 성공적인 로그인을 의미합니다), 서버와 접촉하지 않으면 서버가 소켓을 손상시키지 않도록 서버에 하트 비트 패킷을 서버로 보내야합니다 (서버는 서버의 블로그 참조). Keepalivedog의 코드는 다음과 같이 구현됩니다 (나중에 NewsCheduledthreadpool (1)로 조정될 수 있으므로 여기의 코드도 조정됩니다).
/*** Keepalivedog :이 클라이언트가 실행중인 서버에게 알려줍니다. * * @author yaolin */public class keepalivedog emplements runnable {개인 최종 소켓 소켓; 개인 최종 문자열; public keepalivedog (소켓 소켓, 문자열) {this.socket = 소켓; this.from = from; } @override public void run () {while (socket! = null &&! socket.isclosed ()) {try {printwriter out = new printwriter (socket.getoutPutStream ()); AliveMessage Message = New AliveMessage (); message.setfrom (from); out.println (json.tojson (메시지)); out.flush (); Thread.sleep (constantValue.keep_alive_period * 1000); } catch (예외 e) {loggerutil.error ( "클라이언트 전송 메시지 실패!" + e.getMessage (), e); }}}}[receivelistener.java]
클라이언트의 start () 메소드는 서버의 청취를 시작하고 수신자가 구현합니다. 서버에서 메시지를 수신 한 후 수신자는 콜백이 특정 비즈니스 로직을 처리 할 수 있도록 콜백의 Dowork () 메소드를 호출합니다. 따라서 수신자는 서버의 메시지를들을 책임이 있으며 특정 처리는 콜백에 의해 처리됩니다. 여기에서 메시지 유형이 파일 유형 인 경우 콜백의 Dowork가 다음 루프를 직접 입력하는 대신 서버로의 파일 흐름을 읽을 수 있도록 실행 간격을 구성하기 위해 잠을 자게됩니다. 여기의 디자인은 서버와 유사합니다. 수신자의 특정 구현 코드는 다음과 같습니다.
공개 클래스 수신자는 런닝 가능 {개인 최종 소켓 소켓; 개인 최종 콜백 콜백; 공개 수신자 (소켓 소켓, 콜백 콜백) {this.socket = 소켓; this.callback = 콜백; } @Override public void run () {if (socket! = null) {while (! socket.isclosed ()) {try {inputStream은 = socket.getInputStream (); 문자열 라인 = null; StringBuffer sb = null; if (is.available ()> 0) {bufferedReader bufr = new bufferedReader (new inputStreamReader (is)); sb = new StringBuffer (); while (is.aveailable ()> 0 && (line = bufr.readline ())! = null) {sb.append (line); } loggerutil.trach ( "rece [" + sb.toString () + "]에서" + new date ()); Callback.dowork (Socket, sb.tostring ()); BaseMessage 메시지 = json.parseobject (sb.toString (), BaseMessage.class); if (message.getType () == messagetype.file) {// 파일을 받기 위해 일시 중지로 loggerutil.trach ( "클라이언트 : 파일을 받기 위해 일시 중지"); thread.sleep (constantvalue.message_period); }} else {thread.sleep (constantValue.message_period); }} catch (예외 e) {loggerutil.error ( "클라이언트 전송 메시지 실패!" + e.getMessage (), e); }}}}}}}}[Callback.java, DefaultCallback.java]
위에서 볼 때 클라이언트의 메시지 처리가 콜백 콜백이며 콜백은 단지 인터페이스 일뿐입니다. 모든 콜백 구현은 필요에 따라 메시지를 처리하기 위해 인터페이스를 구현합니다. 여기서 콜백의 기본 구현은 DefaultCallback입니다. DefaultCallback은 세 가지 유형의 메시지, 즉 채팅 메시지, 파일 메시지 및 반환 메시지 만 처리합니다. 채팅 메시지의 경우 DefaultCallback은 UI의 라우터 경로를 통해 해당 인터페이스를 얻습니다 (자세한 내용은 아래 UI 디자인 참조). 그런 다음 해당 채팅 상자에 메시지를 표시합니다. 파일 메시지의 경우 DefaultCallback은 구성에 지정된 경로에 파일을 작성합니다 (파일은 여기에 사용자의 권한없이 수신됩니다.이 디자인은 매우 친숙하지 않으므로 지금은; 반환 메시지의 경우 반환 메시지의 키에 따라 DefaultCallback이 다른 핸들러에게 호출됩니다. 특정 코드는 다음과 같습니다.
공개 인터페이스 콜백 {public void dowork (소켓 서버, 객체 데이터); } 공개 클래스 DefaultCallback은 콜백을 구현 {@override public void dowork (소켓 서버, 객체 데이터) {if (data! = null) {Basemessage 메시지 = json.parseobject (data.toString (), basemessage.class); switch (message.gettype ()) {case messagetype.chat : handlechatmessage (data); 부서지다; case messagetype.file : handleFileMessage (서버, 데이터); 부서지다; Case Messagetype.return : HandlerEturnMessage (데이터); 부서지다; }}} private void handleChatMessage (객체 데이터) {chatmessage m = json.parseobject (data.tostring (), chatmessage.class); String tabkey = m.getfrom (); // jcomponent comp = router.getView (chatroomview.class) .getComponent (chatroomView.Catabbed); if (jtabbedpane) {jtabbedpane tab = (jtabbedpane) comp; int index = tab.indexoftab (tabkey); if (index == -1) {tab.addtab (tabkey, resultholder.get (tabkey) .getScrollPane ()); } jtextArea textArea = resultholder.get (tabkey) .getTextArea (); textRea.settext (new StringBuffer () .append (textArea.getText ()). Append (System.LineseParator ()). Append (System.LineseParator ()) .append ( "[")) .TOSTRING ()); // 하단으로 스크롤 TextRea.setCaretPosition (textArea.getText (). length ()); }} private void handleFileMessage (소켓 서버, 객체 데이터) {fileMessage message = json.parseobject (data.toString (), filemessage.class); if (message.getSize ()> 0) {outputStream os = null; try {if (server! = null) {inputStream은 = server.getInputStream (); file dir = 새 파일 (constantvalue.client_receive_dir); if (! dir.exists ()) {dir.mkdirs (); } os = new FileOutputStream (새 파일 (pathUtil.comBination (constantValue.client_receive_dir, new Date (). gettime () + message.getName ()))); int total = 0; while (! server.isclosed ()) {if (is.available ()> 0) {byte [] buff = new Byte [constantValue.buff_size]; int len = -1; while (is.aveailable ()> 0 && (len = is.read (buff))! = -1) {os.write (buff, 0, len); 총 += 렌; loggerutil.debug ( "수신 버프 [" + len + "]"); } os.flush (); if (total> = message.getsize ()) {loggerutil.info ( "수신 버프 [OK]"); 부서지다; }}}}}} catch (Exception e) {loggerutil.error ( "수신 파일 실패!" + e.getMessage (), e); } 마침내 {if (os! = null) {try {os.close (); } catch (예외 무시) {} os = null; }}}} private void handlerEturnMessage (객체 데이터) {returnMessage m = json.parseObject (data.toString (), returnMessage.class); if (stringUtil.isnotempty (m.getKey ())) {switch (m.getKey ()) {case key.notify : // 클라이언트를 USR 목록 hf.gethandler (key.notify) .handle (data)를 업데이트하도록 클라이언트에 알립니다. 부서지다; Case Key.login : hf.gethandler (key.login) .handle (데이터); 부서지다; Case Key.register : hf.gethandler (key.register) .handle (데이터); 부서지다; Case Key.Listuser : HF.Gethandler (key.listuser) .handle (데이터); 부서지다; case key.tip : hf.gethandler (key.tip) .handle (data); 부서지다; }}}}[handler.java, hf.java, listuserhdl.java ...]
핸들러 구성 요소는 서버의 반환 메시지 유형의 메시지를 처리 할 책임이 있습니다. DefaultCallback은 다른 키에 따라 메시지를 다른 핸들러에 배포합니다. 이것은 또한 간단한 공장 구성 요소입니다. 서버가받은 데이터와 유사합니다. 전체 클래스 다이어그램은 다음과 같습니다.
이 섹션의 코드는 다음과 같습니다. 공간을 줄이기 위해 핸들러가 구현 한 모든 코드가 수집됩니다.
공개 인터페이스 핸들러 {public 객체 핸들 (Object OBJ); } 공개 클래스 hf {public static handler gethandler (문자열 키) {switch (key) {case key.notify : return new notifyhdl (); Case Key.login : return new loginhdl (); Case Key.register : return new RegisterHDL (); Case Key.listuser : 새로운 ListUserHDL ()을 반환합니다. CASE KEY.TIP : return new tiphdl (); } return null; }} 공개 클래스 listuserhdl handler {@override public 객체 핸들 (Object obj) {if (obj! = null) {try {retrymessage rm = json.parseobject (obj.toString (), returnMessage.class); if (rm.issuccess () && rm.getContent ()! = null) {clientListUserDto dto = json.parseObject (rm.getContent (). toString (), clientListUserDTO.class); jcomponent comp = router.getView (chatroomview.class) .getComponent (chatroomview.listusrlist); if (comp instanceof jlist) {@suppresswarnings ( "선택 취소") // jlist <string> listusrlist = (jlist <string>) comp; List <string> listUser = new LinkedList <string> (); ListUser.addall (dto.getListuser ()); Collections.SORT (ListUser); listuser.add (0, constantValue.to_all); listusrlist.setListData (listUser.toArray (new String [] {})); }}} catch (Exception e) {loggerutil.error ( "핸들 러스트 실패!" + e.getMessage (), e); }} return null; }} 공개 클래스 loginhdl handlements handler {@override public object handle (object obj) {if (obj! = null) {try {retrymessage rm = json.perseobject (obj.toString (), returnMessage.class); if (rm.issuccess ()) {router.getView (registerAndLoginView.class) .trash (); router.getView (chatroomview.class) .create (). display (); clientholder.getClient (). rectalive (rm.getto ()); // recoy ...} else {컨테이너 컨테이너 = router.getView (registerandLoginView.class) .container (); if (container! = null) {// 오류 표시 joptionpane.showmessagedialog (컨테이너, rm.getMessage ()); }}} catch (예외 e) {loggerutil.error ( "핸들 로그인 실패!" + e.getMessage (), e); }} return null; }} 공개 클래스 notifyhdl handles handler {@override public 객체 핸들 (Object obj) {if (obj! = null) {try {retrymessage rm = json.pseobject (obj.toString (), returnMessage.class); if (rm.issuccess () && rm.getContent ()! = null) {clientNotifyDto dto = json.parseObject (rm.getContent (). toString (), clientNotifyDto.class); jcomponent comp = router.getView (chatroomview.class) .getComponent (chatroomview.listusrlist); if (comp instanceof jlist) {@suppresswarnings ( "선택 취소") // jlist <string> listusrlist = (jlist <string>) comp; list <string> listuser = modeltolist (listusrlist.getModel ()); if (dto.isflag ()) {if (! listuser.contains (dto.getusername ())) {listuser.add (dto.getusername ()); ListUser.remove (constantValue.to_all); Collections.SORT (ListUser); listuser.add (0, constantValue.to_all); }} else {listuser.remove (dto.getUername ()); } listusrlist.setListData (listUser.toArray (new String [] {})); }}} catch (예외 e) {loggerutil.error ( "nofity 실패!" + e.getMessage (), e); }} return null; } private list <string> modeltolist (listmodel <string> listmodel) {list <string> list = new LinkedList <string> (); if (listModel! = null) {for (int i = 0; i <listModel.getSize (); i ++) {list.add (listModel.getElementat (i)); }} 리턴 목록; }} public class registerhdl handlements handler {@override public 객체 핸들 (Object obj) {if (obj! = null) {try {retrymessage rm = json.pseobject (obj.toString (), returnMessage.class); 컨테이너 컨테이너 = router.getView (RegisterAndLoginView.class) .container (); if (container! = null) {if (rm.issuccess ()) {joptionpane.showmessagedialog (컨테이너, rm.getContent ()); } else {joptionpane.showmessagedialog (컨테이너, rm.getMessage ()); }}} catch (예외 e) {loggerutil.error ( "핸들 레지스터 실패!" + e.getMessage (), e); }} return null; }} 공개 클래스 tiphdl handles handler {@override public Object Handle (object obj) {if (obj! = null) {try {retrymessage m = json.pseobject (obj.toString (), returnMessage.class); if (m.issuccess () && m.getContent ()! = null) {String tabkey = m.getfrom (); 문자열 tip = m.getContent (). toString (); jcomponent comp = router.getView (chatroomview.class) .getComponent (chatroomView.Catabbed); if (jtabbedpane) {jtabbedpane tab = (jtabbedpane) comp; int index = tab.indexoftab (tabkey); if (index == -1) {tab.addtab (tabkey, resultholder.get (tabkey) .getScrollpane ()); } jtextArea textArea = resultholder.get (tabkey) .getTextArea (); textArea.settext (new StringBuffer () .append (textArea.getText ()). Append (System.LineseParator ()). Append (System.LineseParator ()) .Append ( "[")) // 하단으로 스크롤 TextRea.setCaretPosition (textArea.getText (). length ()); }}} catch (예외 e) {loggerutil.error ( "핸들 팁 실패!" + e.getMessage (), e); }} return null; }} 소켓 통신 모듈의 또 다른 클래스, 즉 클라이언트 홀더는 서버의 Socketholder와 유사한 현재 클라이언트를 저장하는 데 사용됩니다.
/** * @author yaolin */public class clientholder {public static client client; 공개 정적 클라이언트 getClient () {return Client; } public static void setclient (클라이언트 클라이언트) {clientholder.client = 클라이언트; }}UI 모듈의 특정 구현 :
위의 내용은 소켓 통신 모듈의 설계를 기록합니다. 다음으로 UI의 디자인 모듈을 기록합니다. 나는 혼자서 UI를 쓸 계획이 없다. 결국, 글은 너무 못 생겼으므로 반 친구 나 친구들에게 나중에 노크하는 데 도움이 될 수 있습니다. 따라서 UI 이벤트 처리를 수행하여 처리하여 UI 디자인 및 이벤트 응답을 분리합니다. 모든 UI는 JFrame을 상속하고보기 인터페이스를 구현합니다. 위의 핸들러 구현 클래스는 라우터를 통해 얻어집니다 (존재하는 경우 직접 반환되며 존재하지 않으면 생성 및 저장됩니다). 보기는 UI Creation ()을 제공하고, 컨테이너 ()를 얻고 UI getComponent (), display ()에서 구성 요소를 얻고 휴지통 ()을 재활용합니다. ResultWrapper 및 Resultholder는 채팅 탭을 작성하고 저장하기위한 것입니다. 디자인은 다음과 같습니다.
[router.java, view.java]
모든 UI는 JFrame을 상속하고보기 인터페이스를 구현합니다. 핸들러 구현 클래스는 라우터를 통해 지정된 UI를 얻습니다 (존재하는 경우 직접 반환하고 존재하지 않으면 생성 및 저장). 보기는 UI Creation ()을 제공하고 컨테이너 ()를 얻고 UI getComponent ()에서 구성 요소를 얻고 display () 및 휴지통 ()을 재활용합니다. 특정 구현은 다음과 같습니다.
/*** 경로보기* @Author yaolin*/public class router {private static map <string, view> listroute = new Hashmap <String, view> (); public static view getView (class <?> clazz) {view v = listroute.get (clazz.getName ()); if (v == null) {try {v = (view) class.forname (clazz.getName ()). newInstance (); listroute.put (clazz.getname (), v); } catch (예외 e) {loggerutil.error ( "보기 실패!" + e.getMessage (), e); }} return v; }} /** * 모든 인터페이스에 대한 표준 인터페이스 * @author yaolin */public interface view {/** */public view create (); / ** * */ public 컨테이너 컨테이너 (); / ** * @param 키 */ public jcomponent getComponent (문자열 키); / ** * */ public void display (); / ** * */ public void 쓰레기 (); }[registerandloginview.java, chatroomview.java]
직접 UI를 쓰고 싶지 않기 때문에 여기에 두 개의 UI 인터페이스, 즉 등록 및 로그인 인터페이스 및 채팅 인터페이스를 작성했습니다. 다음은 두 가지 추악한 인터페이스입니다.
로그인 인터페이스를 등록하십시오
채팅 인터페이스
다음은이 두 인터페이스의 특정 코드입니다.
/*** 등록 및 로그인* @Author yaolin*/public class registerandLoginView는 JFrame Ampess view {private static final long serialversionuid = 632208074312546736L; 개인 최종 최종 레지스터 andloginaction Action = New RegisterandLoginaction (); 개인 정적 부울 생성 = 거짓; @override public view create () {if (! create) {init (); 생성 = true; } 이것을 반환하십시오. } public 컨테이너 컨테이너 () {create (); return getContentPane (); } @override public jcomponent getComponent (String key) {return null; } @override public void display () {setVisible (true); } @override public void trash () {dispose (); } private void init () {// 속성 setSize (500, 300); setResizable (false); setLocationRelativeto (null); // 컨테이너 jpanel 패널 = new jpanel (); PALEN.SETLAYOUT (NULL); // 구성 요소 // username jlabel lbusername = new jlabel (i18n.text_username); lbusername.setBounds (100, 80, 200, 30); 최종 Jtextfield tfusername = New Jtextfield (); tfusername.setBounds (150, 80, 230, 30); PALEN.ADD (LBUSERNAME); PANIL.ADD (TFUSERNAME); // 비밀번호 jlabel lbpassword = new Jlabel (i18n.text_password); lbpassword.setbounds (100, 120, 200, 30); 최종 JPASSWORDFIELD PFPASSWORD = NEW JPASSWORDFIELD (); pfpassword.setbounds (150, 120, 230, 30); PALEN.ADD (lbpassword); PALEN.ADD (PFPASSWORD); // btnregister jbutton btnregister = new Jbutton (i18n.btn_register); btnregister.setbounds (100, 175, 80, 30); // btnlogin 최종 Jbutton btnlogin = new Jbutton (i18n.btn_login); btnlogin.setbounds (200, 175, 80, 30); // btncancel Jbutton btnexit = new Jbutton (i18n.btn_exit); btnexit.setbounds (300, 175, 80, 30); PALEN.ADD (BTNREGISTER); PALEN.ADD (BTNLOGIN); PALEN.ADD (BTNEXIT); // event pfpassword.addkeylistener (new keyadapter () {public void keypressed (final keyEvent e) {if (e.getKeyCode () == keyEvent.vk_enter) btnlogin.doclick ();}}); // public vtneRgenter.addactionlister (new actionlistner) ActionPerformed (Final ActionEvent e) {if (stringUtil.isempty (tfusername.getText ()) || stringUtil.isempty (joptionpane.showmessaged pane (), i18n. action.handleregister (tfusername.getText (), new String (pfpassword.getPassword ())); // addActionListener Btnlogin.addactionListener () {public actionPerformed (final actionEvent e) {if (stringUtil.isempty (tfusername)) stringUtil.isempty (pfpassword.getPassword ())) {joptionpane.showmessagegedialog (getContentPane () addActionListener btnexit.addactionListener (new ActionListener () {public void actionPerformed (Final ActionEvent e) {System.Exit (0); }}); // addActionListener getContentPane (). add (패널); setDefaultCloseOperation (jframe.exit_on_close); }} /** * 클라이언트 채팅 창 * * @Author yaolin */public class chatroomview jframe emplements view {private static final long serialversionuid = -451583117289054818L; 공개 정적 최종 문자열 listusrlist = "listusrlist"; 공개 정적 최종 문자열 chattabbed = "chattabbed"; 개인 정적 부울 생성 = 거짓; 개인 채팅 룸 액션 액션 = 새로운 chatroomaction (); 개인 jlist <string> listusrlist = null; 개인 jtabbedpane chattabbed = null; @override public view create () {if (! create) {init (); 생성 = true; } 이것을 반환하십시오. } public 컨테이너 컨테이너 () {create (); return getContentPane (); } @override public jcomponent getComponent (String key) {create (); 스위치 (키) {case listusrlist : return listusrlist; 사례 chattabbed : return chattabbed; } return null; } @override public void display () {setVisible (true); } @override public void trash () {dispose (); } public void init () {settitle (i18n.text_app_name); setSize (800, 600); setResizable (false); setLocationRelativeto (null); setLayout (New BorderLayout ()); 추가 (CreateChatpanel (), BorderLayout.Center); add (createUsrListView (), BorderLayout.East); setDefaultCloseOperation (jframe.exit_on_close); } private jcomponent createChatpanel () {// 파일 선택기 최종 JFILECHOOSER FILECHOOSER = new JFILECHOOSER (); JPANEL 패널 = New JPanel (New BorderLayout ()); // 센터 chattabbed = new jtabbedpane (); chattabbed.addtab (constantvalue.to_all, resultholder.get (constantValue.to_all) .getScrollPane ()); PALEN.ADD (chattabbed, BorderLayout.Center); // South JPanel South = New JPanel (New BorderLayout ()); // South- 파일 JPANEL MIDDLE = NEW JPANEL (New BorderLayout ()); middle.add (New Jlabel (), BorderLayout.Center); // 패딩 만용 jbutton btnupload = new Jbutton (i18n.btn_send_file); middle.add (btnupload, borderlayout.east); South.add (중간, BorderLayout.north); // South -TextArea Final JtextArea Tasend = New JtextArea (); tasend.setcaretcolor (color.blue); tasend.setmargin (새로운 삽입 (10, 10, 10, 10)); tasend.setrows (10); South.add (Tasend, BorderLayout.Center); // South -BTN JPANEL 바닥 = New JPANEL (New BorderLayout ()); bottom.add (new jlabel (), BorderLayout.Center); // 패딩 용 jbutton btnsend = new Jbutton (i18n.btn_send); bottom.add (btnsend, borderlayout.east); South.add (하단, BorderLayout.South); btnupload.addactionListener (new ActionListener () {public void actionPerformed (final actionEvent e) {if (! constantValue.to_all.equals (chattAbbed.getTitLeat (chattAbbed.getStectedIndex ()))) {int returnVal = filechooser.showopendialog (chat roomview.f (chat roomview)); jfileChooser.Approve_Option) {file = file = filechooser.getSelectedFile (); }}}}); btnsend.addactionListener (new ActionListener () {public void actionPerformed (Final ActionEvent e) {if (stringUtil.isnotempty (tasend.getText ())) {action.Send (chattAbbed.getTitLeat (chattAbbed.getSelectedEdex ()); tastEdt ()) }}); PALEN.ADD (South, BorderLayout.South); 리턴 패널; } private jcomponent createUrListView () {listUsrList = new Jlist <string> (); listusrlist.setborder (New Lineborder (color.blue)); listusrlist.setListData (new String [] {constantValue.to_all}); listusrlist.setfixedCellWidth (200); listusrlist.setfixedCellHeight (30); listusrlist.addlistselectionListener (new listSelectionListener () {@Override public void valueChanged (listSelectionEvent e) {// 채팅 if (chattAbbed.indexOftab (listUSrlist.getSteltSteSrlist.getSel) == -1 && listUSRList.getSelectedValue ()! = null && listusrlist.getSelectedValue (). Equals (clientHolder.getClient (). getFrom ())) {chattabbed.addtab (listusrlist.getSelectedValue (), resultholder.get (listusrlist.getSelectedValue ()); chattabbed.setselectedIndex (chattabbed.indexoftab (listusrlist.getSelectedValue ())); ListUsrlist를 반환합니다. }}[registerandloginaction.java, chatroomaction.java]
여기서 UI 이벤트 처리는 동작으로 처리되며, 이는 단순히 UI 설계 및 이벤트 응답을 분리합니다. RegisterAndLoginView의 이벤트는 RegisterAndLoginaction에 의해 처리되며 ChatroomView의 이벤트는 Chatroomaction에 의해 처리됩니다. 특정 구현은 다음과 같습니다.
public class registerandloginaction {public void handleregister (문자열 사용자 이름, 문자열 암호) {if (stringUtil.isempty (username) || stringUtil.isempty (password)) {return; } registermessage messag message.setfrom (username); sendHelper.Send (clientholder.getClient (). getSocket (), 메시지); } public void handlelogin (문자열 사용자 이름, 문자열 비밀번호) {if (stringUtil.isempty (username) || stringUtil.isempty (password)) {return; } loginMessage messag message.setfrom (username); sendHelper.Send (clientholder.getClient (). getSocket (), 메시지); }} UI 디자인을위한 다른 두 가지 클래스, 즉 Resultholder 및 resultWrapper가 있습니다. ResultWrapper 및 Resultholder는 채팅 탭을 작성하고 저장하기위한 것입니다. 특정 구현은 다음과 같습니다.
공개 클래스 resultwrapper {private jscrollpane scrollpane; 개인 jtextArea textRea; public resultwrapper (jscrollpane scrollpane, jtextArea textarea) {this.scrollpane = scrollpane; this.textArea = textRea; } public jscrollpane getScrollpane () {return scrollpane; } public void setscrollpane (jscrollpane scrollpane) {this.scrollpane = scrollpane; } public jtextArea getTextArea () {return TextArea; } public void setTextArea (jtextArea textRea) {this.textArea = textRea; }} 공개 클래스 resultholder {private static map <string, resultwrapper> listresultWrapper = new Hashmap <String, resultwrapper> (); public static void put (문자열 키, resultwrapper 래퍼) {listresultWrapper.put (키, 래퍼); } public static resultwrapper get (string key) {resultwrapper wrapper = listresultWrapper.get (키); if (wrapper == null) {wrapper = create (); put (키, 래퍼); } 리턴 래퍼; } private static resultWrapper create () {jtextArea resultTextArea = new JtextArea (); resultTextArea.setEdable (false); resultTextArea.setBorder (New Lineborder (Color.Blue)); jscrollpane scrollpane = 새로운 jscrollpane (resulttextArea); scrollpane.sethorizontalscrollbarpolicy (scrollpaneconstants.horizontal_scrollbar_never); scrollpane.setverticalscrollbarpolicy (scrollpaneconstants.vertical_scrollbar_as_seeded); resultwrapper rapper = new resultWrapper (ScrollPane, resultTextArea); 리턴 래퍼; }} 마지막은 고객이 실행하는 항목이 제공됩니다.
/** * * @author yaolin * */public class niloaychat {public static void main (string [] args) {view v = router.getView (registerandLoginView.class) .create (); {v.display (); 클라이언트 클라이언트 = 새 클라이언트 (new defaultCallback ()); client.start (); clientholder.setclient (클라이언트); } catch (ioexception e) {joptionpane.showmessagedialog (v.container (), e.getMessage ()); }}} 데모 다운로드 주소 : 데모
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.