基于socket的c/s聊天系统

在学习socket的时候,通过查找资料,和自己的学习,花了一个星期写出了这个模型,尽管功能和不太强大,还有点bug,但至少体现了socket的基本思想。部分代码省略了

客户端口,可以在client.ini 中配置

功能:
1.  用socket实现client/server的通信,服务端口8888;(配置文件)

2.  提供聊天的GUI,用Java Application实现,包括一个主窗口以及聊天窗口;(Swing)

3. 可实现一对多的聊天,每一个连接打开一个新的聊天窗口;(Thread)

4. 服务器端显示连接到客户端的IP地址,连接人数,以及在线客户的聊天记录

5. 客户端显示服务器IP地址,本机IP地址,和连接服务器的人数,以及当前客户的聊天记录

6. 关闭服务器,客户端连接失败,信息发送不出去; 

7. 关闭客户端,服务器显示离开的客户端的IP地址;

8. 通过文件配置读server的端口,和客户端的IP,Port;

9. 具有发送文本约束性。

1.服务器端

public class ChatServer extends JFrame implements Runnable, ActionListener {
	private JTextArea taOther;
	private JScrollPane pnOther;
	private JMenuItem version;
	private JMenuItem quit;
	private JMenu exitmn;
	private JMenu helpmn;
	private JMenuItem newmn;
	private JMenu filemn;
	private JMenuBar menuBar;
	private static final long serialVersionUID = 1L;
	private JTextArea taMessage;
	private JScrollPane pnMessage;
	private ServerSocket server; // 定义服务器端套接字
	private BManager bMan = new BManager();

	/**
	 * @param title
	 */
	public ChatServer(String title) {
		super(title);
		initialize();
	}

	/**
	 * 启动服务器
	 * 
	 */
	public void startServer() {
		try {
			// XXX 读取服务器端配置文件
			// 格式化代码
			BufferedReader br = new BufferedReader(new FileReader("server.ini"));
			// 产生读属性文件的对象
			Properties properties = new Properties();
			// 加载读文件的数据
			properties.load(br);
			// 从属性对象中取出对应的值
			String port = properties.getProperty("port");
			server = new ServerSocket(Integer.parseInt(port));
			taOther.append("服务器套接字创建完毕.\n");
			taOther.append("主机IP地址:"
					+ InetAddress.getLocalHost().getHostAddress() + "\n");
			taOther.append("等待客户机连接……" + "\n");
			while (true) {
				Socket socket = server.accept();

				taOther.append("客户IP地址:" + socket.getInetAddress() + "\n");
				// 创建并运行与客户机通信的线程
				new Chat_Thread(socket).start();
				// 向消息广播者列表添加socket
				bMan.add(socket);
				// 消息广播者向所有客户机传送当前连接人数
				bMan.sendClientInfo();
			}
		} catch (Exception e) {
			taOther.append(("服务器已经被启用,不能再被启用"));
		}
	}

	public static void main(String[] args) {

		ChatServer server = new ChatServer("聊天服务器");
		server.setVisible(true);
		server.startServer();
	}

	/**
	 * 与客户机通信的线程类
	 * 
	 */

	class Chat_Thread extends Thread {
		Socket socket;
		private BufferedReader reader;
		private PrintWriter writer;

		public Chat_Thread(Socket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			try {
				// 从输出流获取消息
				reader = new BufferedReader(new InputStreamReader(socket
						.getInputStream()));
				writer = new PrintWriter(socket.getOutputStream(), true);
				writer.flush();
				String msg;
				while ((msg = reader.readLine()) != null) {
					taMessage.append(msg.trim() + "\n");
					// 向所有客户机传送信息
					bMan.sendToAll(msg);
				}
			} catch (Exception e) {
			} finally {
				try {
					// 从消息广播者列表删除socket
					bMan.remove(socket);
					taOther.append("客户机" + socket.getInetAddress() + "离开"
							+ "\n");
					if (reader != null)
						reader.close();
					if (writer != null)
						writer.close();
					if (socket != null)
						socket.close();
					reader = null;
					writer = null;
					socket = null;
					bMan.sendClientInfo();
				} catch (Exception e) {
				}
			}
		}
	}

	/**
	 * 消息广播者类,继承Vector类
	 * 
	 * 
	 */
	class BManager extends Vector {
		public BManager() {
		}

		/**
		 * 添加套接字
		 * 
		 * @param sock
		 * 
		 */
		public void add(Socket sock) {
			super.add(sock);
		}

		/**
		 * 删除套接字
		 * 
		 * @param sock
		 */
		public void remove(Socket sock) {
			super.remove(sock);
		}

		/**
		 * 向所有客户机传送内容
		 * 
		 * @param msg
		 */
		public synchronized void sendToAll(String msg) {
			PrintWriter writer = null;
			Socket sock;
			for (int i = 0; i < size(); i++) {
				// 获取第i套接字
				sock = (Socket) elementAt(i);
				try {
					// 输出流刷空
					writer = new PrintWriter(sock.getOutputStream(), true);
				} catch (IOException ie) {
				}
				// 使用第i个套接字输出流,输出msg
				if (writer != null)
					writer.println(msg);

			}
		}

		/**
		 * 服务器和客户端同时显示当前聊天人数
		 * 
		 */
		public synchronized void sendClientInfo() {
			String info = "当前聊天人数:" + size();
			taOther.append(info + "\n");
			sendToAll(info);
		}
	}

	private void initialize() {
		setJMenuBar(getMainJMenuBar());
		setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		setName("f");
		setSize(new Dimension(700, 500));
		getContentPane().add(getPnMessage(), BorderLayout.CENTER);
		getContentPane().add(getPnOther(), BorderLayout.EAST);
	}

	/**
	 * @return
	 */
	protected JScrollPane getPnMessage() {
		if (pnMessage == null) {
			pnMessage = new JScrollPane();
			pnMessage.setViewportView(getTaMessage());
		}
		return pnMessage;
	}

	/**
	 * @return
	 */
	protected JTextArea getTaMessage() {
		if (taMessage == null) {
			taMessage = new JTextArea();
			taMessage.setBackground(new Color(220, 220, 220));
			taMessage.setEditable(false);
		}
		return taMessage;
	}

	/**
	 * @return
	 */
	/**
	 * @return
	 */

	@Override
	public void run() {
		// TODO Auto-generated method stub

	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		if (e.getSource() == getQuit()) {
			System.exit(0);
		}
	}

	/**
	 * @return
	 */
	protected JMenuBar getMainJMenuBar() {
		if (menuBar == null) {
			menuBar = new JMenuBar();

			menuBar.add(getFilemn());
			menuBar.add(getHelpmn());
			menuBar.add(getExitmn());
		}
		return menuBar;
	}

	/**
	 * @return
	 */
	protected JMenu getFilemn() {
		if (filemn == null) {
			filemn = new JMenu();
			filemn.setText("文件");
			filemn.add(getNewmn());
		}
		return filemn;
	}

	/**
	 * @return
	 */
	protected JMenuItem getNewmn() {
		if (newmn == null) {
			newmn = new JMenuItem();
			newmn.setText("新建");
		}
		return newmn;
	}

	/**
	 * @return
	 */
	protected JMenu getHelpmn() {
		if (helpmn == null) {
			helpmn = new JMenu();
			helpmn.setText("帮助");
			helpmn.add(getVersion());
		}
		return helpmn;
	}

	/**
	 * @return
	 */
	protected JMenu getExitmn() {
		if (exitmn == null) {
			exitmn = new JMenu();
			exitmn.setText("退出");
			exitmn.add(getQuit());
		}
		return exitmn;
	}

	/**
	 * @return
	 */
	protected JMenuItem getQuit() {
		if (quit == null) {
			quit = new JMenuItem();
			quit.addActionListener(this);
			quit.setText("关闭");
		}
		return quit;
	}

	/**
	 * @return
	 */
	protected JMenuItem getVersion() {
		if (version == null) {
			version = new JMenuItem();
			version.setText("版本");
		}
		return version;
	}

	/**
	 * @return
	 */
	protected JScrollPane getPnOther() {
		if (pnOther == null) {
			pnOther = new JScrollPane();
			pnOther.setViewportView(getTaOther());
		}
		return pnOther;
	}

	/**
	 * @return
	 */
	protected JTextArea getTaOther() {
		if (taOther == null) {
			taOther = new JTextArea();

			taOther.setEditable(false);
		}
		return taOther;
	}
}

 

2.客户端

public class ChatClient extends JFrame implements Runnable, ActionListener {

	private JTextArea tauser;
	private JScrollPane userpn;
	private JScrollPane viewpn;
	private JMenuItem versionmn;
	private JMenuItem shutupmn;
	private JTextArea ta;
	private JMenuItem connectmn;
	private JTextField nametf;
	private JTextField sendtf;
	private JButton sendbtn;
	private JPanel panel;
	private JMenu helpmn;
	private JMenu quitmn;
	private JMenu newmn;
	private JMenuBar menuBar;
	private Socket socket;
	private BufferedReader reader;
	private PrintWriter writer;
	private boolean runnable = true;

	public boolean getRunnable() {
		return runnable;
	}

	public void stop() {
		runnable = false;
	}

	/**
	 * 客户端主程序
	 * 
	 * @param args
	 */
	public static void main(String args[]) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					ChatClient client = new ChatClient();
					client.setVisible(true);
					client.connect();// 连接服务器
				} catch (Exception e) {
					// e.printStackTrace();
				}
			}
		});
	}

	/**
	 * 添加动作事件监听器
	 * 
	 */

	public void actionPerformed(ActionEvent e) {
		// 监听发送按钮和输入信息文本框的事件
		if (e.getSource() == getSendtf() || e.getSource() == getSendbtn()) {
			// 如果输入昵称为空,弹出警告框
			if (nametf.getText().equals("")) {
				JOptionPane.showMessageDialog(null, "请输入昵称!", "警告",
						JOptionPane.ERROR_MESSAGE);
			}
			// 如果输入的内容为空,弹出警告框
			else if (sendtf.getText().equals("")) {
				JOptionPane.showMessageDialog(null, "输入内容不能为空!", "警告",
						JOptionPane.ERROR_MESSAGE);
			}
			// 否则向文本框发送输入的内容
			else {
				writer.println(nametf.getText().trim() + " : "
						+ sendtf.getText().trim());
				sendtf.setText("");// 清除sendBox 内容
			}
		}
		// 关闭客户端事件
		if (e.getSource() == getShutupmn()) {
			try {
				this.stop();
				socket.close();
			} catch (Exception e1) {
				// e1.printStackTrace();
				System.exit(0);
			}
			System.exit(0);
		}
		// 版本事件
		if (e.getSource() == getVersionmn()) {
			Version v = new Version();
			v.show();
		}

		if (e.getSource() == getNewmn()) {
			ChatClient client = null;
			// return;
			client.connect();
		}

	}

	/**
	 * 客户端连接服务器
	 * 
	 */
	private void connect() {
		try {
			// 读文件
			// 格式化代码
			BufferedReader rd = new BufferedReader(new FileReader("client.ini"));
			// 产生读属性文件的对象
			Properties properties = new Properties();
			// 加载读文件的数据
			properties.load(rd);
			// 从属性对象中取出key对应的值

			String ip = (String) properties.get("IP");
			String port = (String) properties.get("port");
			// 客户端ip,port
			socket = new Socket(ip, Integer.parseInt(port));
			reader = new BufferedReader(new InputStreamReader(socket
					.getInputStream()));
			writer = new PrintWriter(socket.getOutputStream(), true);
			writer.flush();
			new Thread(this).start();
			tauser.append("连接服务器成功!" + "\n");
			tauser.append("本机地址:" + InetAddress.getLocalHost().getHostAddress()
					+ "\n");
			tauser.append("服务器地址:" + socket.getInetAddress() + "\n");
		} catch (Exception e) {
			// e.printStackTrace();
			tauser.append("服务器未启动,请先启动服务器!");
		}
	}

	/**
	 * 创建客户端界面
	 * 
	 */
	public ChatClient() {
		super();
		setBounds(100, 100, 500, 375);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		initialize();

	}

	private void initialize() {
		setTitle("客户机");
		setJMenuBar(getMainJMenuBar());
		getContentPane().add(getPanel(), BorderLayout.SOUTH);
		getContentPane().add(getNametf(), BorderLayout.NORTH);
		getContentPane().add(getViewpn());
		getContentPane().add(getUserpn(), BorderLayout.EAST);
	}

	/**
	 * @return 菜单栏
	 */
	protected JMenuBar getMainJMenuBar() {
		if (menuBar == null) {
			menuBar = new JMenuBar();
			menuBar.add(getNewmn());
			menuBar.add(getHelpmn());
			menuBar.add(getQuitmn());
		}
		return menuBar;
	}

	/**
	 * @return 新建
	 */
	protected JMenu getNewmn() {
		if (newmn == null) {
			newmn = new JMenu();
			newmn.setText("新建");
			newmn.add(getConnectmn());
		}
		return newmn;
	}

	/**
	 * @return 退出
	 */
	protected JMenu getQuitmn() {
		if (quitmn == null) {
			quitmn = new JMenu();
			quitmn.addActionListener(this);
			quitmn.setText("退出");
			quitmn.add(getShutupmn());
		}
		return quitmn;
	}

	/**
	 * @return 帮助
	 */
	protected JMenu getHelpmn() {
		if (helpmn == null) {
			helpmn = new JMenu();
			helpmn.setText("帮助");
			helpmn.add(getVersionmn());
		}
		return helpmn;
	}

	/**
	 * @return 面板
	 */
	protected JPanel getPanel() {
		if (panel == null) {
			panel = new JPanel();
			panel.setLayout(new BorderLayout());
			panel.add(getSendbtn(), BorderLayout.EAST);
			panel.add(getSendtf());
		}
		return panel;
	}

	/**
	 * @return 发送
	 */
	protected JButton getSendbtn() {
		if (sendbtn == null) {
			sendbtn = new JButton();
			sendbtn.addActionListener(this);
			sendbtn.setText("发送");
		}
		return sendbtn;
	}

	/**
	 * @return 发送文本框
	 */
	protected JTextField getSendtf() {
		if (sendtf == null) {
			sendtf = new JTextField();
		}
		return sendtf;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (runnable) {
			try {
				// 从输入流读取数据,然后加至msgView

				ta.append(reader.readLine().trim() + "\n");

			} catch (IOException ie) {

				// tauser.append("服务器已关闭,请重新连接!" + "\n");
				// tauser.append("");
			}
		}
		tauser.append("服务器已关闭,请重新连接!" + "\n");
	}

	/**
	 * @return 姓名文本框
	 */
	protected JTextField getNametf() {
		if (nametf == null) {
			nametf = new JTextField();
		}
		return nametf;
	}

	/**
	 * @return 菜单新建连接
	 */
	protected JMenuItem getConnectmn() {
		if (connectmn == null) {
			connectmn = new JMenuItem();
			connectmn.addActionListener(this);
			connectmn.setText("新建连接");
		}
		return connectmn;
	}

	/**
	 * @return 信息输入文本框
	 */
	protected JTextArea getTa() {
		if (ta == null) {
			ta = new JTextArea();
			ta.setBackground(new Color(250, 240, 230));
			ta.setEditable(false);
		}
		return ta;
	}

	/**
	 * @return 菜单关闭
	 */
	protected JMenuItem getShutupmn() {
		if (shutupmn == null) {
			shutupmn = new JMenuItem();
			shutupmn.addActionListener(this);
			shutupmn.setText("关闭");
		}
		return shutupmn;
	}

	/**
	 * @return 菜单版本
	 */
	protected JMenuItem getVersionmn() {
		if (versionmn == null) {
			versionmn = new JMenuItem();
			versionmn.addActionListener(this);
			versionmn.setText("版本");
		}
		return versionmn;
	}

	/**
	 * @return 消息面板
	 */
	protected JScrollPane getViewpn() {
		if (viewpn == null) {
			viewpn = new JScrollPane();
			viewpn.setViewportView(getTa());
		}
		return viewpn;
	}

	/**
	 * @return 信息面板
	 */
	protected JScrollPane getUserpn() {
		if (userpn == null) {
			userpn = new JScrollPane();
			userpn.setViewportView(getTauser());
		}
		return userpn;
	}

	/**
	 * @return 文本用户
	 */
	protected JTextArea getTauser() {
		if (tauser == null) {
			tauser = new JTextArea();
			tauser.setEditable(false);
		}
		return tauser;
	}
}

 

 3.版本

public class Version extends JFrame implements ActionListener {

	private JLabel version10betaLabel;

	/**
	 * 版本框架
	 * 
	 * @param args
	 */
	public static void main(String args[]) {

		Version frame = new Version();
		frame.setVisible(true);

	}

	/**
	 * Create the frame
	 */
	public Version() {
		super();
		setBounds(60, 60, 210, 198);
		initialize();

	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub

	}

	private void initialize() {
		getContentPane().add(getVersion10betaLabel(), BorderLayout.CENTER);
	}

	/**
	 * @return
	 */
	protected JLabel getVersion10betaLabel() {
		if (version10betaLabel == null) {
			version10betaLabel = new JLabel();
			version10betaLabel.setText("Version:1.0Beta 设计者:mhm");
		}
		return version10betaLabel;
	}

}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本文将介绍如何使用MFC Winsock实现基于TCP协议的C/S聊天程序。 1. 创建MFC应用程序 首先,我们需要创建一个MFC应用程序。在创建向导中选择“单文档应用程序”类型,勾选“包含MFC的ActiveX控件”和“支持ActiveX控件”,其他选项默认即可。 2. 添加界面元素 在资源视图中添加两个编辑框和一个按钮,用于输入和显示聊天内容,以及发送消息。 3. 编写代码 在Dlg.cpp文件中添加以下代码: 在头文件中添加以下头文件: #include "stdafx.h" #include "Dlg.h" #include "afxdialogex.h" #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") 在OnInitDialog()函数中添加以下代码: WSADATA wsaData; SOCKET ConnectSocket = INVALID_SOCKET; struct addrinfo *result = NULL, *ptr = NULL, hints; int iResult; // Initialize Winsock iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { MessageBox(_T("WSAStartup failed with error: %d"), iResult); return FALSE; } ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; // Resolve the server address and port iResult = getaddrinfo(_T("localhost"), _T("27015"), &hints, &result); if ( iResult != 0 ) { MessageBox(_T("getaddrinfo failed with error: %d"), iResult); WSACleanup(); return FALSE; } // Attempt to connect to an address until one succeeds for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) { // Create a SOCKET for connecting to server ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (ConnectSocket == INVALID_SOCKET) { MessageBox(_T("socket failed with error: %ld\n"), WSAGetLastError()); WSACleanup(); return FALSE; } // Connect to server. iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); if (iResult == SOCKET_ERROR) { closesocket(ConnectSocket); ConnectSocket = INVALID_SOCKET; continue; } break; } freeaddrinfo(result); if (ConnectSocket == INVALID_SOCKET) { MessageBox(_T("Unable to connect to server!")); WSACleanup(); return FALSE; } // Set the mode of the socket to be nonblocking u_long iMode = 1; iResult = ioctlsocket(ConnectSocket, FIONBIO, &iMode); if (iResult == SOCKET_ERROR) { MessageBox(_T("ioctlsocket failed with error: %d"), WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return FALSE; } // Disable Nagle's algorithm char value = 1; setsockopt(ConnectSocket, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); 在OnDestroy()函数中添加以下代码: // shutdown the connection since no more data will be sent iResult = shutdown(ConnectSocket, SD_SEND); if (iResult == SOCKET_ERROR) { MessageBox(_T("shutdown failed with error: %d"), WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return FALSE; } // cleanup closesocket(ConnectSocket); WSACleanup(); 在OnBnClickedButtonSend()函数中添加以下代码: // Send a message CString strMsg; m_edtMsg.GetWindowText(strMsg); char buf[1024]; strcpy_s(buf, CT2A(strMsg.GetBuffer())); iResult = send(ConnectSocket, buf, strlen(buf), 0); if (iResult == SOCKET_ERROR) { MessageBox(_T("send failed with error: %d"), WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return FALSE; } m_lstMsg.AddString(strMsg); m_edtMsg.SetWindowText(_T("")); 在OnReceive()函数中添加以下代码: char buf[1024]; int iResult = recv(ConnectSocket, buf, sizeof(buf), 0); if (iResult > 0) { CString strMsg(buf); m_lstMsg.AddString(strMsg); } else if (iResult == 0) { MessageBox(_T("Connection closed")); closesocket(ConnectSocket); WSACleanup(); return FALSE; } else { int err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { MessageBox(_T("recv failed with error: %d"), err); closesocket(ConnectSocket); WSACleanup(); return FALSE; } } 4. 编译运行 现在我们可以编译运行程序,在输入框中输入消息并点击发送按钮,就可以发送消息到服务器并在列表框中显示。当接收到服务器返回的消息时,也会在列表框中显示。 注意:本程序仅实现了客户端的代码,需要和服务器端代码配合使用才能实现完整的C/S聊天程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值