一个简易聊天功能的服务器端和客户端源码

本文来自:小易博客专栏。转载请注明出处: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 + '\n');
				}
			} catch(SocketException e){
				System.out.println("退出了,bye!");
			} catch (IOException e) {
				e.printStackTrace();
			}
		}	
	}
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值