Socket编程------模拟QQ聊天(TCP)

来源:互联网 发布:铁岭网络预约出租汽车 编辑:程序博客网 时间:2024/05/16 09:03

模拟QQ聊天

一、要求

1、一个服务器可以与多个用户同时通讯

2、用户可以通过服务器与用户之间通讯

3、用户可以选择和所有人发消息,也可以选择和某个用户单独发消息

4、服务器要显示当前所有在线人员

5、用户要显示当前在线的人员

6、当有新用户登录时或在线用户退出时,服务器要向所有其他在线用户发送提示信息,并且服务器也要显示相应的提示信息

7、不能有相同的用户名同时登陆

8、不能发送空消息

9、客户端可以设置连接的服务器IP和端口


二、QQ聊天协议

在服务器端 用一个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])


三、代码

ClientForm.java

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.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.ListSelectionModel;import javax.swing.border.TitledBorder;public class ClientForm extends JFrame implements ActionListener{private JTextField tfdUserName=null;private JTextArea allMsg=null;private DefaultListModel dlm=null;private JList list=null;private JTextField tfdMsg=null;private static PrintWriter pw;private static String HOST="192.168.1.106";private static int PORT=9090;private static Socket clientSocket=null;public ClientForm(){//menuaddMenu();//panel aboveJPanel p1=new JPanel();p1.add(new JLabel("userLogo"));tfdUserName=new JTextField(10);p1.add(tfdUserName);JButton connect=new JButton("connect");connect.setActionCommand("connect");connect.addActionListener(this);JButton exit=new JButton("exit");exit.setActionCommand("exit");exit.addActionListener(this);p1.add(connect);p1.add(exit);this.getContentPane().add(p1,BorderLayout.NORTH);//middle panelJPanel p2=new JPanel(new BorderLayout());this.getContentPane().add(p2,BorderLayout.CENTER);//online list widgetdlm=new DefaultListModel();dlm.addElement("ALL");list=new JList(dlm);list.setSelectedIndex(0);list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);list.setVisibleRowCount(2);JScrollPane js=new JScrollPane(list);js.setBorder(new TitledBorder("Online"));js.setPreferredSize(new Dimension(70, p2.getHeight()));p2.add(js,BorderLayout.EAST);//chatting message areaallMsg=new JTextArea();allMsg.setEditable(false);p2.add(new JScrollPane(allMsg),BorderLayout.CENTER);//message sending panelJPanel p3=new JPanel();JLabel messageLabel=new JLabel("message");p3.add(messageLabel);tfdMsg=new JTextField(20);p3.add(tfdMsg);JButton sendBtn=new JButton("send");sendBtn.setActionCommand("send");sendBtn.addActionListener(this);p3.add(sendBtn);this.getContentPane().add(p3,BorderLayout.SOUTH);this.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {sendExitMsg();}});this.setBounds(300, 300, 400, 300);this.setVisible(true);}private void addMenu() {JMenuBar menubar=new JMenuBar();this.setJMenuBar(menubar);JMenu menu=new JMenu("Selection");menubar.add(menu);JMenuItem itemSet=new JMenuItem("Set");JMenuItem itemHelp=new JMenuItem("Help");menu.add(itemSet);menu.add(itemHelp);itemSet.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {final JDialog dlg=new JDialog(ClientForm.this);dlg.setLayout(new FlowLayout());dlg.setBounds(ClientForm.this.getX()+40, ClientForm.this.getY()+40, 300, 100);dlg.add(new JLabel("server"));final JTextField tfdId=new JTextField(HOST);final JTextField tfdPort=new JTextField(""+PORT);dlg.add(tfdId);dlg.add(new JLabel(":"));dlg.add(tfdPort);JButton btnSet=new JButton("Set");dlg.add(btnSet);btnSet.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {HOST=tfdId.getText();PORT=Integer.parseInt(tfdPort.getText());dlg.dispose();}});dlg.setVisible(true);}});itemHelp.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {JDialog dlg=new JDialog(ClientForm.this);dlg.setLayout(new FlowLayout());dlg.setBounds(ClientForm.this.getX()+40, ClientForm.this.getY()+40, 300, 100);dlg.add(new JLabel("Copyright@hncu.2016-5-15,QQ:666666"));dlg.setVisible(true);}});}@Overridepublic void actionPerformed(ActionEvent e) {if (e.getActionCommand().equalsIgnoreCase("connect")){System.out.println("connecting server");String userName=tfdUserName.getText().trim();byte byteName[]=userName.getBytes();if (byteName.length==0){JOptionPane.showMessageDialog(this, "please input right name format");return;} else {try {connecting(userName);} catch (Exception e1) {JOptionPane.showMessageDialog(this, "Failed in connecting,please check your internet or ip address and port");return;}((JButton)e.getSource()).setEnabled(false);tfdUserName.setEditable(false);}}if (e.getActionCommand().equalsIgnoreCase("send")){if (tfdMsg.getText()==null){JOptionPane.showMessageDialog(this, "Please input dialog message");return;}String str="on@#"+list.getSelectedValue()+"@#"+tfdMsg.getText()+"@#"+tfdUserName.getText();try {pw.println(str);pw.flush();} catch (Exception e1) {JOptionPane.showMessageDialog(this, "Please connect the server first.");return;}tfdMsg.setText("");}if (e.getActionCommand().equalsIgnoreCase("exit")){sendExitMsg();}}private void sendExitMsg() {if (clientSocket==null){System.exit(0);}String str="exit@#all@#null@#"+tfdUserName.getText();pw.println(str);pw.flush();System.exit(0);}private void connecting(String userName) throws Exception{clientSocket=new Socket(HOST,PORT);if (userName!=""){pw=new PrintWriter(clientSocket.getOutputStream(),true);pw.println(userName);this.setTitle("User ["+userName+"] is online...");new ClientThread().start();}}class ClientThread extends Thread{@Overridepublic void run() {try {Scanner input=new Scanner(clientSocket.getInputStream());while (input.hasNextLine()){String str=input.nextLine();String msgs[]=str.split("@#");if ("msg".equals(msgs[0])){if ("server".equals(msgs[1])){str="[notify]:"+msgs[2];} else{str="["+msgs[1]+"] says:"+msgs[2];}allMsg.append("\r\n"+str);} else if ("cmdAdd".equals(msgs[0])){dlm.addElement(msgs[2]);} else if ("cmdRed".equalsIgnoreCase(msgs[0])){dlm.removeElement(msgs[2]);}}} catch (IOException e) {e.printStackTrace();}}}public static void main(String[] args) {JFrame.setDefaultLookAndFeelDecorated(true);new ClientForm();}}
ServerForm.java

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.JOptionPane;import javax.swing.JScrollPane;import javax.swing.JTextArea;import javax.swing.KeyStroke;import javax.swing.border.TitledBorder;public class ServerForm extends JFrame {private static final int PORT=9090;private JTextArea area;private DefaultListModel dlm;private Map<String, Socket> map=new HashMap<String, Socket>();public ServerForm() {this.setDefaultCloseOperation(EXIT_ON_CLOSE);final int winWidth=(int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();final int winHeight=(int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();//message windowarea=new JTextArea();area.setEditable(false);this.getContentPane().add(new JScrollPane(area),BorderLayout.CENTER);//online list widgetdlm=new DefaultListModel();JList list=new JList(dlm);JScrollPane js=new JScrollPane(list);js.setBorder(new TitledBorder("Online"));js.setPreferredSize(new Dimension(100, this.getHeight()));this.getContentPane().add(js,BorderLayout.EAST);//menuJMenuBar bar=new JMenuBar();this.setJMenuBar(bar);JMenu menu=new JMenu("Control(C)");bar.add(menu);menu.setMnemonic('C');final JMenuItem run=new JMenuItem("run");run.setActionCommand("run");run.setAccelerator(KeyStroke.getKeyStroke('R',KeyEvent.CTRL_MASK));menu.add(run);menu.addSeparator();final JMenuItem exit=new JMenuItem("exit");exit.setActionCommand("exit");exit.setAccelerator(KeyStroke.getKeyStroke('E',KeyEvent.CTRL_MASK));menu.add(exit);ActionListener al=new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {if (e.getActionCommand().equalsIgnoreCase("run")){startServer();run.setEnabled(false);} else {System.exit(0);}}};run.addActionListener(al);exit.addActionListener(al);this.setBounds(winWidth/4, winHeight/4, winWidth/2, winHeight/2);this.setVisible(true);}private void startServer() {try {ServerSocket server=new ServerSocket(PORT);area.append("start service:"+server);new ServerThread(server).start();} catch (IOException e) {e.printStackTrace();}}class ServerThread extends Thread{private ServerSocket server=null;ServerThread(ServerSocket server){this.server=server;}@Overridepublic void run() {try {while (true){Socket clientSocket=server.accept();Scanner input=new Scanner(clientSocket.getInputStream());if (input.hasNextLine()){String userName=input.nextLine();area.append("\r\n user:["+userName+"] login,"+clientSocket);dlm.addElement(userName);new ClientThread(clientSocket).start();msgAll(userName);msgSelf(clientSocket);map.put(userName, clientSocket);}}} catch (IOException e) {e.printStackTrace();}}}class ClientThread extends Thread{private Socket clientSocket=null;public ClientThread(Socket clientSocket) {this.clientSocket = clientSocket;}@Overridepublic void run() {try {Scanner input=new Scanner(clientSocket.getInputStream());while (input.hasNextLine()){String str=input.nextLine();String msgs[]=str.split("@#");if (msgs.length!=4){JOptionPane.showMessageDialog(null, "wrong message format");}if ("on".equalsIgnoreCase(msgs[0])){sendMsgToSb(msgs);}if ("exit".equalsIgnoreCase(msgs[0])){map.remove(msgs[3]);dlm.removeElement(msgs[3]);sendExitMsgToAll(msgs);area.append("\r\n user["+msgs[3]+"] leaves.");}}} catch (IOException e) {e.printStackTrace();}}}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 msg="msg@#server@#user ["+msgs[3]+"] leaves";pw.println(msg);msg="cmdRed@#server@#"+msgs[3];pw.println(msg);pw.flush();}}private void sendMsgToSb(String[] msgs) throws IOException {if ("all".equalsIgnoreCase(msgs[1])){Iterator<String> userNames=map.keySet().iterator();while (userNames.hasNext()){String userName=userNames.next();Socket s=map.get(userName);String msg="msg@#"+msgs[3]+"@#"+msgs[2];PrintWriter pw=new PrintWriter(s.getOutputStream(), true);pw.println(msg);pw.flush();}} else {String userName=msgs[1];Socket s=map.get(userName);String msg="msg@#"+msgs[3]+"@#"+msgs[2];PrintWriter pw=new PrintWriter(s.getOutputStream(), true);pw.println(msg);pw.flush();}}private void msgAll(String userName) {Iterator<Socket> it = map.values().iterator();while(it.hasNext()){Socket s = it.next();try {PrintWriter 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 clientSocket) {try {PrintWriter pw=new PrintWriter(clientSocket.getOutputStream(),true);Iterator<String> it=map.keySet().iterator();while (it.hasNext()){String msg="cmdAdd@#server@#"+it.next();pw.println(msg);pw.flush();}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {JFrame.setDefaultLookAndFeelDecorated(true);new ServerForm();}}


本程序登录时需要先启动服务器,然后再输入用户名才可以登录,聊天过程中可以群聊也可以单聊,单聊只要点击需要单聊的人的名字即可,群聊即点击列表中的“ALL”;还可以在菜单设置IP地址和端口号。

本程序修复好的漏洞:

没有输入用户名应不可登录

登陆后应不可再次点击登录按钮

没有登录应不可发送消息且发送的消息不可为空

IP地址和端口错误也不可登录




0 0
原创粉丝点击