`
茶杯里的台风
  • 浏览: 12451 次
  • 性别: Icon_minigender_2
文章分类
社区版块
存档分类
最新评论

通信初步

 
阅读更多

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();
			}		
		}

 

   在写代码时,一定要注意细节问题,可能一个小小的错误,就会影响到整个程序的运行。总之,就是要细心,细心,再细心!

1
1
分享到:
评论
1 楼 fxrz12 2013-08-05  
[在写代码时,一定要注意细节问题,可能一个小小的错误,就会影响到整个程序的运行。总之,就是要细心,细心,再细心!]
这句萌到我了!我怎么没想到。哈哈!

相关推荐

Global site tag (gtag.js) - Google Analytics