怎么使用Java编写网络聊天程序


本篇内容介绍了“怎么使用Java编写网络聊天程序”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

实验目的:

使用客户机/服务器模式、基于TCP协议编写一对多“群聊”程序。其中客户机端单击“连接服务器”或“断开连接”按钮,均能即时更新服务器和所有客户机的在线人数和客户名。

实验要求:

设计一对多的网络聊天程序,要求:

1、基于TCP/IP设计聊天程序
2、采用图形界面设计
3、能够进行一对多聊天

项目截图

服务器端代码:

importjavax.swing.*;importjavax.swing.border.TitledBorder;importjava.awt.*;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjava.io.*;importjava.net.ServerSocket;importjava.net.Socket;importjava.util.ArrayList;importjava.util.Vector;publicclassServerextendsJFrame{//TODO该图形界面拥有三块区域,分别位于上、中、下(up、middle、down)。privateJPanelpanUp=newJPanel();privateJPanelpanMid=newJPanel();privateJPanelpanDown=newJPanel();//panUp区域的子节点定义,标签、输入框、按钮privateJLabellblLocalPort=newJLabel("本机服务器监听端口:");protectedJButtonbutStart=newJButton("启动服务器");protectedJTextFieldtfLocalPort=newJTextField(25);//panMid区域的子节点定义,显示框以及滚动条protectedJTextAreataMsg=newJTextArea(25,25);JScrollPanescroll=newJScrollPane(taMsg);//panDown区域的子节点定义,lstUsers在线用户界面JListlstUsers=newJList();//TODO以下是存放数据的变量publicstaticintlocalPort=8000;//默认端口8000staticintSerialNum=0;//用户连接数量ServerSocketserverSocket;//服务器端SocketArrayList<AcceptRunnable.Client>clients=newArrayList<>();//用户连接对象数组Vector<String>clientNames=newVector<>();//lstUsers中存放的数据//TODO构造方法publicServer(){init();}//TODO初始化方法:初始化图形界面布局privatevoidinit(){//panUp区域初始化:流式区域panUp.setLayout(newFlowLayout());panUp.add(lblLocalPort);panUp.add(tfLocalPort);panUp.add(butStart);tfLocalPort.setText(String.valueOf(localPort));butStart.addActionListener(newstartServerHandler());//注册"启动服务器"按钮点击事件//panMid区域初始化panMid.setBorder(newTitledBorder("监听消息"));taMsg.setEditable(false);panMid.add(scroll);//panDown区域初始化panDown.setBorder(newTitledBorder("在线用户"));panDown.add(lstUsers);lstUsers.setVisibleRowCount(10);//图形界面的总体初始化+启动图形界面this.setTitle("服务器端");this.add(panUp,BorderLayout.NORTH);this.add(panMid,BorderLayout.CENTER);this.add(panDown,BorderLayout.SOUTH);this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);this.setPreferredSize(newDimension(600,400));this.pack();this.setVisible(true);}//TODO“启动服务器”按钮的动作事件监听处理类privateclassstartServerHandlerimplementsActionListener{@OverridepublicvoidactionPerformed(ActionEvente){try{//当点击按钮时,获取端口设置并启动新进程、监听端口localPort=Integer.parseInt(tfLocalPort.getText());serverSocket=newServerSocket(localPort);ThreadacptThrd=newThread(newAcceptRunnable());acptThrd.start();taMsg.append("****服务器(端口"+localPort+")已启动****\n");}catch(Exceptionex){System.out.println(ex);}}}//TODO接受用户连接请求的线程关联类privateclassAcceptRunnableimplementsRunnable{publicvoidrun(){//持续监听端口,当有新用户连接时再开启新进程while(true){try{Socketsocket=serverSocket.accept();//新的用户已连接,创建Client对象Clientclient=newClient(socket);taMsg.append("——客户【"+client.nickname+"】加入\n");ThreadclientThread=newThread(client);clientThread.start();clients.add(client);}catch(Exceptionex){System.out.println(ex);}}}//TODO服务器存放用户对象的客户类(主要编程)。每当有新的用户连接时,该类都会被调用//TODO该类继承自Runnable,内部含有run()方法privateclassClientimplementsRunnable{privateSocketsocket;//用来保存用户的连接对象privateBufferedReaderin;//IO流privatePrintStreamout;privateStringnickname;//保存用户昵称//Client类的构建方法。当有新用户连接时会被调用publicClient(Socketsocket)throwsException{this.socket=socket;InputStreamis=socket.getInputStream();in=newBufferedReader(newInputStreamReader(is));OutputStreamos=socket.getOutputStream();out=newPrintStream(os);nickname=in.readLine();//获取用户昵称for(Clientc:clients){//将新用户的登录消息发给所有用户c.out.println("——客户【"+nickname+"】加入\n");}}//客户类线程运行方法publicvoidrun(){try{while(true){Stringusermsg=in.readLine();//读用户发来消息StringsecondMsg=usermsg.substring(usermsg.lastIndexOf(":")+1);//字符串辅助对象//如果用户发过来的消息不为空if(usermsg!=null&&usermsg.length()>0){//如果消息是bye,则断开与此用户的连接并告知所有用户当前信息,跳出循环终止当前进程if(secondMsg.equals("bye")){clients.remove(this);for(Clientc:clients){c.out.println(usermsg);}taMsg.append("——客户离开:"+nickname+"\n");//更新在线用户数量lstUsers的界面信息updateUsers();break;}/***每当有新用户连接时,服务器就会接收到USERS请求*当服务器接收到此请求时,就会要求现在所有用户更新在线用户数量的列表**/if(usermsg.equals("USERS")){updateUsers();continue;}//当用户发出的消息都不是以上两者时,消息才会被正常发送for(Clientc:clients){c.out.println(usermsg);}}}socket.close();}catch(Exceptionex){System.out.println(ex);}}//TODO更新在线用户数量lstUsers信息,并要求所有的用户端同步更新publicvoidupdateUsers(){//clientNames是Vector<String>对象,用来存放所有用户的名字clientNames.removeAllElements();StringBufferallname=newStringBuffer();for(AcceptRunnable.Clientclient:clients){clientNames.add(0,client.nickname);allname.insert(0,"|"+client.nickname);}panDown.setBorder(newTitledBorder("在线用户("+clientNames.size()+"个)"));//要求所有的用户端同步更新for(Clientc:clients){c.out.println(clientNames);}lstUsers.setListData(clientNames);}}}//TODO主方法publicstaticvoidmain(String[]args){newServer();}}

客户端代码:

importjavax.swing.*;importjavax.swing.border.TitledBorder;importjava.awt.*;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjava.awt.event.WindowAdapter;importjava.awt.event.WindowEvent;importjava.io.BufferedReader;importjava.io.InputStreamReader;importjava.io.PrintStream;importjava.net.Socket;importjava.util.Vector;publicclassClientextendsJFrame{//客户机窗体类//TODO该图形界面拥有四块区域,分别位于上、左、中、下(up、Left、middle、down)。privateJPanelpanUp=newJPanel();privateJPanelpanLeft=newJPanel();privateJPanelpanMid=newJPanel();privateJPanelpanDown=newJPanel();//panUp区域的子节点定义,3个标签、3个输入框、2个按钮privateJLabellblLocalPort1=newJLabel("服务器IP:");privateJLabellblLocalPort2=newJLabel("端口:");privateJLabellblLocalPort3=newJLabel("本人昵称:");protectedJTextFieldtfLocalPort1=newJTextField(15);protectedJTextFieldtfLocalPort2=newJTextField(5);protectedJTextFieldtfLocalPort3=newJTextField(5);protectedJButtonbutStart=newJButton("连接服务器");protectedJButtonbutStop=newJButton("断开服务器");//TODO//panLeft区域的子节点定义,显示框、滚动条protectedJTextAreataMsg=newJTextArea(25,25);JScrollPanescroll=newJScrollPane(taMsg);//panMid区域的子节点定义,lstUsers在线用户界面JListlstUsers=newJList();//panDown区域的子节点定义,标签,输入框privateJLabellblLocalPort4=newJLabel("消息(按回车发送):");protectedJTextFieldtfLocalPort4=newJTextField(20);/***=====变量分割=====*上面是图形界面变量,下面是存放数据的变量*/BufferedReaderin;PrintStreamout;publicstaticintlocalPort=8000;//默认端口publicstaticStringlocalIP="127.0.0.1";//默认服务器IP地址publicstaticStringnickname="Cat";//默认用户名publicSocketsocket;publicstaticStringmsg;//存放本次发送的消息Vector<String>clientNames=newVector<>();//TODO构造方法publicClient(){init();}//TODO初始化方法:初始化图形界面privatevoidinit(){//panUp区域初始化:流式面板,3个标签、3个输入框,2个按钮panUp.setLayout(newFlowLayout());panUp.add(lblLocalPort1);panUp.add(tfLocalPort1);panUp.add(lblLocalPort2);panUp.add(tfLocalPort2);panUp.add(lblLocalPort3);panUp.add(tfLocalPort3);tfLocalPort1.setText(localIP);tfLocalPort2.setText(String.valueOf(localPort));tfLocalPort3.setText(nickname);panUp.add(butStart);panUp.add(butStop);butStart.addActionListener(newlinkServerHandlerStart());butStop.addActionListener(newlinkServerHandlerStop());butStop.setEnabled(false);//断开服务器按钮的初始状态应该为不可点击,只有连接服务器之后才能点击//添加LefttaMsg.setEditable(false);panLeft.add(scroll);panLeft.setBorder(newTitledBorder("聊天——消息区"));scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);//添加MiddlepanMid.setBorder(newTitledBorder("在线用户"));panMid.add(lstUsers);lstUsers.setVisibleRowCount(20);//添加Down//TODO此处注意:JTextField输入框的回车事件默认存在,无需添加panDown.setLayout(newFlowLayout());panDown.add(lblLocalPort4);panDown.add(tfLocalPort4);tfLocalPort4.addActionListener(newClient.SendHandler());//图形界面的总体初始化+启动图形界面this.setTitle("客户端");this.add(panUp,BorderLayout.NORTH);this.add(panLeft,BorderLayout.WEST);this.add(panMid,BorderLayout.CENTER);this.add(panDown,BorderLayout.SOUTH);this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);this.addWindowListener(newWindowHandler());this.setPreferredSize(newDimension(800,600));this.pack();this.setVisible(true);}//TODO“连接服务器”按钮的动作事件监听处理类:privateclasslinkServerHandlerStartimplementsActionListener{@OverridepublicvoidactionPerformed(ActionEvente){//当点击"连接服务器"按钮之后,该按钮被禁用(不可重复点击)。同时"断开服务器按钮"被恢复使用butStart.setEnabled(false);butStop.setEnabled(true);localIP=tfLocalPort1.getText();localPort=Integer.parseInt(tfLocalPort2.getText());nickname=tfLocalPort3.getText();linkServer();//连接服务器ThreadacceptThread=newThread(newClient.ReceiveRunnable());acceptThread.start();}}//TODO“断开服务器”按钮的动作事件监听处理类privateclasslinkServerHandlerStopimplementsActionListener{/***当点击该按钮之后,断开服务器连接、清空图形界面所有数据*/@OverridepublicvoidactionPerformed(ActionEvente){taMsg.setText("");clientNames=newVector<>();updateUsers();out.println("——客户【"+nickname+"】离开:bye\n");butStart.setEnabled(true);butStop.setEnabled(false);}}//TODO连接服务器的方法publicvoidlinkServer(){try{socket=newSocket(localIP,localPort);}catch(Exceptionex){taMsg.append("====连接服务器失败~====");}}//TODO接收服务器消息的线程关联类privateclassReceiveRunnableimplementsRunnable{publicvoidrun(){try{in=newBufferedReader(newInputStreamReader(socket.getInputStream()));out=newPrintStream(socket.getOutputStream());out.println(nickname);//当用户首次连接服务器时,应该向服务器发送自己的用户名、方便服务器区分taMsg.append("——本人【"+nickname+"】成功连接到服务器......\n");out.println("USERS");//向服务器发送"神秘代码",请求当前在线用户列表while(true){msg=in.readLine();//读取服务器端的发送的数据//此if语句的作用是:过滤服务器发送过来的更新当前在线用户列表请求if(msg.matches(".*\\[.*\\].*")){clientNames.removeAllElements();String[]split=msg.split(",");for(Stringsingle:split){clientNames.add(single);}updateUsers();continue;}//更新"聊天——消息区"信息taMsg.append(msg+"\n");//此if语句作用:与服务器进行握手确认消息。//当接收到服务器端发送的确认离开请求bye的时候,用户真正离线msg=msg.substring(msg.lastIndexOf(":")+1);if(msg.equals(nickname)){socket.close();clientNames.remove(nickname);updateUsers();break;//终止线程}}}catch(Exceptione){}}}//TODO"发送消息文本框"的动作事件监听处理类privateclassSendHandlerimplementsActionListener{@OverridepublicvoidactionPerformed(ActionEvente){out.println("【"+nickname+"】:"+tfLocalPort4.getText());tfLocalPort4.setText("");//当按下回车发送消息之后,输入框应该被清空}}//TODO窗口关闭的动作事件监听处理类//当用户点击"x"离开窗口时,也会向服务器发送bye请求,目的是为了同步更新数据。privateclassWindowHandlerextendsWindowAdapter{@OverridepublicvoidwindowClosing(WindowEvente){cutServer();}}privatevoidcutServer(){out.println("——客户【"+nickname+"】离开:bye");}//TODO更新"在线用户列表"的方法publicvoidupdateUsers(){panMid.setBorder(newTitledBorder("在线用户("+clientNames.size()+"个)"));lstUsers.setListData(clientNames);}//TODO主方法publicstaticvoidmain(String[]args){newClient();}}

如何同时开启两个客户端进行聊天?

将上述的 Client 类复制一份,改名为 Client2 ,然后同时启动 Client 和 Client2 程序。

“怎么使用Java编写网络聊天程序”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注主机评测网网站,小编将为大家输出更多高质量的实用文章!


上一篇:如何使用Swift?Package插件生成代码

下一篇:Redis如何实现数据的交集、并集、补集


Copyright © 2002-2019 测速网 www.inhv.cn 皖ICP备2023010105号
测速城市 测速地区 测速街道 网速测试城市 网速测试地区 网速测试街道
温馨提示:部分文章图片数据来源与网络,仅供参考!版权归原作者所有,如有侵权请联系删除!

热门搜索 城市网站建设 地区网站制作 街道网页设计 大写数字 热点城市 热点地区 热点街道 热点时间 房贷计算器