新浪面试题

在服务器端 用一个HashMap<userName,socket> 维护所有用户相关的信息,从而能够保证和所有的用户进行通讯。

客户端的动作:
(1)连接(登录):发送userName    服务器的对应动作:1)界面显示,2)通知其他用户关于你登录的信息, 3)把其他在线用户的userName通知当前用户 4)开启一个线程专门为当前线程服务

(2)退出(注销):
(3)发送消息
※※发送通讯内容之后,对方如何知道是干什么,通过消息协议来实现:
客户端向服务器发的消息格式设计:
命令关键字@#接收方@#消息内容@#发送方
1)连接:userName      ----握手的线程serverSocket专门接收该消息,其它的由服务器新开的与客户进行通讯的socket来接收
2)退出:exit@#全部@#null@#userName
3)发送: on @# JList.getSelectedValue() @# tfdMsg.getText() @# tfdUserName.getText()

服务器向客户端发的消息格式设计:
命令关键字@#发送方@#消息内容
登录:
   1) msg   @#server @# 用户[userName]登录了  (给客户端显示用的)
   2) cmdAdd@#server @# userName (给客户端维护在线用户列表用的)
退出:
   1) msg   @#server @# 用户[userName]退出了  (给客户端显示用的)
   2) cmdRed@#server @# userName (给客户端维护在线用户列表用的)
发送:
   msg   @#消息发送者( msgs[3] ) @# 消息内容 (msgs[2])


运行结果如下:
package cn.hncu;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;

import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.border.TitledBorder;

public class ServerForm extends JFrame{
    private DefaultListModel lm;
    private JTextArea area;
    private final static int PORT=10010;
    private Map<String, Socket> map=new HashMap<String, Socket>();
	public ServerForm() {
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		//setBounds(x, y, width, height)
		final int winWidth=(int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
		final int winHeight=(int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
		setBounds(winWidth/4, winHeight/4, winWidth/2, winHeight/2);
		
		//加聊天面板
		area=new JTextArea();
		area.setEditable(false);
		this.getContentPane().add(new JScrollPane(area),BorderLayout.CENTER);
		lm=new DefaultListModel();
		JList list=new JList(lm);
		
		JScrollPane js=new JScrollPane(list);
		js.setBorder(new TitledBorder("在线"));
		js.setPreferredSize(new Dimension(180, this.getHeight()));
		getContentPane().add(js,BorderLayout.EAST);
		
		JMenuBar bar=new JMenuBar();
		JMenu menu=new JMenu("控制");
		menu.setMnemonic('C');//助记符
		final JMenuItem run=new JMenuItem("开启");
		run.setAccelerator(KeyStroke.getKeyStroke('R',KeyEvent.CTRL_MASK));//设置快捷键ctrl+R
		run.setActionCommand("run");
		menu.add(run);
		menu.addSeparator();
		JMenuItem exit=new JMenuItem("退出");
		exit.setAccelerator(KeyStroke.getKeyStroke('E',KeyEvent.CTRL_MASK));
		exit.setActionCommand("exit");
		menu.add(exit);
		bar.add(menu);
		setJMenuBar(bar);
		//监听
		ActionListener action=new ActionListener() {		
			@Override
			public void actionPerformed(ActionEvent e) {
                if(e.getActionCommand().equals("run")){
                	startServer();
                	run.setEnabled(false);
                }else{
                	System.exit(0);
                }				
			}			
		};	
		run.addActionListener(action);
		exit.addActionListener(action);
		setVisible(true);	
	}
	public static void main(String[] args) {
        JFrame.setDefaultLookAndFeelDecorated(true);
        new ServerForm();
	}
	private void startServer() {
		try {
			ServerSocket server=new ServerSocket(PORT);
			area.setText("启动服务"+server);
			new ServerThread(server).start();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	class ServerThread extends Thread{
        private ServerSocket server;
        
		public ServerThread(ServerSocket server) {
			this.server = server;
		}

		@Override
		public void run() {
			while(true){
				//握手,第一次。专门来接待客户端第一次链接。
				try {
					Socket socketClient=server.accept();
					Scanner sc=new Scanner(socketClient.getInputStream());
					if(sc.hasNext()){
						//System.out.println("..........");
						String userName=sc.nextLine();
						area.append("\r\n用户"+userName+"登录"+socketClient);
						lm.addElement(userName);
						new ClientThread(socketClient).start();
						
						
						msgAll(userName);//把当前用户登陆的消息通知给所有在线的人。
						msgSelf(socketClient);//通知自己当前在线的所有人。
						//把当前登陆的用户加到在线用户列表中
						map.put(userName,socketClient);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				
			}
		}	
	

		class ClientThread extends Thread{
            private Socket socketClient;
             
			public ClientThread(Socket socketClient) {
				this.socketClient = socketClient;
			}

			@Override
			public void run() {
				System.out.println("一个与客户端通讯的线程启动");
				try {
					Scanner sc=new Scanner(socketClient.getInputStream());
					while(sc.hasNext()){
						String msg=sc.nextLine();
						String msgs[]=msg.split("@#");
						//防黑
						if(msgs.length!=4){
							System.out.println("防黑处理");
						}
						if("on".equals(msgs[0])){
							sendMsgToSb(msgs);
						}
						if("exit".equals(msgs[0])){
							//从在线用户map中把该用户删掉
							map.remove(msgs[3]);
							//从服务器的在线列表中把该用户删除
							lm.removeElement(msgs[3]);
							
							//通知其他在线用户有关退出的消息
							sendExitMsgToAll(msgs);
							//在服务器窗口显示该用户退出的消息
							area.append("\r\n用户"+msgs[3]+"退出了");
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			}

			

			
		}
	}
	
	//服务器把客户端的聊天消息转发给相应的其它客户端
	private void sendMsgToSb(String[] msgs) throws Exception {
		if("全部".equals(msgs[1])){
			Iterator it=map.keySet().iterator();
			while(it.hasNext()){//遍历每一个在线用户,把聊天消息转发给他
				String userName=(String) it.next();
				Socket s=map.get(userName);
				PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
				String str="msg@#"+msgs[3]+"@#"+msgs[2];
				pw.println(str);
				pw.flush();
			}
		}else{
			Socket s=map.get(msgs[1]);
			PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
			String str="msg@#"+msgs[3]+"@#"+msgs[2];
			pw.println(str);
			pw.flush();
		}
	}
	//通知其他在线用户有人退出了
	private void sendExitMsgToAll(String[] msgs) throws IOException {
        Iterator<String> it=map.keySet().iterator();
        while(it.hasNext()){
        	String userName=it.next();
        	Socket s=map.get(userName);
        	PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
        	String str="msg@#server@#用户["+msgs[3]+"]退出了";
        	pw.println(str);
        	pw.flush();
        	str="cmdRed@#server@#"+msgs[3];
        	pw.println(str);
        	pw.flush();
        }
	}
	private void msgAll(String userName) {
		 Iterator<Socket> it=map.values().iterator();
		 while(it.hasNext()){
			 Socket s=it.next();
			 PrintWriter pw;
			try {//把服务器要我们做的事情变成协议格式,由我们来解析
				pw = new PrintWriter(s.getOutputStream(),true);
				String msg="msg@#server@#用户["+userName+"]登陆了";
				pw.println(msg);
				//pw.flush();
				msg="cmdAdd@#server@#"+userName;
				pw.println(msg);
				//pw.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		 }
	}
	
	private void msgSelf(Socket socketClient) {
		try {
			PrintWriter pw=new PrintWriter(socketClient.getOutputStream(),true);
			Iterator it=map.keySet().iterator();
			while(it.hasNext()){
				pw.println("cmdAdd@#server@#"+it.next());
				//pw.flush();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


//客户端
package cn.hncu;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.border.TitledBorder;
public class ClientForm  extends JFrame implements ActionListener{
	private JButton btnConn;
	private JButton btnExit;
	private DefaultListModel lm;
	private JTextArea allMsg;
	private JTextField text;
	private JTextField userText;
	private JList list;
	private  static String HOST="127.0.0.1";
	private  static int PORT=10010;
	public ClientForm() {
		//菜单条
		addMenu();
		setBounds(300, 200, 500, 400);	
		//上部面板
		JPanel panel=new JPanel();
		panel.add(new JLabel("用户标识"));
		panel.add(this.userText=new JTextField(10));
		panel.add(this.btnConn=new JButton("连接"));
		btnConn.addActionListener(this);
		btnConn.setActionCommand("conn");
		//btnConn.addActionListener(this);
		panel.add(this.btnExit=new JButton("退出"));
		btnExit.addActionListener(this);
		btnExit.setActionCommand("exit");
		this.getContentPane().add(panel,BorderLayout.NORTH);
		//中部面板
		//lm=new ListModel() ;
		//JList list=new JList(dataModel)
		JPanel panelC=new JPanel(new BorderLayout());//默认是FlowLayout
		this.getContentPane().add(panelC,BorderLayout.CENTER);
		lm=new DefaultListModel();
		list=new JList(lm);
	    lm.addElement("全部");
	    list.setSelectedIndex(0);
	    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	    list.setVisibleRowCount(2);
	    JScrollPane js=new JScrollPane(list);
	    js.setBorder(new TitledBorder("在线"));
	    //注意,在使用布局方式控制组件时,最好使用setPreferredSize来控制大小,使用setSize有时会失效。
	    js.setPreferredSize(new Dimension(150, panelC.getHeight()));
	    //js.setSize(150, panelC.getHeight());
	    panelC.add(js,BorderLayout.EAST); 
	    allMsg=new JTextArea();
	    allMsg.setEditable(false);
	    panelC.add(new JScrollPane(allMsg),BorderLayout.CENTER);   
	    //消息发送面板
	    JPanel panelS=new JPanel();
	    panelS.add(new JLabel("消息"));
	    text=new JTextField(20);
	    panelS.add(text);
		JButton btnSend=new JButton("发送");
		panelS.add(btnSend);
		btnSend.addActionListener(this);
		btnSend.setActionCommand("send");
		panelC.add(panelS,BorderLayout.SOUTH);	
		
		//给窗口右上角的关闭按钮添加事件处理
		addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {
				sendExitMsg();
				
			}
			
		});
		setVisible(true);
	}
	private void addMenu() {
        JMenuBar menuBar=new JMenuBar();
        JMenu menu=new JMenu("选项");
        JMenuItem item=new JMenuItem("设置");
        JMenuItem help=new JMenuItem("帮助");
        setJMenuBar(menuBar);
        menuBar.add(menu);
        menu.add(item);
        menu.add(help);
        help.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				JDialog dlg=new JDialog(ClientForm.this);
				dlg.setBounds(ClientForm.this.getX(), ClientForm.this.getY(),300, 100);
				dlg.setLayout(new FlowLayout());
				dlg.add(new JLabel("版本所有@城院杜雅倩qq:1115179167"));
				dlg.setVisible(true);
			}
		});
        item.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				final JDialog dlg=new JDialog(ClientForm.this);
				dlg.setBounds(ClientForm.this.getX(), ClientForm.this.getY(),300, 100);
				dlg.setLayout(new FlowLayout());
				dlg.add(new JLabel("服务器"));
				final JTextField host=new JTextField(10);
				host.setText(HOST);
				dlg.add(host);
				dlg.add(new JLabel(":"));
				final JTextField port=new JTextField(5);
				port.setText(""+PORT);
				dlg.add(port);
				JButton btnSet=new JButton("设置");
				dlg.add(btnSet);
				btnSet.addActionListener(new ActionListener() {
					
					@Override
					public void actionPerformed(ActionEvent e) {
						//按理应该先解析并判断端口号是否合法,此处省略了
                        HOST=host.getText();
                        //System.out.println(HOST);
                        PORT=Integer.parseInt(port.getText());
                        //System.out.println(PORT);
                        dlg.dispose();//关闭并销毁该对话框,清理内存
					}
				});
				
				dlg.setVisible(true);
			}
		});
	}
	public static void main(String[] args) {
		JFrame.setDefaultLookAndFeelDecorated(true);
        new ClientForm();   
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getActionCommand().equals("conn")){
			System.out.println("连接服务器。。。");
			try {
				connecting();
				((JButton)e.getSource()).setEnabled(false);
				//setEnabled(false);
				userText.setEditable(false);
			} catch (IOException e1) {
				JOptionPane.showMessageDialog(this, "服务器连接失败,请检查网络或者查看服务器地址与端口号");
			}
		}
		if(e.getActionCommand().equals("send")){
			if(text.getText()==null || text.getText().trim().length()==0){
				JOptionPane.showMessageDialog(this, "请输入聊天消息");
				return;
			}
			String msg="on@#"+list.getSelectedValue()+"@#"+text.getText()+"@#"+userText.getText();
			pw.println(msg);
		}
		if(e.getActionCommand().endsWith("exit")){
			sendExitMsg();
			//((JButton)e.getSource()).setEnabled(false);
			System.exit(0);
		}
	}
	//向服务器发送退出消息
	private void sendExitMsg() { 
		 if(client==null){
			 System.exit(0);
		 }
		 String msg="exit@#全部@#null@#"+userText.getText();
		 System.out.println("退出"+msg);
		 pw.println(msg);
		 pw.flush();
		 System.exit(0);
	}
	private Socket client;
	private PrintWriter pw;
	private void connecting() throws IOException {
        
			client=new Socket(HOST, PORT);
			String userName=userText.getText();
			pw=new PrintWriter(client.getOutputStream(),true);
			pw.println(userName);
			this.setTitle("用户"+"["+userName+"]"+"在线");
			//服务器也要给我们发消息的,比如说谁在线等消息。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            这里是在图形界面线程,最好另外开一个线程来相应。
			new ClientThread().start();
		
	}	
    class ClientThread extends Thread{
		@Override
		public void run() {
			//用来接收服务器给我们发送的消息
			try {
				Scanner sc = new Scanner(client.getInputStream());
				while(sc.hasNext()){
					String str=sc.nextLine();
					//拆分服务器发送过来的消息,并解析
					String[] msgs=str.split("@#");
					if("msg".equals(msgs[0])){
						if("server".equals(msgs[1])){
							str="[通知]:"+msgs[2]; 
						}else {//服务器转发的聊天消息
							str=msgs[1]+"说"+msgs[2];
						}	
						allMsg.append("\r\n"+str);//在用户聊天面板里添加消息
					}else if("cmdAdd".equals(msgs[0])){
						lm.addElement(msgs[2]);	//实时维护用户在线列表
					}else{
						lm.removeElement(msgs[2]);
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}	
		}  	
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值