Java Socket TCP 通信,实现聊天室,服务器端指定客户端发送消息

效果图:

李四先进入聊天室,张三也在,然后王五加入;

参考地址:Java多线程机交互

额,原代码在客户端显示谁谁谁发送消息有个bug。。。

算了,直接上代码吧!

服务器端:

package testTCP;

import java.net.*;

import java.util.ArrayList;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.FlowLayout;

import java.io.*;

import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.JScrollPane;

import javax.swing.JTextArea;

public class TCPServer extends JFrame {

	public static JTextArea centerTextArea = new JTextArea();

	private JPanel southPanel, bottompanel;

	public List<Client> list = new ArrayList<Client>();

	ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
	

	public void setui() throws Exception {

		ServerSocket ss = new ServerSocket(5678);// 创建一个Socket服务器,监听5566端口

		// TODO Auto-generated constructor stub

		setTitle("服务器");

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		setSize(400, 400);

		setResizable(false); // 窗口大小不可调整

		setLocationRelativeTo(null);// 窗口剧中

		// 窗口的center部分

		centerTextArea.setEditable(false);

		centerTextArea.setBackground(new Color(211, 211, 211));

		// 窗口的SHOTU部分

		southPanel = new JPanel(new BorderLayout());

		bottompanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));

		southPanel.add(bottompanel, BorderLayout.SOUTH);

		add(new JScrollPane(centerTextArea), BorderLayout.CENTER);

		add(southPanel, BorderLayout.SOUTH);

		setVisible(true);

		while (true) {

			Socket s = ss.accept();// 利用Socket服务器的accept()方法获取客户端Socket对象。

			addclient(s);

			System.out.println(list.size());

		}

	}

	// 添加客户端

	private void addclient(Socket s) {
		String name1 = "";

		try {

			BufferedReader in = new BufferedReader(new InputStreamReader(

					s.getInputStream()));

			name1 = in.readLine();

		} catch (IOException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		}

		Client c = new Client(name1, s);// 创建客户端处理线程对象

		list.add(c);

		//Thread t = new Thread(c);// 创建客户端处理线程

		//t.start();// 启动线程
		cachedThreadPool.execute(c);

	}

	// 客户端处理线程类(实现Runnable接口)

	class Client implements Runnable {

		String name;// 客户端名字

		Socket s = null;// 保存客户端Socket对象

		BufferedReader in;

		PrintWriter out;

		Client(String name, Socket s) {

			this.s = s;

			this.name = name;

			try {

				in = new BufferedReader(new InputStreamReader(

						s.getInputStream()));

				out = new PrintWriter(s.getOutputStream());

				centerTextArea.append("客户端" + name + "连接成功\n");

				centerTextArea.setCaretPosition(centerTextArea.getText().length());

			} catch (IOException e) {

				// TODO Auto-generated catch block

				e.printStackTrace();

			}

		}

		public void run() {

			try {

				while (true) {

					String str = in.readLine();

					for (int j = 0; j < list.size(); j++) {

						Client c = list.get(j);

						if (c != this) {

							System.out.println(str);

							c.send(str+"-|1|2|-"+name);

						}

					}

					centerTextArea.append(name + "发出消息:" + str + "\n");

					centerTextArea.setCaretPosition(centerTextArea.getText().length());

					if (str.equals("end"))

						break;

				}

				try {

					s.close();

				} catch (IOException e) {

					// TODO Auto-generated catch block

					e.printStackTrace();

				}

			} catch (IOException e1) {

				// TODO Auto-generated catch block

				e1.printStackTrace();

			}

		}

		public void send(String str) {

			//out.println("客户端  " + name + "说:" + str);
			out.println(str);

			out.flush();

		}

	}

	public static void main(String[] args) throws Exception {

		// 利用死循环不停的监听端口

		TCPServer tc = new TCPServer();

		tc.setui();

	}

}

客户端:

package testTCP;

import java.net.*;

import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.FlowLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.*;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JOptionPane;

import javax.swing.JPanel;

import javax.swing.JScrollPane;

import javax.swing.JTextArea;

import javax.swing.JTextField;

public class TCPClient extends JFrame {

	private JLabel stateLB;

	private JTextArea centerTextArea, inputTextArea;

	private JPanel southPanel, bottompanel;

	private JTextField ipTextField, remotePortTF;

	private JButton sendBT, clearBT;

	static Socket server;

	String name = null;

	PrintWriter out;

	BufferedReader in;

	Thread receive = new Thread(new receiveThread());

	public TCPClient() throws Exception {

		// 此处向服务器发送请求

		server = new Socket(InetAddress.getLocalHost(), 5678);

		in = new BufferedReader(new InputStreamReader(server.getInputStream()));

		out = new PrintWriter(server.getOutputStream());

		// 建立接收

		receive.start();

	}

	public void setUpUI() {// 初始化面板

		setTitle("客户端");

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		setSize(400, 400);

		setResizable(false); // 窗口大小不可调整

		setLocationRelativeTo(null);// 窗口剧中

		stateLB = new JLabel("未链接");

		stateLB.setHorizontalAlignment(JLabel.RIGHT);

		// 窗口的center部分

		centerTextArea = new JTextArea();

		centerTextArea.setEditable(false);

		centerTextArea.setBackground(new Color(211, 211, 211));

		// 窗口的SHOTU部分

		southPanel = new JPanel(new BorderLayout());

		inputTextArea = new JTextArea(5, 20);

		bottompanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));

		ipTextField = new JTextField("127.0.0.1", 8);

		sendBT = new JButton("发送");

		clearBT = new JButton("清屏");

		bottompanel.add(ipTextField);

		bottompanel.add(sendBT);

		bottompanel.add(clearBT);

		southPanel.add(new JScrollPane(inputTextArea), BorderLayout.CENTER);

		southPanel.add(bottompanel, BorderLayout.SOUTH);

		add(stateLB, BorderLayout.NORTH);

		add(new JScrollPane(centerTextArea), BorderLayout.CENTER);

		add(southPanel, BorderLayout.SOUTH);

		setVisible(true);

		name = JOptionPane.showInputDialog(this, "请输入客户端的名字", JOptionPane.QUESTION_MESSAGE);

		setTitle(name);

		out.println(name);

		out.flush();

		stateLB.setText(name + "已链接");

	}

	public void setListener() {// 发送消息

		sendBT.addActionListener(new ActionListener() {

			@Override

			public void actionPerformed(ActionEvent e) {

				// TODO Auto-generated method stub

				String sendcontent = inputTextArea.getText();

				centerTextArea.append("你说:"+inputTextArea.getText() + "\n");

				centerTextArea.setCaretPosition(centerTextArea.getText().length());

				inputTextArea.setText("");

				try {

					out.println(sendcontent);

					out.flush();

					if (sendcontent.equals("end")) {

						server.close();

					}

				} catch (Exception e2) {

					// TODO: handle exception

					JOptionPane.showMessageDialog(TCPClient.this, "出错了发送不成功");

					e2.printStackTrace();

				}

				clearBT.addActionListener(new ActionListener() {

					@Override

					public void actionPerformed(ActionEvent e) {

						centerTextArea.setText("");// 清空面板聊天记录

					}

				});

			}

		});

	}

	class receiveThread implements Runnable {

		// 接收线程

		@Override

		public void run() {

			// TODO Auto-generated method stub

			try {

				while (server != null)

				{

					String info = in.readLine();
					
					System.out.println(info);

					String message = info.split("-\\|1\\|2\\|-")[0];
					
					String name = info.split("-\\|1\\|2\\|-")[1];

					centerTextArea.append(name + "说:" + message + "\n");

					centerTextArea.setCaretPosition(centerTextArea.getText().length());

				}

			} catch (Exception e) {

				// TODO: handle exception

			}

		}

	}

	public static void main(String[] args) throws Exception {
		
			TCPClient c = new TCPClient();

			c.setUpUI();

			c.setListener();
		

		

	}

}

服务器指定客户端发送消息:

这里我为了在单机上多开客户端,把代码中根据IP来分客户端换成了根据时间线来分客户端,这些都是小问题自行修改~

服务器端代码:

package MyTCP;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import tcp.Client;

public class MyTCPServer implements Runnable {

	Map<String, Client> clientMap = new ConcurrentHashMap<String, Client>();
	// 可缓存线程池 - 建客户端处理线程池
	ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

	// 可缓存线程池 - 建客户端处理线程池
	ExecutorService keepAliveThreadPool = Executors.newCachedThreadPool();
	
	/**
	 * 
	 * @param checkDelay 检查时间:单位毫秒
	 * @param keepAliveDelay 心跳时间:单位秒
	 */
	public MyTCPServer(long checkDelay, long keepAliveDelay) {
		keepAliveThreadPool.execute(new KeepAliveWatchDog(checkDelay, keepAliveDelay*1000));
	}
	
	public MyTCPServer() {
		keepAliveThreadPool.execute((new KeepAliveWatchDog(10, 15*1000)));
	}
	
	public MyTCPServer(boolean keepAlive) {
		if (keepAlive) {
			//keepAliveThreadPool.execute((new Thread(new KeepAliveWatchDog(10, 10 * 1000))));
			keepAliveThreadPool.execute((new KeepAliveWatchDog(10, 10 * 1000)));
		}
	}

	public static void main(String[] args) {
		MyTCPServer server = new MyTCPServer(true);
		Thread t = new Thread(server);// 创建客户端处理线程
		t.start();// 启动线程
		@SuppressWarnings("resource")
		Scanner scan = new Scanner(System.in);
		while (true) {
			System.out.println("输入操作:");
			String op = scan.nextLine();
			if ("1".equals(op)) {
				for (String key : server.clientMap.keySet()) {
					System.out.println("key = " + key);
				}
				for (Client value : server.clientMap.values()) {
					System.out.println("Client = " + value.toString());
				}
			} else {
				System.out.println("输入地址");
				String ClientIp = scan.nextLine();
				System.out.println("输入地址是:" + ClientIp);
				System.out.println("输入消息");
				String message = scan.nextLine();
				System.out.println("输入消息:" + message);

				Client c = server.clientMap.get(ClientIp);
				if(c!=null){
					c.send(ClientIp, message);
				}else{
					System.out.println("该链接不存在!");
				}
				
			}

		}
	}

	@SuppressWarnings("resource")
	@Override
	public void run() {
		ServerSocket ss = null;
		try {
			ss = new ServerSocket(5678);// 创建一个Socket服务器,监听5566端口
			System.out.println("服务已启动。。。");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		while (true) {
			Socket client = null;
			try {
				client = ss.accept();
				addclient(client);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}

	// 添加客户端
	private void addclient(Socket s) {
		// 第一次收到客户端信息
		/*
		 * String message; try { BufferedReader in = new BufferedReader(new
		 * InputStreamReader( s.getInputStream())); message = in.readLine(); }
		 * catch (IOException e) { e.printStackTrace(); }
		 */
		Client c = new Client(s);// 创建客户端处理线程对象
		// clientMap.put(s.getInetAddress().toString().replace("/", ""), c);
		clientMap.put(System.currentTimeMillis() + "", c);

		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// Thread t = new Thread(c);// 创建客户端处理线程
		// t.start();// 启动线程
		cachedThreadPool.execute(c);

		// 遍历
		/*
		 * for (String key : clientMap.keySet()) { System.out.println("key = " +
		 * key); } for (Client value : clientMap.values()) { System.out.println(
		 * "Client = " + value.toString()); }
		 */

	}

	class Client implements Runnable {
		Socket s = null;// 保存客户端Socket对象
		BufferedReader in;
		PrintWriter out;
		String toClient;
		String ClientIp;
		boolean send = false;

		Client(Socket s) {
			this.s = s;
			try {
				in = new BufferedReader(new InputStreamReader(s.getInputStream()));
				out = new PrintWriter(s.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();

			}
		}

		void send(String ClientIp, String message) {
			this.ClientIp = ClientIp;
			this.toClient = message;
			send = true;
			if (send) {
				Client c = clientMap.get(ClientIp);
				if (c == null) {
					System.out.println(ClientIp + "连接不存在!");
				}

				try {
					c.send(toClient);
				} catch (Exception e) {
					e.printStackTrace();
					c.close();
				}

			}
			send = false;
		}

		@Override
		public void run() {

			while (true) {
				try {
					String str = null;
					// str = in.readLine();
					if ((str = in.readLine()) != null) {
						System.out.println(s.getInetAddress().toString().replace("/", "") + ":" + str);
					}
				} catch (Exception e) {
					// e.printStackTrace();
					close();
					break;
				}

				/*
				 * if (send) { Client c = clientMap.get(ClientIp);
				 * c.send(toClient); } send = false;
				 */
			}
		}

		public void send(String str) {
			out.println(str);
			if (!"KeepAlive".equals(str))
				System.out.println("send:" + str);
			out.flush();

		}

		public void close() {
			try {
				s.close();
				in.close();
				;
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			clientMap.remove(s.getInetAddress().toString().replace("/", ""));
			System.out.println(s.getInetAddress().toString().replace("/", "") + "连接已关闭!");
		}

	}

	class KeepAliveWatchDog implements Runnable {
		long lastSendTime;
		long checkDelay = 10;
		long keepAliveDelay = 10000;

		
		/**
		 * 
		 * @param checkDelay 检查时间
		 * @param keepAliveDelay 心跳时间
		 */
		public KeepAliveWatchDog(long checkDelay, long keepAliveDelay) {
			this.checkDelay = checkDelay;
			this.keepAliveDelay = keepAliveDelay;
		}

		@Override
		public void run() {
			while (true) {
				if (System.currentTimeMillis() - lastSendTime > keepAliveDelay) {
					for (String ClientIp : clientMap.keySet()) {
						Client c = clientMap.get(ClientIp);
						c.send("KeepAlive");
					}
					lastSendTime = System.currentTimeMillis();
				} else {
					try {
						Thread.sleep(checkDelay);
					} catch (InterruptedException e) {
						e.printStackTrace();

					}
				}
			}
		}
	}

}

客户端,略微修改:

package MyTCP;

import java.net.*;

import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.FlowLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.*;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JOptionPane;

import javax.swing.JPanel;

import javax.swing.JScrollPane;

import javax.swing.JTextArea;

import javax.swing.JTextField;

public class MyTCPClient extends JFrame {

	private JLabel stateLB;

	private JTextArea centerTextArea, inputTextArea;

	private JPanel southPanel, bottompanel;

	private JTextField ipTextField, remotePortTF;

	private JButton sendBT, clearBT;

	static Socket server;

	String name = null;

	PrintWriter out;

	BufferedReader in;

	Thread receive = new Thread(new receiveThread());

	public MyTCPClient() throws Exception {

		// 此处向服务器发送请求

		server = new Socket(InetAddress.getLocalHost(), 5678);

		in = new BufferedReader(new InputStreamReader(server.getInputStream()));

		out = new PrintWriter(server.getOutputStream());

		// 建立接收

		receive.start();

	}

	public void setUpUI() {// 初始化面板

		setTitle("客户端");

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		setSize(400, 400);

		setResizable(false); // 窗口大小不可调整

		setLocationRelativeTo(null);// 窗口剧中

		stateLB = new JLabel("未链接");

		stateLB.setHorizontalAlignment(JLabel.RIGHT);

		// 窗口的center部分

		centerTextArea = new JTextArea();

		centerTextArea.setEditable(false);

		centerTextArea.setBackground(new Color(211, 211, 211));

		// 窗口的SHOTU部分

		southPanel = new JPanel(new BorderLayout());

		inputTextArea = new JTextArea(5, 20);

		bottompanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));

		ipTextField = new JTextField("127.0.0.1", 8);

		sendBT = new JButton("发送");

		clearBT = new JButton("清屏");

		bottompanel.add(ipTextField);

		bottompanel.add(sendBT);

		bottompanel.add(clearBT);

		southPanel.add(new JScrollPane(inputTextArea), BorderLayout.CENTER);

		southPanel.add(bottompanel, BorderLayout.SOUTH);

		add(stateLB, BorderLayout.NORTH);

		add(new JScrollPane(centerTextArea), BorderLayout.CENTER);

		add(southPanel, BorderLayout.SOUTH);

		setVisible(true);

		stateLB.setText(name + "已链接");

	}

	public void setListener() {// 发送消息

		sendBT.addActionListener(new ActionListener() {

			@Override

			public void actionPerformed(ActionEvent e) {

				// TODO Auto-generated method stub

				String sendcontent = inputTextArea.getText();

				centerTextArea.append("你说:" + inputTextArea.getText() + "\n");

				centerTextArea.setCaretPosition(centerTextArea.getText().length());

				inputTextArea.setText("");

				try {

					out.println(sendcontent);

					out.flush();

					if (sendcontent.equals("end")) {

						server.close();

					}

				} catch (Exception e2) {

					// TODO: handle exception

					JOptionPane.showMessageDialog(MyTCPClient.this, "出错了发送不成功");

					e2.printStackTrace();

				}

				clearBT.addActionListener(new ActionListener() {

					@Override

					public void actionPerformed(ActionEvent e) {

						centerTextArea.setText("");// 清空面板聊天记录

					}

				});

			}

		});

	}

	class receiveThread implements Runnable {

		// 接收线程

		@Override

		public void run() {

			// TODO Auto-generated method stub

			try {

				while (server != null)

				{

					String info = in.readLine();

					centerTextArea.append(info + "\n");

					centerTextArea.setCaretPosition(centerTextArea.getText().length());

				}

			} catch (Exception e) {

				// TODO: handle exception

			}

		}

	}

	public static void main(String[] args) throws Exception {

		MyTCPClient c = new MyTCPClient();

		c.setUpUI();

		c.setListener();

	}

}

运行效果:



keepAlive是心跳包。。

输入操作字符1是查看所有客户端;

其他就是制定客户端发送消息;



当然,这里关闭客户端的时候代码没改。。。是根据IP来关闭客户端的,所以关闭后仍可以在服务器上看到;

把根据时间线的代码换成原来的就好;


这时把客户端关闭:


好,基本这样~


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值