`
piperzero
  • 浏览: 3470708 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

Java Socket编程 - 基于TCP方式的客户服务器聊天程序

 
阅读更多

本文讲演示如何通过Java Socket建立C/S方式的聊天程序。实现的功能

主要包括如下几个方面:

1. 用户登录,在线用户列表刷新

2.客户端与服务器的TCP连接,实现消息的发送与接受

3.Java Swing与多线程编程技巧

一个整体的Class关系图如下:


程序实现的服务器端UI如下:


一个JList组件用来显示在线的所有用户,一个JTextArea组件用来显示所有消息

记录。所有消息必须通过服务器端转发。点击【start】按钮启动服务器端监听

默认监听端口为9999。

启动服务器端的Action中的代码如下:

	Thread startThread = new Thread(new Runnable() {
                public void run() {
                	startServer(9999);
                }
            });
	startThread.start();
	startBtn.setEnabled(false);
	shutDownBtn.setEnabled(true);
startServer()的代码如下:

private void startServer(int port) {
	try {
		serverSocket = new ServerSocket(port);
		System.out.println("Server started at port :" + port);
		while(true) {
			Socket client = serverSocket.accept(); // blocked & waiting for income socket
			System.out.println("Just connected to " + client.getRemoteSocketAddress());
			DataInputStream bufferedReader = new DataInputStream(client.getInputStream());
			byte[] cbuff = new byte[256];
			int size = bufferedReader.read(cbuff);
			char[] charBuff = convertByteToChar(cbuff, size);
			String userName = String.valueOf(charBuff);
			ChatServerClientThread clentThread = new ChatServerClientThread(userName, client, this);
			clientList.add(clentThread);
			userNameList.add(userName);
			clentThread.start();
			updateUserList();
		}

	} catch (IOException e) {
		e.printStackTrace();
	}
}
简单协议规则:

1.任何消息发送完以后系统自动加上结束标志EOF

2.接受到用户消息以后通过解析EOF来完成消息传递

3.自动发送更新用户列表到所有客户端当有新客户登入时

为什么我要实现上述简单协议,其实任何网络通信都是基于协议实现

只有基于协议实现才可控制,可检查。协议是网络通信的最重要一环。

客户端UI设计如下:

一个自定义的JPanel实现背景渐进颜色填充。

- Message组件用来接受用户输入的聊天信息

- Friend List 会自动刷新用户列表,当有新用户登录时候

- History Record用来显示聊天记录

- 【Connect】点击连接到Server端,前提是必须填写设置中全部,默认

的机器IP为127.0.0.1端口为9999

- 【send】按钮点击会发送用户输入的消息到指定的其它客户端。如果

没有选择用户,则发送到服务器端。

- 一次发送消息的大小不得大于200个字节。

完整的客户端代码如下:

package com.gloomyfish.socket.tutorial.chat;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import com.gloomyfish.custom.swing.ui.CurvedGradientPanel;

public class ChatClient extends JFrame implements ActionListener {
	public final static String CONNECT_CMD = "Connect";
	public final static String DISCONNECT_CMD = "Disconnect";
	public final static String SEND_CMD = "Send";
	public final static String END_FLAG = "EOF";

	/**
	 * 
	 */
	private static final long serialVersionUID = 5837742337463099673L;
	private String winTitle;
	private JLabel userLabel;
	private JLabel passwordLabel;
	private JLabel ipLabel;
	private JLabel portLabel;
	
	// text field
	private JTextField userField;
	private JPasswordField passwordField;
	private JTextField ipField;
	private JTextField portField;
	
	private JList friendList;
	private JTextArea historyRecordArea;
	private JTextArea chatContentArea;
	
	// buttons
	private JButton connectBtn;
	private JButton disConnectBtn;
	private JButton sendBtn;
	private JCheckBox send2AllBtn;
	
	// socket
	private Socket mSocket;
	private SocketAddress address;
	private ChatClientThread m_client;
	
	public ChatClient() {
		super("Chat Client");
		initComponents();
		setupListener();
	}
	
	private void initComponents() {
		JPanel settingsPanel = new CurvedGradientPanel();
		JPanel chatPanel = new CurvedGradientPanel();
		GridLayout gy = new GridLayout(1,2,10,2);
		getContentPane().setLayout(gy);
		getContentPane().add(settingsPanel);
		getContentPane().add(chatPanel);
		
		// set up settings info
		settingsPanel.setLayout(new BorderLayout());
		settingsPanel.setOpaque(false);
		JPanel gridPanel = new JPanel(new GridLayout(4, 2));
		gridPanel.setBorder(BorderFactory.createTitledBorder("Server Settings & User Info"));
		gridPanel.setOpaque(false);
		userLabel = new JLabel("User Name:");
		passwordLabel = new JLabel("User Password:");
		ipLabel = new JLabel("Server IP Address:");
		portLabel = new JLabel("Server Port");
		userLabel.setOpaque(false);
		passwordLabel.setOpaque(false);
		ipLabel.setOpaque(false);
		portLabel.setOpaque(false);
		userField = new JTextField();
		passwordField = new JPasswordField();
		ipField = new JTextField();
		portField = new JTextField();
		connectBtn = new JButton(CONNECT_CMD);
		disConnectBtn = new JButton(DISCONNECT_CMD);
		JPanel btnPanel = new JPanel();
		btnPanel.setOpaque(false);
		btnPanel.setLayout(new FlowLayout());
		btnPanel.add(connectBtn);
		btnPanel.add(disConnectBtn);
		
		gridPanel.add(userLabel);
		gridPanel.add(userField);
		gridPanel.add(passwordLabel);
		gridPanel.add(passwordField);
		gridPanel.add(ipLabel);
		gridPanel.add(ipField);
		gridPanel.add(portLabel);
		gridPanel.add(portField);
		friendList = new JList();
		JScrollPane friendPanel = new JScrollPane(friendList);
		friendPanel.setOpaque(false);
		friendPanel.setBorder(BorderFactory.createTitledBorder("Friend List:"));
		settingsPanel.add(btnPanel, BorderLayout.SOUTH);
		settingsPanel.add(gridPanel, BorderLayout.NORTH);
		settingsPanel.add(friendPanel,BorderLayout.CENTER);
		
		chatPanel.setLayout(new GridLayout(3,1));
		chatPanel.setOpaque(false);
		historyRecordArea = new JTextArea();
		JScrollPane histroyPanel = new JScrollPane(historyRecordArea);
		histroyPanel.setBorder(BorderFactory.createTitledBorder("Chat History Record:"));
		histroyPanel.setOpaque(false);
		chatContentArea = new JTextArea();
		JScrollPane messagePanel = new JScrollPane(chatContentArea);
		messagePanel.setBorder(BorderFactory.createTitledBorder("Message:"));
		messagePanel.setOpaque(false);
		// chatPanel.add(friendPanel);
		chatPanel.add(histroyPanel);
		chatPanel.add(messagePanel);
		sendBtn = new JButton(SEND_CMD);
		send2AllBtn = new JCheckBox("Send to All online Users");
		send2AllBtn.setOpaque(false);
		JPanel sendbtnPanel = new JPanel();
		sendbtnPanel.setOpaque(false);
		sendbtnPanel.setLayout(new FlowLayout());
		sendbtnPanel.add(sendBtn);
		sendbtnPanel.add(send2AllBtn);
		chatPanel.add(sendbtnPanel);
	}
	
	private void setupListener() {
		connectBtn.addActionListener(this);
		disConnectBtn.addActionListener(this);
		sendBtn.addActionListener(this);
		disConnectBtn.setEnabled(false);
	}
	
	/**
	 * <p></p>
	 * 
	 * @param content - byte array
	 * @param bsize - the size of bytes
	 */
	public synchronized void handleMessage(char[] content, int bsize) {
		// char[] inputMessage = convertByteToChar(content, bsize);
		String receivedContent = String.valueOf(content);
		int endFlag = receivedContent.indexOf(END_FLAG);
		receivedContent = receivedContent.substring(0, endFlag);
		System.out.println("Client " + userField.getText() + " Message:" + receivedContent);
		if(receivedContent.contains("#")) {
			String[] onlineUserList = receivedContent.split("#");
			friendList.setListData(onlineUserList);
		} else {
			// just append to chat history record...
			appendHistoryRecord(receivedContent + "\r\n");
		}
	}
	
	public synchronized void appendHistoryRecord(String record) {
		historyRecordArea.append(record);
	}
	
	private String getSelectedUser() {
		int index = friendList.getSelectedIndex();
		if(index >= 0) {
		String user = (String)friendList.getSelectedValue();
		return user;
		} else {
			return "Server";
		}
	}
	
//	private char[] convertByteToChar(byte[] cbuff, int size) {
//		char[] charBuff = new char[size];
//		for(int i=0; i<size; i++) {
//			charBuff[i] = (char)cbuff[i];
//		}
//		return charBuff;
//	}
	
	public void setTitle(String title) {
		winTitle = title;
		super.setTitle(winTitle);
	}
	
	public String getTitle() {
		return super.getTitle();
	}
	
	public static void main(String[] args) {
		ChatClient client = new ChatClient();
		client.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		client.pack();
		client.setVisible(true);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if(SEND_CMD.equals(e.getActionCommand())) {
			String chatContent = chatContentArea.getText();
			if(checkNull(chatContent)) {
				JOptionPane.showMessageDialog(this, "Please enter the message at least 6 characters!");
				return;
			} else if(chatContent.getBytes().length > 200) {
				JOptionPane.showMessageDialog(this, "The length of the message must be less than 200 characters!");
				return;
			}
			try {
				m_client.dispatchMessage(getSelectedUser() + "#" + chatContent);
				m_client.dispatchMessage(END_FLAG);
				appendHistoryRecord("me :" + chatContent + "\r\n");
				chatContentArea.setText(""); // try to clear user enter......
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		} else if(DISCONNECT_CMD.equals(e.getActionCommand())) {
			enableSettingsUI(true);
		} else if(CONNECT_CMD.equals(e.getActionCommand())) {
			String serverHostName = ipField.getText();
			String portStr = portField.getText();
			String userName = userField.getText();
			char[] password = passwordField.getPassword();
			System.out.println("Password = " + password.length);
			if(checkNull(serverHostName) || checkNull(portStr) || checkNull(userName)) {
				JOptionPane.showMessageDialog(this, "Please enter user name, server host name, server port!");
				return;
			}
			setTitle("Chat Client-" + userName);
			address = new InetSocketAddress(serverHostName, Integer.parseInt(portStr));
			mSocket = new Socket();
			try {
				mSocket.connect(address);
				m_client = new ChatClientThread(this, mSocket);
				m_client.dispatchMessage(userName); // send user name
				// m_client.dispatchMessage(END_FLAG); // send end flag
				m_client.start();
				enableSettingsUI(false);
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
			
		}
	}
	
	private void enableSettingsUI(boolean enable) {
		ipField.setEditable(enable);
		portField.setEnabled(enable);
		userField.setEditable(enable);
		passwordField.setEnabled(enable);
		connectBtn.setEnabled(enable);
		disConnectBtn.setEnabled(!enable);
	}
	
	private boolean checkNull(String inputString) {
		if(inputString ==  null || inputString.length() == 0) {
			return true;
		} else {
			return false;
		}
	}

}
客户端SOCKET通信线程的代码如下:

package com.gloomyfish.socket.tutorial.chat;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class ChatClientThread extends Thread {
	private ChatClient _mClient;
	private Socket _mSocket;
	private DataOutputStream dos;
	
	public ChatClientThread(ChatClient cclient, Socket socket) {
		this._mClient = cclient;
		this._mSocket = socket;
	}
	
	public void run() {
		try {
			DataInputStream bufferedReader = new DataInputStream(_mSocket.getInputStream());
			byte[] cbuff = new byte[256];
			char[] tbuff = new char[256];
			int size = 0;
			int byteCount = 0;
			int length = 0;
			while(true) {
				if((size = bufferedReader.read(cbuff))> 0) {
					char[] temp = convertByteToChar(cbuff, size);
					length = temp.length;
					if((length + byteCount) > 256) {
						length = 256 - byteCount;
					}
					System.arraycopy(temp, 0, tbuff, byteCount, length);
					byteCount += size;
					if(String.valueOf(tbuff).indexOf(ChatClient.END_FLAG) > 0) {
						_mClient.handleMessage(tbuff, byteCount);
						byteCount = 0;
						clearTempBuffer(tbuff);
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void clearTempBuffer(char[] tbuff) {
		for(int i=0; i<tbuff.length; i++) {
			tbuff[i] = ' ';
		}
	}
	
	private char[] convertByteToChar(byte[] cbuff, int size) {
		char[] charBuff = new char[size];
		for(int i=0; i<size; i++) {
			charBuff[i] = (char)cbuff[i];
		}
		return charBuff;
	}
	
	public synchronized void dispatchMessage(String textMsg) throws IOException {
		if(dos == null) {
			dos = new DataOutputStream(_mSocket.getOutputStream());
		}
		byte[] contentBytes = textMsg.getBytes();
		dos.write(contentBytes, 0, contentBytes.length);
	}
}
服务器端的消息转发代码如下:

	public synchronized void dispatchMessage(String[] keyValue, String userName) throws IOException {
		chatArea.append(userName + " to " + keyValue[0] + " : " + keyValue[1] + "\r\n");
		for(ChatServerClientThread client : clientList) {
			if(client.getUserName().equals(keyValue[0])) {
				client.dispatchMessage(userName + " says: " + keyValue[1]);
				client.dispatchMessage(END_FLAG);
				break;
			}
		}
	}
服务器端的客户端线程run方法的代码如下:

public void run() {
	System.out.println("start user = " + userName);
	try {
		DataInputStream bufferedReader = new DataInputStream(userSocket.getInputStream());
		byte[] cbuff = new byte[256];
		char[] tbuff = new char[256];
		int size = 0;
		int byteCount = 0;
		int length = 0;
		while(true) {
			if((size = bufferedReader.read(cbuff))> 0) {
				char[] temp = convertByteToChar(cbuff, size);
				length = temp.length;
				if((length + byteCount) > 256) {
					length = 256 - byteCount;
				}
				System.arraycopy(temp, 0, tbuff, byteCount, length);
				byteCount += size;
				if(String.valueOf(tbuff).indexOf(ChatServer.END_FLAG) > 0) {
					String receivedContent = String.valueOf(tbuff);
					int endFlag = receivedContent.indexOf(ChatServer.END_FLAG);
					receivedContent = receivedContent.substring(0, endFlag);
					String[] keyValue = receivedContent.split("#");
					if(keyValue.length > 1) {
						server.dispatchMessage(keyValue, userName);
					}
					byteCount = 0;
					clearTempBuffer(tbuff);
				}
			}
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}
最终程序的运行结果截屏如下:



分享到:
评论

相关推荐

    基于TCP协议网络socket编程-简单聊天机器人(java实现C/S通信)

    1、本项目则是使用Java实现TCP的Socket网络通信,包含C/S软件架构的程序设计,偏向实践,更加有趣! 2、实现简单有趣的“创意”聊天机器人。 3、建立通信规则: Server和Client之间需要约定相同的规则,保证正常通信...

    基于TCP的Socket编程服务器和客户端代码

    基于TCP的Socket编程服务器和客户端代码

    基于java socket网络编程实现的简单模拟qq聊天的程序,可实现并发聊天

    基于java socket网络编程实现的简单模拟qq聊天的程序,可实现并发聊天,分为服务器和客户端两个部分

    基于java的modbus TCP通信

    基于java的modbus TCP通信

    基于TCP的计算机通信工具编程实现

    基于TCP的计算机通信工具编程实现,可以很好地理解socket运行于java编程

    Java TCP/IP Socket编程

    《Java TCP/IP Socket编程(原书第2版)》基于TCP/IP Socket相关原理,对如何在Java中进行Socket编程作了深入浅出的介绍。《Java TCP/IP Socket编程(原书第2版)》内容简明扼要,条理清晰,并在讲解相应的概念或编程...

    java写的一个简单基于tcp协议的多用户聊天系统

    用j2se写的一个简单的聊天系统,写了客户端和服务器。用到了socket,流读写技术,服务器端采用了多线程等技术。基本上涵盖了j2se中所有学到的技术,非常适合学习了j2se的初学者练习使用。

    Java编程实现基于TCP协议的Socket聊天室示例

    主要介绍了Java编程实现基于TCP协议的Socket聊天室,结合实例形式详细分析了java基于TCP协议的Socket聊天室客户端与服务器端相关实现与使用技巧,需要的朋友可以参考下

    java基于socket聊天室源代码.rar

    注:供java socket编程的初学者学习使用,严禁用作作业抄袭和商业用途。若转载源代码,请注明原作者以及作者的博客地址。 项目名称:TCPChatRoomServer(服务器端),TCPChatRoomClient(客户端) 版本:v0.1.0 开发...

    SOCKET TCP 文件传输 客户端 服务器端 client svever

    SOCKET TCP 文件传输 客户端 服务器端 client svever 实现简易文件传输功能

    Linux环境下基于TCP的Socket编程浅析.pdf

    论文摘要: Socket 适用于同一台...文章介绍了L inux 平台下的Socket 及其在TCP 协议下的编程原理, 并通过一个用Java 编写的基于 TCP 的客户ö服务器程序, 描述了网络中不同主机上的两个进程之间的 Socket 通信机制。

    socket编程 TCP client.java TCPserver.java

    socket编程,客户端与服务器端,计算机网络

    基于TCP/IP 的Java 简易多人聊天器

    这是基于TCP协议网上聊天程序。主要分为两个部分,一部分是服务器,一部分是客户端。两者结合,实现了基本的局域网多人聊天功能。对于程序设计语言,选择了JAVA语言。JAVA程序语言自带了强大的网络功能设计包,而且...

    Java TCPIP Socket编程 源码

    一个Socket实例代表了TCP连接的一个客户端,而一个ServerSocket实例代表了TCP连接的一个服务器端,一般在TCP Socket编程中,客户端有多个,而服务器端只有一个,客户端TCP向服务器端TCP发送连接请求,服务器端的...

    《Java程序设计实训》报告 多人聊天室

    4、使用网络编程,掌握基于TCP协议的Socket编程,了解Socket编程的协议约定,掌握简单应用协议的开发。  5、使用C/S架构,对网络编程有一定的了解 二.项目概述 运用java程序编写聊天室,实现简单的聊天功能。它是...

    Java网络编程--在HTTP服务器上找到的分组

    在HTTP服务器上找到的分组 14.1 概述 142 14.2 多个HTTP服务器 144 14.3 客户端SYN的到达间隔时间 145...14.10 T/TCP路由表大小的模拟 160 14.11 mbuf的交互 162 14.12 TCP的PCB高速缓存和首部预测 163 14.13 小结 165

    Java Socket 编程模拟Ftp

    1、本项目是使用Java socket 编程来模拟Ftp,严格按照M-V-C架构分包分类,并实现Swing界面,Ftp使用TCP端口21传输控制信息,使用TCP端口20来传输文件数据。 2、本项目默认使用TCP端口4321来传输控制信息,使用TCP...

    android即时通讯软件毕业设计论文

    1.3 SOCKET编程 - 7 - 1.3.1 关于TCP/IP协议 - 7 - 1.3.2 服务器和客户机 - 8 - 1.4本文内容安排 - 8 - 2 需求分析 - 9 - 2.1.1 可行性分析 - 9 - 2.1.2系统功能需求 - 9 - 2.1.3 系统性能需求 - 10 - 2.1.4 系统...

    java源码包---java 源码 大量 实例

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...

    Java基于TCP-IP协议的聊天工具的设计与开发-毕业设计论文

    模式,客户端与服务器通过 Socket 传递数据,服务器采用多线程技术。利用 Eclipse 进行开发。 该软件完成后的主要功能有:点对点、点对多、多对多的聊天,服务器对聊天 进行统一管理,比如用户注册,用户登陆,踢出...

Global site tag (gtag.js) - Google Analytics