Java代码实现通信工具server端(TCP连接, 多线程,GUI界面操作)

此server端代码可以分别在console运行和或者GUI界面运行。如果需要开发其他测试工具,比如一个模拟第三方的测试桩,可以根据代码做修改协议和添加交互消息。
GUI界面继承JFrame 类。
运行效果如图:
Java代码实现通信工具server端(TCP连接, 多线程,GUI界面操作)_第1张图片

ServerGUI.class
GUI使用JTextArea。使用BorderLayout布局,EAST有一个JTextField控件显示服务器要监听的端口号和两个开始/停止服务器的按键。CENTER区域有两个JScrollPane控件分别包含了一个JTextArea控件。第一个JTextArea控件显示聊天室的消息,客户端可见。第二个JTextArea控件显示事件消息:谁登录了,谁退出了,错误信息等。

    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    //这是一个GUI方式的server
    public class ServerGUI extends JFrame implements ActionListener, WindowListener {
    	
    	private static final long serialVersionUID =1L;
    	private JButton stopStart;
    	//JTextArea 的聊天室和事件
    	private JTextArea chatWindow,eventWindow;
    	//端口号
    	private JTextField tPortNumber;
    	private Server server;
    	//server构造,端口用于接收连接
    	ServerGUI(int port){
    		super("QQ Server");
    		server=null;
    		// 开关键和端口号在窗口右侧
    		JPanel ServerWindow=new JPanel();
    		ServerWindow.add(new JLabel("Port number: "));
    		ServerWindow.setPreferredSize(new Dimension(150, 50));
    		tPortNumber=new JTextField(" "+port);
    		tPortNumber.setBackground(Color.GREEN);
    		ServerWindow.add(tPortNumber);
    		//to stop or start the server, we start with "Start"
    		stopStart=new JButton("开始监听");
    		stopStart.addActionListener(this);
    		ServerWindow.add(stopStart);
    		add(ServerWindow,BorderLayout.EAST);
    		// 聊天窗口
    		JPanel center=new JPanel(new GridLayout(2,6));
    		chatWindow=new JTextArea(40,40);
    		chatWindow.setEditable(false);
    		appendRoom("聊天室:\n");
    		center.add(new JScrollPane(chatWindow));
    		//事件窗口
    		eventWindow=new JTextArea(50,50);
    		eventWindow.setEditable(false);
    		appendEvent("事件日志:\n");
    		center.add(new JScrollPane(eventWindow));
    		add(center);
    		// need to be informed when the user click the close button on the frame
    		addWindowListener(this);
    		setSize(600,400);
    		setVisible(true);
    	}
        // 添加并显示消息到如下两个消息窗口JTextArea
        // position at the end
    	void appendRoom(String str) {
    		chatWindow.append(str);
    		chatWindow.setCaretPosition(chatWindow.getText().length()-1);
    	}
    	void appendEvent(String str) {
    		eventWindow.append(str);
    		eventWindow.setCaretPosition(chatWindow.getText().length()-1);
    	}
    	//点击开启和关闭的按键
    	public void actionPerformed(ActionEvent e) {
    		if(server!=null) {//server在运行 状态,先停止
    			server.stop();
    			server=null;
    			tPortNumber.setEditable(true);
    			stopStart.setText("开始监听");
    			return;
    		}
    		//启动server
    		int port;
    		try {
    			port = Integer.parseInt(tPortNumber.getText().trim());
    		}catch(Exception er) {
    			appendEvent("Invalid port number.");
    			return;
    		}
    		//创建一个新server
    		server=new Server(port,this);
    		new ServerRunning().start();//以一个线程的方式启动server
    		stopStart.setText("停止监听");
    		tPortNumber.setEditable(false);
    	}
    	//启动server的入口
    	public static void main(String[] arg) {
    		new ServerGUI(2500);
    	}
    	//一个启动server的线程
    	class ServerRunning extends Thread{
    		public void run() {
    			server.start();//一直执行直到启动失败
    			stopStart.setText("开始监听");
    			tPortNumber.setEditable(true);
    			appendEvent("Server Crashed.\n");
    			server=null;
    		}
    	}
    	//如果用户点击关闭,需要释放端口
    	public void windowClosing(WindowEvent e) {
    		//如果server存在
    		if(server!=null) {
    			try {
    				server.stop();
    			}catch (Exception eClose) {
    			}
    			server=null;
    		}
    		//dispose the frame
    		dispose();
    		System.exit(0);
    	}
    	//忽略其他WindowListener的方法
    	public void windowClosed(WindowEvent e) {}
    	public void windowOpened(WindowEvent e) {}
        public void windowIconified(WindowEvent e) {}
        public void windowDeiconified(WindowEvent e) {}
        public void windowActivated(WindowEvent e) {}
        public void windowDeactivated(WindowEvent e) {}
    }

说明:extends是继承父类,只要那个类不是声明为final或者那个类定义为abstract的就能继承,JAVA中不支持多重继承,但是可以用接口来实现,这样就要用到implements,继承只能继承一个类,但implements可以实现多个接口,用逗号分开就行了 ,比如 class A extends B implements C,D,E

Server.class
如下为控制台console运行的server
首先你可以输入以下的命令来执行服务器程序:

java Server

它会运行在命令行模式,并且监听2500端口,等待连接。如果你想使用其他的端口,可以附加端口参数:

java Server 1200

代码中id=++uniqueId;为唯一标示ID。(a++是先执行运算或比较再赋值增加1,而++a是先赋值增加1,再运算比较)

import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.*;
public class Server {
	private static int uniqueId;
	//保存客户端的数组
	private ArrayList<ClientThread> al;
	private ServerGUI sg;
	private SimpleDateFormat sdf;//显示时间
	private int port;
	private boolean keepGoing;//是否停止server开关
	private Object String;
	
	/** 构造server的console端口参数 */
	public Server(int port) {
		this(port,null);
	}
	public Server(int port,ServerGUI sg) {
		//判断是否为GUI
		this.sg=sg;
		this.port=port;
		sdf=new SimpleDateFormat("HH:mm:ss");
		//数组存放客户端client list
		al=new ArrayList<ClientThread>();
	}
	public void start() {
		keepGoing=true;
		//创建socket server 等待连接请求
		try {//server使用的socket
			ServerSocket serverSocket=new ServerSocket(port);
			//无线循环 等待连接
			while(keepGoing) {
				//格式化消息 提示正在等待连接
				display("Server is listenning on port "+port+" for new connections.");
				Socket socket=serverSocket.accept();//接收连接
				if(!keepGoing)
					break;
				ClientThread t=new ClientThread(socket);//给连接创建一个线程
				al.add(t);//在数组list中保存线程
				t.start();
			}
			//被要求关闭
			try {
				serverSocket.close();
				for(int i=0;i<al.size();++i) {
					ClientThread tc=al.get(i);
					try {
						tc.sInput.close();
						tc.sOutput.close();
						tc.socket.close();
					}catch(IOException ioE) {
						//还没打算做啥
					}
				}
			}catch(Exception e) {
				display("关闭服务器或者客户端失败:"+e);
			}
		}catch(IOException e) {
			String msg=sdf.format(new Date())+"Exception on new ServerSocket: "+e+"\n";
			display(msg);
		}
	}
	/** 在GUI停止server */
	protected void stop() {
		keepGoing=false;
		try {
			new Socket("localhost",port);
		}catch(Exception e) {
			//预留操作
		}
	}
	
	/** 展示一个事件到console或者GUI */
	private void display(String msg) {
		String time = sdf.format(new Date())+" "+msg;
		//String sss=new String ("hello world");
		String sss="aaa";
		if(sg==null)
			System.out.println(time);
		else
			sg.appendEvent(time+"\n");
	}
	/** 发布广播给所有客户端 */
	private synchronized void broadcast(String message) {
		//添加时间给广播消息
		String time=sdf.format(new Date());
		String messageLF=time+" "+message+"\n";
		//把消息在console或者GUI上显示
		if(sg==null)
			System.out.println(messageLF);
		else
			sg.appendRoom(messageLF);//在GUI的聊天窗口显示
		//用倒序方法发送消息,防止遇到断连的客户端,以便移除
		for(int i=al.size();--i>=0;) {
			ClientThread ct=al.get(i);
			//发送消息给client,如果无法发送 则移除client
			if(!ct.writeMsg(messageLF)) {
				al.remove(i);
				display("Disconnected Client " + ct.username + " removed from list.");
			}
		}
		
	}
	//当客户端使用logout消息退出登录
	synchronized void remove(int id) {
		//扫描数组,找到该id
		for(int i=0;i<al.size();i++) {
			ClientThread ct=al.get(i);
			if(ct.id==id) {
				al.remove(i);
				return;
			}
		}
	}
	/** 以console application的形式运行,只需要打开 一个console 窗口
	 * > java Server
	 * > java Server portNumber
	 * */
	public static void main(String[] args) {
		int portNumber=1500;
		switch(args.length) {
		case 1:
			try {
				portNumber=Integer.parseInt(args[0]);
			}catch(Exception e) {
				System.out.println("无效端口.");
				System.out.println("java Server [portNumber]");
				return;
			}
		case 0:
			break;
		default:
			System.out.println("java Server [portNumber]");
			return;
		}
		Server server=new Server(portNumber);
		server.start();
	}
	
	/** One instance of this thread will run for each client */
	class ClientThread extends Thread{
		//监听和讲话的socket
		Socket socket;
        ObjectInputStream sInput;
        ObjectOutputStream sOutput;
        int id;//my unique id
        String username;
        ChatMessage cm;
        String date;
        //构造函数
        ClientThread(Socket socket){
        	id=++uniqueId;//a++是先执行运算或比较再赋值增加1,而++a是先赋值增加1,再运算比较
        	this.socket=socket;
        	/* Creating both Data Stream 创建两个数据流*/
        	System.out.println("Thread trying to create Object Input/Output Streams");
        	try {
        		sOutput = new ObjectOutputStream(socket.getOutputStream());
        		sInput  = new ObjectInputStream(socket.getInputStream());
        		username=(String) sInput.readObject();//读取username
        		display(username+" successfully connected.");
        	}catch (IOException e) {
        		display("Exception creating new Input/output Streams: "+e);
        		return;
        	}//必须抓取ClassNotFoundException错误,but I read a String, I am sure it will work
        	catch (ClassNotFoundException e) {
        	}
        	date=new Date().toString()+"\n";
        }
        //永远run状态,直到logout
        public void run() {
        	boolean keepGoing=true;
        	while(keepGoing) {
        		//读取一个String
        		try {
        			cm=(ChatMessage) sInput.readObject();
        		}catch (IOException e) {
        			display(username+" Exception reading Streams:"+e);
        		}catch (ClassNotFoundException e2) {
        			break;
        		}
        		String message=cm.getMessage();
        		//打开开关
        		switch(cm.getType()) {
        		case ChatMessage.MESSAGE:
        			broadcast(username+": "+message);
        		case ChatMessage.LOGOUT:
        			display(username+" disconnected with server with Logout");
        			keepGoing = false;
        			break;
        		case ChatMessage.WHOISIN:
        			writeMsg("List of the users connected at " + sdf.format(new Date()) + "\n");
        			for(int i = 0; i < al.size(); ++i) {
                        ClientThread ct = al.get(i);
                        writeMsg((i+1) + ") " + ct.username + " since " + ct.date);
                    }
        			break;
        		}
        	}//从数组中移除
        	remove(id);
        	close();
        }//try to close everything
        private void close() {
        	//关闭连接
        	try {
        		if(sOutput!=null) sOutput.close();
        	}catch (Exception e) {}
        	try {
        		if(sInput!=null) sInput.close();
        	}catch (Exception e) {}
        	try {
                if(socket != null) socket.close();
            }
            catch (Exception e) {}
        }
        private boolean writeMsg(String msg){
        	//如果客户端一直在连接状态,则发送消息
        	if(!socket.isConnected()) {
        		close();
        		return false;
        	}//发送消息到stream
        	try {
        		sOutput.writeObject(msg);
        	}//如果发送失败,不用退出,直接提醒用户
        	catch(IOException e) {
        		display("Error sending message to "+username);
        		display(e.toString());
        	}
        	return true;
        }
	}
}

你可能感兴趣的:(Java代码实现通信工具server端(TCP连接, 多线程,GUI界面操作))