QQ-系统在线用户改变时,更新客户端列表

QQProtocal.java

package com.InetAddress;

public interface QQProtocal {
	
		String UserName = "§я";
		String UserList = "ζω";
}


 

FirstServer.java

package com.InetAddress;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class FirstServer {
	//线程安全Set集合
	public static Map<Socket,String> clients = Collections.synchronizedMap(
			new HashMap<Socket,String>());

	public static void main(String[] args) throws IOException {
		//ServerSocket只负责接收连接
		ServerSocket ss = new ServerSocket(9999);
		System.out.println("服务器正在等待连接:");
		while(true){
			//Socket会阻塞线程
			Socket s = ss.accept();
		
			new ServerThread(s).start();
	   }
	}
 }

class ServerThread extends Thread{
	
	private Socket socket;
	public ServerThread(Socket socket){
		this.socket = socket;
	}
	public void run(){

		/*
		 * 从socket里面读,然后打印到控制台
		 */
		try{
			//使用BufferedReader的好处是可以一次读一行
			BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			
		    String line = null;
		
			while((line = br.readLine())!=null){
				//读到的是用户名,外面包着自定义的协议。
				if(line.startsWith(QQProtocal.UserName) && line.endsWith(QQProtocal.UserName)){
					
					//把读取的包装好的用户名拆分出真正的用户名
					String UserName = line.substring(QQProtocal.UserName.length(),
							line.length()-QQProtocal.UserName.length());
				//	System.out.println(line.length());
				//	System.out.println(QQProtocal.UserName.length());
				//	System.out.println(line.length()-QQProtocal.UserName.length());
					System.out.println(UserName);
					//每当有一个客户端连接就把该客户端的socket加入到Map集合中
					FirstServer.clients.put(socket,UserName);
					
					System.out.print("已连接");	
					System.out.print(FirstServer.clients.values());
					System.out.println("客户端数量:"+FirstServer.clients.size());
					
					//发送所有客户端的用户名
					BroadCast(QQProtocal.UserList+FirstServer.clients.values()+QQProtocal.UserList);
				}
				//读到的是普通的聊天信息
				else{
					BroadCast(line);
				}
				
			}//while
		}catch(SocketException sex){
			//把退出的客户端的socket从集合中删除
			//因为此处是以socket来删除,所以Map里就用socket作为键值对的键
			FirstServer.clients.remove(socket);
			BroadCast(QQProtocal.UserList+FirstServer.clients.values()+QQProtocal.UserList);
		}catch(Exception ex){
			ex.printStackTrace();
		}
		
	}//run()
	
	
	//服务器端每次修改了客户端(remove())之后,要把信息返回给客户端
	//用来广播消息
	private void BroadCast(String info){
		//for(Socket s : FirstServer.clients){	
		//用迭代器来遍历HashMap集合,clients.keySet()取到Map里的键后再迭代
		for(Iterator<Socket> it = FirstServer.clients.keySet().iterator();it.hasNext(); ){
			Socket s = it.next();
			try{
					//往socket里面写,println()一次就会清空一次
					PrintStream ps = new PrintStream(s.getOutputStream());
					System.out.println(info);//打印到控制台
					ps.println(info);//输出到各客户端的socket,就相当于打印到客户端控制台上		
			}catch(SocketException sex){
				//此时捕获到的异常即是客户端断开连接时会产生的SocketException,要把这个客户端从set集合中删除
			    //此处可以不做任何处理,只要在最后捕获SocketException就可以了
				FirstServer.clients.remove(s);
				//sex.printStackTrace();
			}catch(Exception ex){
				ex.printStackTrace();
			}
		}//for
	}
	
	
 }//Thread


	


 

FirstClient.java

package com.InetAddress;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class FirstClient {
	private JFrame mainwin = new JFrame("聊天窗口");
	
	private JTextArea display = new JTextArea(15,40);
	private JTextField input = new JTextField(40);
	JButton send = new JButton("发送");
	
	//DefaultListModel用来在用户列表中添加用户
	private DefaultListModel listmodel = new DefaultListModel();
	private JList list = new JList(listmodel);//用户列表
	
	private JCheckBox isprivate = new JCheckBox("私聊");
	
	public void init() throws UnknownHostException, IOException{
		Socket s = new Socket("127.0.0.1",9999);//创建连接
		
		final PrintStream socketOut = new PrintStream(s.getOutputStream());	
		
		//获取用户输入的用户名
		String tip = "";
		while(true){
			String UserName  = JOptionPane.showInputDialog(mainwin, tip+"请您输入用户名", 
					"输入用户名",JOptionPane.QUESTION_MESSAGE);
			if(UserName == null || UserName.trim().equals("")){
				tip = "您没有填写用户名,";
				continue;
			}
			//把用户名发送给服务器,用户名前后各加两个特殊的字符,用以判断是用户名,而不是普通消息
			//往socket里写
			socketOut.println(QQProtocal.UserName + UserName + QQProtocal.UserName);
		//	System.out.println(QQProtocal.UserName + UserName + QQProtocal.UserName);
			break;
		}
		
		
		display.setEditable(false);//设置永不不允许编辑文本域部分
		
		mainwin.add(new JScrollPane(display));
	
		JPanel bottompanel = new JPanel();//默认流式布局
		bottompanel.add(input);//加滚动条
		bottompanel.add(send);
		mainwin.add(bottompanel,BorderLayout.SOUTH);
		
		JPanel rightPanel = new JPanel();
		rightPanel.setLayout(new BorderLayout());
		list.setFixedCellWidth(60);
		rightPanel.add(new JScrollPane(list));//加滚动条
		rightPanel.add(isprivate,BorderLayout.SOUTH);
		mainwin.add(rightPanel,BorderLayout.EAST);
		
		mainwin.pack();
		mainwin.setVisible(true);
		mainwin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		
		
		new ClientThread(s).start();
		//局部变量在内部类中访问要加final
	
		//给send按钮加监听器
		send.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {
				//获得用户输入的内容,trim()去掉前后空格
				String inputContent = input.getText().trim();
				if(inputContent.length()>0){
					//把input中的内容发送到socket里
					socketOut.println(inputContent);
					//发送信息发送完以后应该清空文本框
					input.setText("");
				}
				
			}
		} );//send
		
		//给input按钮加监听器
		input.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {
				//获得用户输入的内容
				String inputContent = input.getText().trim();
				if(inputContent.length()>0){
					//把input中的内容发送到socket里
					socketOut.println(inputContent);
					//发送信息发送完以后应该清空文本框
					input.setText("");
				}
				
			}
		} );//send
			
	}//init()
	
	
	public static void main (String[] args) throws IOException {
		 
		new FirstClient().init();
			
	}
	
	
	//作为内部类,可以访问私有的成员变量
	class ClientThread extends Thread{
		private Socket socket;
		public ClientThread(Socket socket){
			this.socket = socket;
		}
		public void run(){
				try {
					/*
					 *从socket读,打印到控制台 
					 */
						//把字节流转化成字符流,从socket读
					InputStreamReader reader1 = new InputStreamReader(socket.getInputStream());
					//使用BufferedReader的好处是可以一次读一行
					BufferedReader br1 = new BufferedReader(reader1);
					//往socket里面写,println()一次就会清空一次
					//PrintStream ps = new PrintStream(s.getOutputStream());
					String keyline1 = null;
					while((keyline1 = br1.readLine())!=null){
						//判断发送过来的消息的种类,此处是服务器发送过来有哪些客户端在线的消息
						if(keyline1.startsWith(QQProtocal.UserList) && 
								keyline1.endsWith(QQProtocal.UserList)){
							
							//UserList是一个HashMap的value值,形式如[aaa,bbb],其中,aaa,bbb分别为客户端的用户名
							//所以此处得到子串(出去协议的头和尾)的时候要去掉[],所以都取长度-1
							
							//把读取的包装好的用户名拆分出真正的用户名
							String UserList = keyline1.substring(QQProtocal.UserList.length()+1,
									keyline1.length()-QQProtocal.UserList.length()-1);
							System.out.println(UserList);
							
							//用,来分解多个用户名
							String[] usernames = UserList.split(",");
							listmodel.clear();
							for(int i =0;i<usernames.length;++i){
								//添加用户名到用户列表
								listmodel.add(i, usernames[i].trim());
							}
							
							
						//	display.append(UserList+"\n");
							
						}
						
						//打印到控制台
					//	System.out.println(keyline1);
						//将聊天信息在文本域中显示
						else{
							display.append(keyline1+"\n");
						}
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}		
		}
	}

	
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值