zoukankan      html  css  js  c++  java
  • 一个简易聊天功能的服务器端和客户端源码

    本文来自:小易博客专栏。转载请注明出处:http://blog.csdn.net/oldinaction

    学习完J2SE可以写一个简易的聊天软件来让刚学的知识融会贯通,代码注释的很详细哦!

    开发版本历程:

    V0.1:客户端实现一个界面
    V0.2:客户端界面有输入框和显示框的界面
    V0.3:客户端关闭按钮可以关闭窗口
    V0.4:客户端按回车后输入框的文字显示在显示框
    V0.5:服务端能够起一个服务监听某端口
    V0.6:客户端能够连接到服务器端
    V0.7:客户端发一条消息,服务端能够接收到
    V0.8:服务器端能到接收多条来自客户端的消息,改善V0.7的只能接受一条消息
    V0.9:客户端关闭后,服务器端采取相应的措施,是服务器端不报错
    V1.0:服务器端可以连接多个客户端,并且可以接收到来自每个客户端发送的消息
    V1.1:服务器端把接收到每个客户端的消息发送出去
    V1.2:每个客户端都能接收来自服务器的数据,并显示在显示框
    V1.3:检查一些异常并修复(下面的代码就是这个版本)
    注:代码还有很多改进的地方,这只是一个简易的版本


    聊天服务器端代码:

    import java.io.*;
    import java.net.*;
    import java.util.*;
    
    public class ChatServer {
    	boolean started = false;
    	ServerSocket ss = null;
    	List<Client> clients = new ArrayList<Client>();
    	
    	public static void main(String[] args){		
    		new ChatServer().start(); //不能直接这样调用start()
    	}
    
    	public void start(){
    		try {
    			ss = new ServerSocket(8888); //在端口8888建立监听
    			started = true; //建立监听成功令此变量为真
    		}catch (SocketException e){
    			System.out.println("端口使用中。。。");
    			System.out.println("请关掉相关程序!");
    			System.exit(0);
    		}catch (IOException e) {
    			e.printStackTrace();
    		}
    		
    		try{
    			while(started){
    				Socket s = ss.accept(); //***重要***若此处报错,尝试用360进行LSP修复
    				Client c = new Client(s); //每接受一个客户端就会new一个线程 
    System.out.println("a client connected");
    				new Thread(c).start(); //启动线程,start()方法只是启动线程,而线程中的run()方法这是实现此线程功能的代码
    				clients.add(c);
    			}
    		}catch (IOException e) {
    			e.printStackTrace();
    		}finally{
    			try {
    				ss.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	//定义一个线程内部类
        class Client implements Runnable{
    		private Socket s;
    		private DataInputStream dis = null;
    		private DataOutputStream dos = null;
    		private boolean bConnected = false;
    		
    		public Client(Socket s){
    			this.s=s;
    			try {
    				dis = new DataInputStream(s.getInputStream());
    				dos = new DataOutputStream(s.getOutputStream());
    				bConnected = true; // 有一个客户端连上后另此变量为真
    			} catch (IOException e) {
    				e.printStackTrace();
    			}			
    		}
    
    		public void send(String str){
    			try {
    				dos.writeUTF(str);
    			} catch (IOException e) {
    				clients.remove(this);//防止有一个客户端退出了,其他客户端还在发消息,此时List里面还有这个退出的客户端,服务器就会报错
    				System.out.println("对方退出了,已经从List里面去除了!");
    			}
    		}
    		
    		public void run() {
    			try {
    				while (bConnected) {
    					String str = dis.readUTF(); //readUTF()是一个阻塞式的,main方法执行到此处就一直一行一行的读,程序停在此处
    System.out.println(str);
    					for(int i = 0; i < clients.size(); i++){
    						Client c = clients.get(i);
    						c.send(str);
    					}
    				}
    			}catch(EOFException e){
    				System.out.println("Client closed!");
    			}catch (IOException e) {
    				e.printStackTrace();
    			}finally{
    				try {
    					if(dis != null) dis.close();
    					if(dos != null) dos.close();
    					if(s != null) s.close();
    				} catch (IOException e1) {
    					e1.printStackTrace();
    				}
    			}
    		}
    	}
        
    } 


    聊天客户端代码:

    import java.awt.*;
    import java.awt.event.*;
    import java.io.*;
    import java.net.*;
    //客户端ChatClient
    public class ChatClient extends Frame{
    	//响应他的事件,需要去访问他的内容,所以定义为成员变量
    	TextField tfTxt = new TextField();
    	TextArea taContent = new TextArea();
    	Socket s = null;
    	DataOutputStream dos = null;
    	DataInputStream dis = null;
    	private boolean bConnected = false;
    	Thread tRecv = new Thread(new RecvThread());
    
    
    	public static void main(String[] args) {
    		new ChatClient().launchFrame();
    
    	}
    	
    	//运行客户端窗口的方法
    	public void launchFrame(){
    		this.setLocation(400,300);
    		setSize(300,300);
    		add(tfTxt,BorderLayout.SOUTH); //BorderLayout为Frame默认布局管理器。调用其他应调用setLayout(某个布局管理器对象)                         
    		add(taContent,BorderLayout.NORTH);
    		pack(); //如果没有pack();则两个控件中有空的部分
    		//使用匿名类添加窗口的事件监听器,注意WindowAdapter()的使用,windowClosing将要关闭窗口
    		this.addWindowListener(new WindowAdapter() {
    			public void windowClosing(WindowEvent e) {
    				disconnect();
    				System.exit(0);
    			}
    		});
    		//把监听器添加在文本框上,此时回车直接返回对象的信息,不需要添加Enter的KeyListener
    		tfTxt.addActionListener(new TFListener());
    		setVisible(true);
    		connect();
    		tRecv.start();
    	}
    	
    	public void connect(){
    		try {
    		s = new Socket("127.0.0.1",8888);
    		dos = new DataOutputStream(s.getOutputStream());
    		dis = new DataInputStream(s.getInputStream());
    System.out.println("connected!");
    		bConnected = true;
    		} catch (UnknownHostException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	} 
    	
    	public void disconnect() {
    		try {
    			dos.close();
    			dis.close();
    			s.close();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    		
    	private class TFListener implements ActionListener {
    		public void actionPerformed(ActionEvent e) {
    			String str = tfTxt.getText().trim(); //String里面的方法trim()去掉两边的空格
    			tfTxt.setText(""); //设置一个空字符串,让文本框里面的内容为空
    			try {
    				dos.writeUTF(str);
    				dos.flush();
    			} catch (IOException e1) {
    				e1.printStackTrace();
    			}
    		}
    	}
    	
    	private class RecvThread implements Runnable {
    		@Override
    		public void run() {
    			try {
    				while (bConnected) {
    					String str = dis.readUTF();
    					taContent.setText(taContent.getText() + str + '
    ');
    				}
    			} catch(SocketException e){
    				System.out.println("退出了,bye!");
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}	
    	}
    }

    您的资助是我最大的动力!
    金额随意, 欢迎来赏!

    文章出处:http://www.cnblogs.com/oldinaction/ (1)可关注微信公众号:【AEZO】获取更多信息 (2)微信公众号/小程序交流QQ群:303522792(验证码:cnblogs)。

    如果,想给予我更多的鼓励,求打

    因为,我的写作热情也离不开您的肯定支持,感谢您的阅读,【小易Smalle】!

  • 相关阅读:
    68
    56
    Django manager 命令笔记
    Django 执行 manage 命令方式
    Django 连接 Mysql (8.0.16) 失败
    Python django 安装 mysqlclient 失败
    H.264 SODB RBSP EBSP的区别
    FFmpeg—— Bitstream Filters 作用
    MySQL 远程连接问题 (Windows Server)
    MySQL 笔记
  • 原文地址:https://www.cnblogs.com/oldinaction/p/5167501.html
Copyright © 2011-2022 走看看