20130726 通信初步——群聊聊天室的创建
最近一直在写通信部分,虽然还有好多功能没有实现,但自我安慰了一下,起码有一部分功能还是实现了的。
也许是太久没有用过脑子了,感觉脑子有点不灵光,反应有点慢,不过,我会尽量跟上大家的脚步的。
下面是我做出来的一个简单的群聊聊天室:
1、首先,我先做了一个服务器的界面,通过在界面上添加按钮的过程,清楚这个聊天室需要有一些什么样的功能。其中,画图功能还没有实现,目前正在写这一部分。
package dyh20130726Frame; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; public class serverFrame extends JFrame { private static final long serialVersionUID = 1L; public static void main(String[] args) { //实例化一个类的对象 serverFrame sFrame = new serverFrame(); sFrame.initGUI(); } public void initGUI(){ //设置窗体的属性 this.setTitle("服务器"); this.setSize(600, 600); this.setDefaultCloseOperation(3); this.setLayout(new FlowLayout()); JLabel jla1 = new JLabel("端口号"); JTextField jtf1 = new JTextField(5); JButton jbu1 = new JButton("确定"); JLabel jla2 = new JLabel("消息栏"); JTextField jtf2 = new JTextField(10); JButton jbu2 = new JButton("发送"); JPanel jpa2 = new JPanel(); jpa2.setPreferredSize(new Dimension(550,290)); jpa2.setBackground(Color.GRAY); //设置面板上的按钮 JButton jbu3 = new JButton("画线"); JButton jbu4 = new JButton("画圆"); //将标签添加到面板上 jpa2.add(jbu3); jpa2.add(jbu4); JTextArea sjte = new JTextArea(); sjte.setPreferredSize(new Dimension(550,200)); // System.out.println("显示框的大小变化了"); this.add(jla1); this.add(jtf1); this.add(jbu1); this.add(jla2); this.add(jtf2); this.add(jbu2); this.add(sjte); this.add(jpa2); this.setVisible(true); //创建监听器的对象 serverListener ac = new serverListener(jtf1, jtf2, sjte); //将监听器添加到事件源上 jbu1.addActionListener(ac); jbu2.addActionListener(ac); } }
添加按钮时要注意的是,要写明每一个按钮的作用,否则,到后来进行调用传参时,有可能会弄混淆。
2、界面完成后,接下来就是要实现每个按钮的功能了。先是“确定”按钮:确定按钮的功能是,一旦点击“确定”按钮,即在指定端口上创建一个服务器。
String str = e.getActionCommand(); if (str.equals("确定")){ int port = Integer.parseInt(jtf1.getText()); try { //创建一个指定端口上的服务器 ServerSocket server = new ServerSocket(port); System.out.println("服务器创建成功!"); //创建一个线程对象 serverThread sThread = new serverThread(server, sjte); sThread.start(); System.out.println("服务器线程启动了!"); } catch (IOException e1) { e1.printStackTrace(); } }
3、服务器创建成功后,要让服务器处于等待状态,以等待客户机来连接。但这一等待过程会发生阻塞,假如一直没有客户机来连接,程序就会卡在发生阻塞的位置,无法就行下去,因此,要将这一部分写在一个线程中,再对线程进行调用,这样就可以避免这种情况的发生。一下是服务器端的第一个线程:
/** * 服务器进入阻塞状态,等待客户机来连 * @param sjte */ public void waiting(JTextArea sjte){ while(true){ Socket socket; try { //让服务器进入阻塞状态 socket = server.accept(); System.out.println("进入了一个客户机连接:"+socket.getRemoteSocketAddress().toString()); //创建一个线程对象 serverThread2 ct = new serverThread2(socket, sjte); ct.start(); serverList.add(ct); System.out.println("启动线程处理连接对象成功"); } catch (IOException e) { e.printStackTrace(); } } }
4、客户机与服务器连接成功后,服务器就要从客户机端读取消息,这一过程中,服务器依然充当了一个被动的角色。假如客户机与服务器连接成功,但客户机一直没有给服务器发送消息,服务器依然会处在阻塞状态,为解决这一问题,我将服务器读取客户机发来的消息的方法,写在了另一线程中。以下就是服务器端的第二个线程:
/** * 客户机与服务器连接成功后,服务器循环读取客户机发来的内容, * 并将其接收到的内容发送给客户机 */ public void work(){ try { //得到一个输入输出流对象 ous = client.getOutputStream(); ins = client.getInputStream(); //表明客户端与服务器连接成功 String s = "欢迎进入聊天室!"+"\r\n"; //用输出对象发送数据 ous.write(s.getBytes()); //强制输出 ous.flush(); //调用读取字符串的方法,读取客户机发来的信息 String inputs = getString(ins); while(!inputs.equals("exit")){ System.out.println("服务器接受的内容是:\r\n"+inputs); sjte.append("客户机发来消息:"+inputs+"\r\n"); String str = "客户端输入的内容是:\r\n"+inputs+"\r\n"; serverList.sendMas(str); //客户机下一次输入 inputs = getString(ins); } //客户机输入内容为“exit”时进行该操作 String str2 = "再见!\r\n"; serverList.sendMas(str2); client.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 服务器读取从客户机发来的消息的方法 * @param ins 输入流 * @return 客户机发送给服务器的字符串 */ public String getString(InputStream ins){ //创建一个字符串缓冲区 StringBuffer strMas = new StringBuffer(); int nRead = 0; while (nRead!= 13){ try { nRead = ins.read(); //将字节添加到字符串中 strMas.append((char)nRead); } catch (IOException e) { e.printStackTrace(); } } //将读到的字节组转为字符串,并调用trim去掉尾部的空格 String inputs = strMas.toString().trim(); System.out.println("得到的字符串是:"+inputs); return inputs; }
5、群聊,顾名思义,就是有很多客户端进行聊天,这样服务器就要将它读到的每一个客户端发来的消息,再发送给每一个客户端。这样,我用一个队列来存储每一个客户端的消息,而客户端一旦与服务器相连,就会启动一个线程,线程中有读取消息的方法,因此,队列中实质存储的内容是线程。一旦启动一个线程,就将其添加到队列中,然后通过循环遍历队列,将队列中的内容发送给每一个客户端。以下是服务器端的队列:
//创建一个队列,用于存储线程 private static List<serverThread2> ctList = new ArrayList<serverThread2>(); //不能在其他类中再创建该对象 // private serverList(){} //向队列中添加线程对象的方法 public static void add(serverThread2 ctThread){ ctList.add(ctThread); } //遍历队列,将队列中存储的线程对象还给线程,并将信息发送出去 public static void sendMas(String mas2){ for(int i=0; i<ctList.size();i++){ serverThread2 ct = ctList.get(i); ct.writeMas(mas2+"\r\n"); System.out.println("服务器发送消息:"+mas2); } }
6、服务器暂时告一段落,下面就是客户端的内容。
首先仍然是要有一个界面,这一部分与服务器端相类似,在此处就不再赘述。
7、客户机端要实现的功能有连接服务器、发送消息、接收(读取)消息。
以下是客户端连接服务器及接收服务器发来的消息的部分:
String str = e.getActionCommand(); if (str.equals("连接")){ int port = Integer.parseInt(jtf1.getText());//端口号 String string = jtf2.getText();//IP try { //创建一个连接到服务器端的Socket对象 Socket socket = new Socket(string,port); System.out.println("客户端与服务器连接成功!"); //得到输入输出流对象 ins = socket.getInputStream(); ous = socket.getOutputStream(); //读取一行字符串 brd = new BufferedReader((new InputStreamReader(ins))); //实例化一个客户端线程对象 clientThread cThread = new clientThread( ous, ins, brd, jte); cThread.start(); } catch (Exception e1) { e1.printStackTrace(); } }
if(str.equals("发送")){ String string2 = jtf3.getText();//获取消息栏中的内容 sendMsg(string2); } } /** * 客户端发送信息的方法 */ public void sendMsg(String msg){ try { msg+="\r\n"; ous.write(msg.getBytes()); ous.flush(); } catch (IOException e) { e.printStackTrace(); } }
由于客户机在等待服务器发来消息时会发生阻塞,因为客户机并不知道服务器什么时候才会发来消息,在等待的过程中就会发生阻塞,因此,客户机读取服务器发送来的消息的方法要写在一个线程中,然后对线程进行调用。一下是客户机端读取消息的线程:
/** * 从服务器中读取消息,这个方法会阻塞,必须在独立线程中 */ public void readFromServer(){ String inputs; try { BufferedReader brd = new BufferedReader(new InputStreamReader(ins)); //逐行读取服务器中发来的消息 inputs = brd.readLine(); System.out.println("服务器中发来的inputs为:"+inputs); while (!inputs.equals("exit")){ System.out.println("服务器发来消息:"+inputs); jte.append("服务器发来消息:"+inputs+"\r\n"); inputs = brd.readLine(); } } catch (IOException e) { e.printStackTrace(); } }
在写代码时,一定要注意细节问题,可能一个小小的错误,就会影响到整个程序的运行。总之,就是要细心,细心,再细心!
相关推荐
扩频移动通信初步方针: 软件: system view 时间:2009/10/01
第六章UNIX的网络通信初步.ppt
通信管道初步设计入门,请仔细阅读方可成功。
通信施工组织设计-通信管道工程初步施工组织设计
关于移动通信初步入学者了解移动通信的概述,有关2G,3G的区别与联系
介绍了PC机与8051单片机的串口通信的初步知识,比较适合通信的初学者。
110kV配套光缆通信工程初步的设计说明.doc110kV配套光缆通信工程初步的设计说明.doc110kV配套光缆通信工程初步的设计说明.doc110kV配套光缆通信工程初步的设计说明.doc110kV配套光缆通信工程初步的设计说明.doc
110kV配套光缆通信工程初步的设计说明.docx110kV配套光缆通信工程初步的设计说明.docx110kV配套光缆通信工程初步的设计说明.docx110kV配套光缆通信工程初步的设计说明.docx110kV配套光缆通信工程初步的设计说明.docx
电力数据通信网络工程初步设计内容深度规定 电力调度通信总机技术要求 电力调度通信总机技术要求 电力通信运行管理规程 电力线载波通信运行管理规程 电力系统光纤通信运行管理规程 电力线载波通信设计技术规程 电力...
变电所通信电源改造初步设计.pdf
通信原理实验(matlab)-本科生-北京航空航天大学-实验1:通信原理初步
通信管道工程初步施工组织设计.doc
通信管道工程初步施工组织方案.doc
上位机与下位机通信的设计初步 讲述c8051单片机的上位机与下位机通信的设计 了解其中的方法与步骤
宽带无线光网络通信技术初步研究 (1).pdf
地区通信系统动力环境监控初步设计样本.doc
在本篇中主要介绍利用C#实现语音通信的基本方法。但是目前只实现了网络上语音传输的基本功能,而且比较粗糙,没有采用什么算法来优化,所以大家千万不要期望过高。我写这篇的目的除了记录自己的经历之外,更希望有...