基础聊天室的实现,实现同局域网的聊天,有详细代码

**

聊天室的实现,基本步骤解析

**
总体思想结构
1.建立服务器端,服务器端不需要界面
建立一个包为服务器包 serve
包内应该有服务器的主方法用来启动服务器,和一个用来接收客户端消息并且转发给所有的客户端的线程类 服务器给所有客户端发消息的线程
2.建立客户端,有界面
建立一个包为客户端包 cient
包内应该有客户端的界面类,可以单独的运行(即有主方法),并且需要一个向服务器发送消息的线程类和一个发送消息的方法,主体大致结构如下
在这里插入图片描述
下面进行更深一步的的讲解
客户端
CientUI.java类:
构建自己喜欢的swing界面;
为按钮添加监听器;(主要逻辑代码)
可以有一个连接服务器的方法

package cient;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.Socket;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

/**
 * 客户端的主界面类
 * @author VastWu
 *
 */
public class CientUI {
	private Socket socket;
	public SendUtil su;
	public static void main(String[] args) {
		CientUI cientUI=new CientUI();
		cientUI.init();
	}
	//连接服务器的方法
	public Socket connection() {
		try {
			//尝试连接服务器
			Socket socket=new Socket("127.0.0.1", 100);
			return socket;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public void init() {
		JFrame jframe=new JFrame("714皇家赌场");
		jframe.setSize(700, 500);
		jframe.setDefaultCloseOperation(3);
		jframe.setLayout(null);
		//设置界面居中
		jframe.setLocationRelativeTo(null);
		//设置最大化按钮不可用
		jframe.setResizable(false);
		//历史消息框、并且添加滚动条
		JTextArea historyMsg=new JTextArea();
		JScrollPane jsp=new JScrollPane(historyMsg);
		jsp.setBounds(5, 25, 685, 300);
		historyMsg.setEditable(false);
		jframe.add(jsp);
		//发送消息框
		JTextArea sendMsg=new JTextArea();
		JScrollPane jsp2=new JScrollPane(sendMsg);
		jsp2.setBounds(5, 325, 600, 140);
		jframe.add(jsp2);
		//按钮
		JButton sendBut=new JButton("发送");
		JButton clearBut=new JButton("清空");
		JButton connectBut=new JButton("连接服务器");
		sendBut.setBounds(605, 364, 84, 100);
		clearBut.setBounds(605, 325, 84, 40);
		connectBut.setBounds(5, 1, 150, 22);
		jframe.add(connectBut);
		jframe.add(sendBut);
		jframe.add(clearBut);
		//给按钮添加监听器
		ActionListener al=new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				String command=e.getActionCommand();
				switch (command) {
				case "发送":
					if(socket!=null) {
						String msg=sendMsg.getText().trim();
						sendMsg.setText("");
						historyMsg.append("我说:"+msg+"\r\n");
						//调用方法发送出去
						su.send(msg);
					}
					break;
				case "清空":
					sendMsg.setText("");
					break;
				case "连接服务器":
					socket=CientUI.this.connection();
					if(socket==null) {
						JOptionPane.showMessageDialog(null, "连接失败,请检查网络或重新连接!");
					}else {
						connectBut.setText("已连接服务器");
						JOptionPane.showMessageDialog(null, "连接成功!");
						//启动线程并且给服务器发送消息
						ReceiveThread rt=new ReceiveThread(socket, historyMsg);
						rt.start();
						su=new SendUtil(socket);
					}
					break;

				default:
					break;
				}
				
			}
		};
		connectBut.addActionListener(al);
		sendBut.addActionListener(al);
		clearBut.addActionListener(al);
		jframe.setVisible(true);
	}
}

ReceiveThread.java类:
接收服务器信息的线程类并且把消息加到界面中

package cient;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

import javax.swing.JTextArea;

/**
 * 接收来自服务器的线程类
 * @author VastWu
 *
 */
public class ReceiveThread extends Thread{
	private Socket socket;
	private JTextArea historyMsg;
	public ReceiveThread(Socket socket,JTextArea historyMsg) {
		this.socket=socket;
		this.historyMsg=historyMsg;
	}
	@Override
	public void run() {
		try {
			InputStream ips=socket.getInputStream();
			InputStreamReader isr=new InputStreamReader(ips);
			BufferedReader br=new BufferedReader(isr);
			while(true) {
				String msg=br.readLine();
				historyMsg.append(msg+"\r\n");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

SendUtil类
客户端发送消息的类

package cient;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 * 发送消息的类
 * @author VastWu
 *
 */
public class SendUtil {
	private Socket socket;
	private BufferedWriter bw;
	private String add;
	public SendUtil(Socket socket) {
		this.socket=socket;
		try {
			OutputStream ops=socket.getOutputStream();
			OutputStreamWriter osw=new OutputStreamWriter(ops);
			bw=new BufferedWriter(osw);
			add=socket.getLocalSocketAddress().toString();
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
	public void send(String msg) {
		try {
			bw.write(add+"说:"+msg+"\r\n");
			bw.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

服务端
Serve.java类
启动一个服务器的类

package serve;


import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

/**
 * 服务器端
 * @author VastWu
 *
 */
public class Serve {
	//声明一个集合用于储存socket对象
	public static ArrayList<Socket> socketList=new ArrayList<Socket>();
	//直接主方法以启动服务器
	public static void main(String[] args) throws Exception {
		//使用一个服务端套接字建立一个端口
		ServerSocket serverSocket=new ServerSocket(100);
		System.out.println("服务器开启成功,正在等待连接。。。");
		SendAllThread sat=new SendAllThread();
		sat.start();
		//为了使socket对象不被覆盖这里使用while死循环对socket进行储存
		while(true) {
			//等待连接
			Socket socket=serverSocket.accept();
			//连接成功后存入集合内
			socketList.add(socket);
			//得到客户端的地址
			String address=socket.getRemoteSocketAddress().toString();
			//连接成功后控制台提示连接成功
			System.out.println(address+"连接上来了");
			//启动转发线程
			TurnSendThread tst=new TurnSendThread(socket);
			tst.start();
			//启动通知线程
			TellThread tt=new TellThread(socket);
			tt.start();
		}
		
	}
}

SendAllThread.java类
服务器主动给服务器发信息的线程类

package serve;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 给所有客户端发消息的线程类
 * @author VastWu
 *
 */
public class SendAllThread extends Thread{
	@Override
	public void run() {
		try {
		//服务器控制台发送消息
		Scanner scan=new Scanner(System.in);
		//因为要发多条消息,所以用死循环
		while(true) {
			//接收输入的一行消息
			String msg=scan.nextLine();
			//使用输出流进行处理,并且需要向所有用户发消息所以需要对集合进行循环发送
			for(Socket socket:Serve.socketList) {
				OutputStream ops=socket.getOutputStream();
				OutputStreamWriter osw=new OutputStreamWriter(ops);
				BufferedWriter bw=new BufferedWriter(osw);
				bw.write("服务器说:"+msg+"\n");
				bw.flush();
			}
			
				}
			} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

TurnSendThread.java类
转发消息的线程类,即把客服端发过来的信息转发给所有的客户端

package serve;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 * 转发客服端发送过来的消息的线程类
 * @author VastWu
 *
 */
public class TurnSendThread extends Thread{
	private Socket socket;
	private String address;
	//先使用构造方法把socket传过来
	public TurnSendThread(Socket socket) {
		this.socket=socket;
		//客户端的地址
		address=socket.getRemoteSocketAddress().toString();
	}
	@Override
	public void run() {
		try {
			InputStream ips=socket.getInputStream();
			InputStreamReader isr=new InputStreamReader(ips);
			BufferedReader br=new BufferedReader(isr);
			while(true) {
				String str=br.readLine();
				//将读到的消息发送给所有的客户端
				for(Socket sk:Serve.socketList) {
					if(sk!=socket) {
						OutputStream ops=sk.getOutputStream();
						OutputStreamWriter osw=new OutputStreamWriter(ops);
						BufferedWriter bw=new BufferedWriter(osw);
						bw.write(str+"\n");
						bw.flush();
					}
				}
			}
		} catch (Exception e) {
			// 如果抛出异常,说明连接已经断开,此处为处理用户离开时通知其他客户端的功能
			// 从List中删除该客户端,并通知其他客户端
			Serve.socketList.remove(this.socket);
			for (Socket sk : Serve.socketList) {
				try {
				// 从sk上获得输出流
				OutputStream ops = sk.getOutputStream();
				OutputStreamWriter osw = new OutputStreamWriter(ops);
				BufferedWriter bw = new BufferedWriter(osw);
				
					bw.write(address + "离开了房间!!\n");
					bw.flush();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}
}

这样子就基本能实现群聊了嘻
下面是拓展
实现一个客户端连接服务端以后,服务器通知其他所有客户端有人上线
即在服务器写一个发信息的线程类,先给连上来的客服端发欢迎消息,然后给其他所有人发送通知。

package serve;

import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 *服务器发送通知的线程类 
 * @author VastWu
 *
 */
public class TellThread extends Thread{
	private Socket socket;
	public TellThread(Socket socket) {
		this.socket=socket;
	}
	@Override
	public void run() {
			try {
				OutputStream ops=socket.getOutputStream();
				OutputStreamWriter osw=new OutputStreamWriter(ops);
				BufferedWriter bw=new BufferedWriter(osw);
				String address=socket.getRemoteSocketAddress().toString();
				bw.write("欢迎"+address+"来到聊天室\n");
				bw.write("当前在线人数:"+Serve.socketList.size()+"人\r\n");
				bw.flush();
				for(Socket sk:Serve.socketList) {
					if(sk!=socket) {
						OutputStream ops2=sk.getOutputStream();
						OutputStreamWriter osw2=new OutputStreamWriter(ops2);
						BufferedWriter bw2=new BufferedWriter(osw2);
						bw2.write(address+"进入了聊天室,当前在线人数:"+Serve.socketList.size()+"人\r\n");
						bw2.flush();
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		
	}
}

功能实现:
在这里插入图片描述
更改ip地址即可实现同局域网的聊天…

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
一个面向局域网、互联网的即时聊天工具,它专门针对学校及企业内部的网络通讯而开发的。 Messenger除了具有一般聊天工具都有的基础功能以外,还有自定义表情符等高级功能,使用简单,服务器无需特别数据库配置。 本软件很适合作为学校、企业内部局域网的通讯工具。 用户登录基本原理 1.用户登录,客户端根据用户填写的IP向服务器发送连接请求,若IP正确且服务器工作正常。服务器会在Winsock数组(Servicesocket(i))中建立一个连接,同时在自定义类型数组UserInfo(u)中初始化一个索引值与该用户使用的Servicesocket索引(Index属性)值相同的元素,并将其所有值设置为N/A(执行LogIn函数)。然后,服务器调用 New_User 、Login_user 判断用户提交资料与服务器存储的用户资料是否相符(密码是否正确、是否新用户、服务器状况是否允许用户登录等)。若条件符合,服务器向客户端发送登录成功信号(.LoginGood Svc2)并将用户资料写进属于该用户的UserInfo类中。否则向客户端发送登录失败信号(.LoginBad [失败原因代号]),断开连接。 2.用户收到服务器的登录成功信号后,向服务器提交获取好友列表请求(.getbuddys)。服务器收到该请求后调用 GetBuddysFromDB 、Get_User_buddies 完成对该用户好友的数据搜索,并将该用户的所有好友的名称、状态合并为一条命令发送给指定的用户。客户端接收到命令后立刻进行解析,完成对好友的添加及状态更新(利用Wordfunc 模块中的函数)。 3.一个用户登录成功后,服务器会向所有连接在其上的用户(无论该用户处于何种状态)发送一个用户上线状态通知。其它用户接受该通知后根据情况自动决定是否进行更新。 发送/接收信息基本原理 1.当用户点击“发送”按钮或通过其他方式命令客户端发送信息时,客户端将Richtextbox 中的信息代码(TextRTF属性)的开头附加上起始标志(.msg )、在其末尾添加接收用户的名称和结束标志( ||),然后向服务器发送该信息。 2.服务器接收到客户端发出的消息后,根据信息头标志(.msg)判断这是一则聊天信息,同时根据信息结束标志( ||)判断该信息是否完整(若不完整,写入缓冲字符串数组)、获取信息的接收用户。判断完毕后,找到对应用户使用的Servicesocket,向该用户发送信息(若该用户不在线,信息将写入临时文件储存,待该用户上线再发送) ZX Messenger 除文件传送和二人模式外,所有通讯均采用这种模式。 3.信息被转发到目标用户后,将再一次进行完整性判断(因为Winsock发送数据包大小受网络情况限制)并对数据进行连接。然后,使用 Word 函数取出有效信息并显示。 Messenger 使用 Microsoft Visual Basic 6.0 开发,(除去各附件)本软件大体分为两个部分——客户端和服务器端(是典型的C/S架构)。其客户端和服务器端依靠Winsock进行通讯(使用TCP/IP 协议),服务器通过建立Winsock控件数组实现多客户端同时连接的支持。Messenger 的消息走向主要采用 客户端发送——服务器中转——客户端接收的模式(个别功能例外)。 ZX Messenger 适用于 Windows 95/98[注]/Me/2000/XP/.Net Server 2003 [注]:本软件只适用于安装了Microsoft Office或其他带有 Microsoft Visual Basic 6.0 运行库 的 Windows 95/98操作系统,对于没有安装 Microsoft Visual Basic 6.0 运行库的WINDOWS 95/98 ,本软件会无法运行,对于这种情况,请下载 Microsoft Visual Basic 6.0 运行库 并安装。Win98 以后的操作系统不存在此问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值