`
wz94
  • 浏览: 31543 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

网络通信聊天室实现

阅读更多

    所谓网络通信聊天室,简单的说就是用代码实现一个以本机ip作为地址的服务器,并能够实现与客户端的数据传递。服务器从客户端的输入流中读取数据,在客户端的输出流中写入数据,从而达到通信目的。   [
 
一、服务器的建立
1.首先,建立一个服务器。     
Java中关于网络通信的类在java.net包中,建立服务器需要调用到ServerSocket类,建立服务器也就是new一个ServerSocket: 
ServerSocket ss = new ServerSocket(8888);
   其中8888表示要建立的服务器的端口号(每台计算机可用的端口号为0~65535,0~1024的端口号为“知名端口”应尽量避免使用),客户机通过该端口号来连上服务器。  
2.然后,等待客户机的连入。
Socket socket = ss.accept();
             服务器建立成功后,应保持等待的状态等待客户机的连入,ss.accept()表示了这一等待过程,在有客户端连入之前程序会阻塞在这一步,直到有客户机访问后返回一个Socket类的对象。  
3.接着,处理服务器接收到的Socket类的对象。              
对于获取到的客户机对象,服务器如果要实现通信,就要从该对象上获取输入输出流,并进行相应的读写操作,从输入流中读取数据,在输出流中写入数据,实现通信。例如:  
try {           InputStream input = socket.getInputStream();
     		OutputStream output = socket.getOutputStream();         
		//向对方发送一个字符串     String str = "欢迎连接!";
 		byte[] b = str.getBytes(); output.write(b); 
		//从网络输入流中读取字符    
		int c = input.read(); 
		System.out.print(c); 
		} catch (IOException e) {         
		e.printStackTrace();    
} 
   当然, 这几行代码只实现了最简单的读写,给客户端发了一句话,从客户端读取了一个字符后服务器就关闭了。如果要不停地从客户机读取数据,可以将读取操作放在循环中进行。而且还要注意到读写数据都是以字节为单位进行的,在实际操作中人们不可能直接操作字节进行聊天,必须进行和字符串的相互转化。        写操作时的转化较为简单,直接“字符串”.getBytes()就可以转化成字节,在write()之后加一个flush()语句,让写的数据强制输出,防止读操作完成而写操作未完成时服务器close()使数据丢失。       从输入流中读数据时,为使代码清晰,定义一个readline()方法,并把输入流作为形参传入该方法。对于输入流中的数据处理方式可以有多种,胡哥讲的是建立StringBuffer即字符串缓冲区,把读到的每一个数据append()进去,然后转化成String;之前强哥讲的方法是建立一个动态数组流ByteArrayOutputStream()读完后转化为字节数组,再转化为字符串,具体操作在如下代码中。 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.net.ServerSocket; 
import java.net.Socket; 
public class MyServer { 
	public void setup(){ 
	try { 	//建立服务器 
		ServerSocket ss = new ServerSocket(8888); 
		System.out.println("服务器建立成功"); 
		Socket socket = ss.accept();//阻塞,等待客户机连入 //获取输入输出流 
		InputStream ins = socket.getInputStream(); 
		OutputStream ous = socket.getOutputStream(); 
		String str = "欢迎连接!"; 		
		byte[] b = str.getBytes("GB2312");//转码,“GB2312”为汉字 
		ous.write(b); ous.flush();//强制输出 
		String s = readline(ins); 
		while(!s.equals("exit")){//如果读到的字符串不是"exit”就退出,否则继续读 
			System.out.println("读到的数据是:"+s); 
			s = readline(ins); 
		} 
		socket.close(); 
		} catch (IOException e) { 
		// TODO Auto-generated catch block 
		e.printStackTrace(); } } 

	//定义一个读取一行字符串的方法 
	public String readline(InputStream input) throws IOException{ 
		//动态数组流,相当于一个动态字节数组,或者一个字节队列 
		ByteArrayOutputStream bytearrayous = new ByteArrayOutputStream(); 
		int c = input.read(); 
		while(c!='\n'){ 
			if(c!='\r')
				bytearrayous.write(c); 
				c = input.read(); 
		} 
		byte [] b = bytearrayous.toByteArray();//转换为字节数组 
		String s = new String(b,"GB2312");//将字节数组转换为字符串
	 	//System.out.println("读到的数据是:"+s); 
		return s; 
	} 

	public static void main(String[] args) { 
		MyServer server = new MyServer(); 
		server.setup(); 
	} 

}
    另外,有IO操作时要注意try{}catch{}或者抛出异常。
4.最后,将服务器与该客户机的连接关闭。
socket.close(); 
     
二、用cmd命令连入服务器        
每一台电脑本身既可以做服务器也可以做客户机,用自己的电脑作为客户端连入这个服务器进行简单的测试,telnet命令是联网操作,输入方式为“telnet ip地址 宽口号”。        
打开cmd,如下图:
因为要连入服务器,所以需要知道服务器也就是本机的ip地址(端口号已知为8888),可以直接输入ipconfig查询本机地址,也可以用localhost直接表示本机地址。 在命令行窗口中输入telnet localhost 8888,点enter确定,弹出如下窗口:
证明连接成功,输入exit就退出,界面上显示遗失对主机的连接。 可是测试时发现如果不输入exit直接关闭cmd界面程序会报错,仔细查找后发现强退时服务器会在循环里不停地读到-1,程序进入死循环。可以在读到-1时让readline函数自动返回"exit”解决这一问题。    
 
 三、实现简单群聊
1.让多个客户机连入服务器
        之前的代码简单的实现了让客户机连入服务器,不过由于服务器accept()每次只能接入一个客户机,而且该客户机退出后服务器也自动关闭了,在实际生活中肯定没有只接待一个客户机就关闭的服务器,所以要让服务器一直开着可以在accept()周围添加一个while(true)语句(虽然直接把true放到判断里不好这里这样做也没什么大的影响)。
         因为不同的客户机是完全独立的,所以要把对客户机连接的处理放到 线程里进行,这样多个客户端可以同时在线。
        建立一个继承自Thread的SocketThread类,注意把socket对象传入处理线程,之前读写操作的处理放到processSocket()中:
public class SocketThread extends Thread{ 
	private Socket socket; 

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

	public void run(){ 
		processSocket(socket); 
	} 

	public void processSocket(Socket socket){} 
}   
   在accept()获取到socket对象后就启动该线程: 
while(true){ 
	Socket socket = ss.accept(); 
	SocketThread socketthread = new SocketThread(socket); 
	socketthread.start(); 
	System.out.println("启动了一个线程"); 
}

  2.让客户机群聊
      服务器面对一个客户机时可以从该客户机的输入流中读取数据,向输出流中写入数据;如果多个客户机在线时服务器可以把每个客户机输入流中读到的数据写给每个输出流,这样就实现了群聊或者消息的广播式推送,如果只对某个客户机发送就实现私聊了。       
      建立一个队列,在每次有客户机连入时将该客户机加入队列,并在读取到某个客户机发来的消息后,将该消息发给该队列里除自己外的所有成员。为了提高代码复用率,将之前写好的往输出流中进行写操作的代码提出来放到一个sendMsg()的方法里,这样发消息时就可以直接调用该方法。 
public void sendMsg(String s) throws IOException{ 
	String st = s+"\n\r";
	byte[] b = st.getBytes(); 
	ous.write(b); 
	ous.flush(); 
}  
 建立队列如下:
public static List socketlist = new ArrayList();
 
  在获取到Socket对象时把该对象的线程加入队列:
while(true){ 
	Socket socket = ss.accept(); 
	SocketThread socketthread = new SocketThread(socket,socketlist); 
	socketlist.add(socketthread); 
	socketthread.start(); 
	System.out.println("启动了一个线程"); 
}
 
 注意修改SocketThread类的构造器,把队列传进去。 把得到的消息发送给除了自己的每个人: 
while(!s.equals("exit")){ Sys
	tem.out.println("读到的数据是:"+s); 
	for(int i=0;i<socketlist.size();i++){ 
		SocketThread so = socketlist.get(i); 
		if(so!=this) 
			so.sendMsg(s); 
	} 
	s = readline(ins); 
}
 
  要注意在某个客户机退出聊天后要将该线程从队列中remove掉。    
为了方便分清楚以及实际情况考虑,需要再建立一个User类,保存用户的姓名和密码,这样在群聊时就可以看见说话人的名字。 定义一个login()方法用来获取用户名和密码: 
public void login(InputStream ins) throws Exception{ 
	String str = "欢迎连接!"; 
	sendMsg(str); str ="请输入用户名:"; 
	sendMsg(str); 
	String name = readline(ins); 
	if(user.getName().equals("exit")) 
		return; 
	str ="请输入密码:"; 
	sendMsg(str); 
	String password = readline(ins); 
	user.setPassword(password); 
	System.out.println("得到的用户密码为:"+user.getPassword()); 
	if(user.getPassword().equals("exit")) 
		return; 
	str="登陆成功!"; 
	sendMsg(str); 
}
 
 群发消息时可以加上用户的名字了:
for(int i=0;i<socketlist.size();i++){ 
	SocketThread so = socketlist.get(i); 
	if(so!=this) 
		so.sendMsg(user.getName()+"说:"+s); 
} 
 
四、为服务器添加界面
        给服务器添加一个界面可以方便管理客户机,直接通过界面上操作对客户机实行群发消息、踢人等操作。 因为accept()获取socket对象时是死循环所以不能把建立界面放在阻塞操作后边,当然最好的方式是把这个循环放到线程里单独执行,再建一个线程等待客户机接入。 下面的代码简单的实现了把服务器界面上的消息群发给客户机的功能(给发送按钮添加监听器,按下按钮时提取文本输入框内的字符串群发给客户机):
  
 public class ServerFrame { 	
	public static List socketlist = new ArrayList(); 
	public void init (){ 
		JFrame serverframe = new JFrame(); 
		serverframe.setTitle("服务器"); 
		serverframe.setSize(500, 400); 
		serverframe.setLocationRelativeTo(null); 
		serverframe.setDefaultCloseOperation(3); 
		JPanel northPanel = createNorthPanel(); 
		serverframe.add(northPanel,BorderLayout.NORTH); 
		serverframe.setVisible(true); 
	} 
	public JPanel createNorthPanel(){ 
		JPanel jp = new JPanel(); 
		final JTextField sendtext = new JTextField(); 
		sendtext.setPreferredSize(new Dimension(300, 30)); 
		JButton send = new JButton("发送"); 
		send.addActionListener(new ActionListener(){ 
			public void actionPerformed(ActionEvent e) { 
				for(int i=0;i<socketlist.size();i++){ 
					SocketThread socketthread = socketlist.get(i); 
					try { 
						socketthread.sendMsg(sendtext.getText()); 
					} catch (Exception e1) { 
						e1.printStackTrace(); 
					} 
				} 
			} 
		}); 
		jp.add(sendtext); 
		jp.add(send); 
		return jp; 
	} 
	public JPanel createCenterPanel(){ 
		JPanel jp = new JPanel(); 
			return jp; 
	} 
	public void initserver(){ 
		ServerThread serverthread = new ServerThread(socketlist); 
		serverthread.start(); 
	} 
	public static void main(String[] args) { 
		ServerFrame sf = new ServerFrame(); 
		sf.initserver(); 
		sf.init(); 
	} 
}
 

五、为客户机添加界面
       前边的操作客户机都是用cmd里telnet来连接服务器的,在实际使用时这样一个原始的操作界面肯定不会让人满意的,所以要让客户机在UI界面上操作。
1.建立客户机
       客户机是独立的,最好放到另一个包下或者再建一个工程。 客户机的实例化和服务器类似,不过客户机的构造器多了一个String 的ip 参数: 
Socket client = new Socket("localhost",8888);
   Socket(String ip地址,int 端口号);
然后也是获取输入输出流并进行读写操作,只不过客户机从服务器的输入流中读取数据,向服务器的输出流中写入数据。
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.net.Socket; 
public class myClient { 
	private OutputStream ous; 
	public void setup(){ 
		try{ 
			Socket client = new Socket("localhost",8888); 
			InputStream ins = client.getInputStream(); 
			ous = client.getOutputStream(); 
			System.out.println("客户机建立成功"); 
			String str = readLine(ins); 
			while(true){ 
				System.out.println("服务器发来的消息是:"+str); 
				str = readLine(ins); 
			} 
		}catch(Exception e){ 
			e.printStackTrace(); 
		} 
	} 
	public String readLine(InputStream in) throws IOException{ 
		ByteArrayOutputStream btayous = new ByteArrayOutputStream(); 
		int c = in.read(); 
		while(c!='\n'){ 
			if(c!='\r') 
				btayous.write(c); 
			c = in.read(); 
		} 
		byte [] b = btayous.toByteArray(); 
		String s = new String(b,"GB2312"); 
		return s; 
	} 
	public static void main(String[] args) { 
		myClient ct = new myClient(); 
		ct.setup(); 
	} 
}
 
 2.添加界面
新建一个类用来实例化界面,把读写操作、对输入输出流的处理放到线程内,基本操作和给服务器添加界面还是一样的。 因为从服务器发来的消息要显示到界面上,所以特别建立了一个MsgListener()的接口,每次服务器发来新的内容时就会触发监听器,将新的字符串输出到界面聊天窗口上。
public interface MsgListener { 
	public void onMsg(String msg); 
}     
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.net.Socket; 

public class ClientThread extends Thread{ 
	private MsgListener listener; 
	private OutputStream ous; 
	private String ip; private int port; 
	private String Msg; 
	public ClientThread(String ip,int port){ 
		this.ip = ip; 
		this.port = port; 
	} 

	//定义一个setListener方法 
	public void setListener(MsgListener listener) { 
		this.listener = listener; 
	} 

	public void run(){ 
		setup(); 
	} 

	public void setup(){ 
		try{ 
			Socket client = new Socket(ip,port); 
			//获取输入输出流 
			InputStream ins = client.getInputStream(); 
			ous = client.getOutputStream(); 
			System.out.println("客户机建立成功"); 
			Msg = readLine(ins); 
			while(true){ 
				if(listener != null){//如果不为空刷新listener里的Msg 
					listener.onMsg(Msg); 
				} 
				System.out.println("服务器发来的消息是:"+Msg); 
				Msg = readLine(ins); 
			} 
		}catch(Exception e){ 
			e.printStackTrace(); 
		} 
	} 

	private String readLine(InputStream in) throws IOException{ 
		//相当于动态字节数组 
		ByteArrayOutputStream btayous = new ByteArrayOutputStream(); 
		int c = in.read(); 
		while(c!='\n'){ 
			if(c!='\r') 
				btayous.write(c); 
			c = in.read(); 
		} 
		byte [] b = btayous.toByteArray(); 
		String s = new String(b,"GB2312");//"GB2312"汉字 
		return s; 
	} 

	//发送消息的方法 
	public void sendMsg(String s){ 
		try{ 
			String st = s+"\n\r"; 
			byte[] b = st.getBytes(); 
			ous.write(b); 
			ous.flush();//强制输出 
		}catch(Exception e){ 
			e.printStackTrace(); 
		} 
	} 
}  
 
import java.awt.BorderLayout; 
import java.awt.Dimension; 
import java.awt.FlowLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTextArea; 
import javax.swing.JTextField; 
	public class ClientFrame { 
		private ClientThread mc; 
		private JTextArea jtreceive; 
		private JTextField jtextclsd; 
		public void init(){ 
			JFrame jf = new JFrame(); 
			jf.setTitle("客户机"); 
			jf.setSize(500, 400); 
			jf.setDefaultCloseOperation(3); 
			jf.setLocationRelativeTo(null); 
			jf.setResizable(false); 
			JPanel northPanel = createNorthPanel(); 
			JPanel centerPanel = createCenterPanel(); 
			jf.add(northPanel,BorderLayout.NORTH); 
			jf.add(centerPanel,BorderLayout.CENTER); 
			jf.setVisible(true); 
		} 
		public JPanel createCenterPanel(){ 
			JPanel jp = new JPanel(); 
			jtextclsd = new JTextField(); 
			jtextclsd.setPreferredSize(new Dimension(350,35)); 
			JButton jbclsd = new JButton("发送"); 
			jbclsd.addActionListener(
				new ActionListener() { 
					public void actionPerformed(ActionEvent e) { 
						mc.sendMsg(jtextclsd.getText()); 
						jtreceive.append("我说:"+jtextclsd.getText()+"\n");//将客户机自己发送的内容显示到聊天界面上 
						jtextclsd.setText(""); 
					} 
			}); 
			jp.add(jtextclsd); 
			jp.add(jbclsd); 
			return jp; 
		} 
	public JPanel createNorthPanel(){ 
		JPanel jp = new JPanel(); 
		jtreceive = new JTextArea(); 
		jtreceive.setPreferredSize(new Dimension(400,300)); 
		jtreceive.setEditable(false);//设置不可编辑 
		jp.add(jtreceive); return jp; 
	} 

	//每收到新的消息就追加到界面上 
	MsgListener l = new MsgListener(){ 
		public void onMsg(String msg) { 
			System.out.println("收到消息:"+msg); 
			jtreceive.append(msg+"\n\r"); 
		} 
	}; 
	public void createClient(){ 
		mc = new ClientThread("localhost",8888); 
		mc.start(); 
		mc.setListener(l); 
	} 
	public static void main(String[] args) { 
		ClientFrame cf = new ClientFrame(); 
		cf.init(); 
		cf.createClient(); 
	} 
} 
 
实际效果:

 
  • 大小: 30.8 KB
  • 大小: 21.8 KB
  • 大小: 15.3 KB
  • 大小: 44.7 KB
0
0
分享到:
评论

相关推荐

    java实现socket编程网络通信多人聊天室

    Java实现的Socket编程是网络通信中的基础技术,它允许两台或多台计算机通过网络进行数据交换,从而实现应用程序之间的通信。在多人聊天室的场景下,Socket编程扮演着至关重要的角色,它使得用户可以通过网络实时地...

    聊天室(C语言)- 基于文件编程、网络通信、数据库实现

    2.私聊 3.群聊:在群聊中若收到私密消息,会以消息闪烁形式通知 4.文件传输 5.管理员权限:禁言、...7.心跳检测:当一用户推出聊天室或掉线,其他在线用户会收到该用户下线通知 用户上线时,其他在线用户也会接到通知

    Socket通信聊天室程序

    采用Socket通信实现的聊天室程序,网络通信 接收端 ,发送端 ,双屏显示

    通信——实现多人聊天室

    在IT行业中,构建一个多人聊天室是常见的应用场景,如标题所示,这通常涉及到网络通信、多线程编程、用户界面设计等技术。这篇博客“通信——实现多人聊天室”(博文链接:...

    基于linux的TCP网络聊天室设计与实现

    【基于Linux的TCP网络聊天室设计与实现】是一个项目,旨在构建一个能在多个客户端之间进行实时通信的系统,尤其强调私聊和群聊功能。在Linux操作系统环境下,TCP(传输控制协议)因其可靠性和面向连接的特性,成为...

    基于java的聊天室设计与实现

    基于Java的聊天室设计与实现 Java语言是当前最流行的编程语言之一,其广泛应用于互联网、移动设备、企业软件等领域。在本文中,我们将讨论基于Java的聊天室设计与实现,涵盖了聊天室的需求分析、总体设计、模块...

    基于C语言Linux下聊天室实现程序源代码(多人群聊+私聊+群主管理员+禁言+踢出群聊+修改密码+找回密码等功能)

    基于C语言Linux下聊天室实现(聊天室+多人群聊+私聊+群主管理员+禁言+踢出群聊+修改密码+找回密码等功能) 在linux下的基于TCP/IP,采用socket通信的聊天室,实现进入聊天室,进行多人群聊,指定人进行私聊,群主...

    VC++网络编程聊天室程序源代码

    本文将详细解析一个基于VC++的网络编程聊天室程序,该程序实现了多点聊天功能,是学习网络编程和理解CSocket类应用的绝佳实例。 首先,我们要理解VC++,它是Microsoft开发的一款集成开发环境,广泛用于Windows平台...

    JAVA聊天室 实现局域网内部通信的软件

    【标题】"JAVA聊天室 实现...总结来说,这个Java聊天室项目展示了Java的广泛用途,包括数据库交互、类型安全、运行时灵活性以及网络通信能力。开发者通过巧妙地融合这些技术,构建了一个高效、可靠的局域网通信工具。

    qt TCP网络编程与聊天室的实现

    在实现聊天室应用时,通常会有一个服务器端(QTcpServer)负责接收多个客户端(QTcpSocket)的连接请求。每个新连接都会分配一个独立的QTcpSocket来处理通信。服务器端可以监听所有客户端的连接,当接收到消息时,...

    基于TCPIP协议的网络聊天室的设计与实现毕业论文.docx

    基于TCP/IP协议的网络聊天室的设计与实现毕业论文 本文主要介绍了基于TCP/IP协议的网络聊天室的设计与实现,讨论了聊天室系统的架构、功能和实现细节。聊天室系统采用C/S模式,基于TCP/IP协议,使用MFC技术实现。...

    网络聊天室java实现

    【网络聊天室java实现】是基于Java编程语言的网络通信项目,主要运用了Java的Socket编程来构建一个多人在线实时交流的平台。在Java中,Socket是网络通信的基础,它为两台机器间的应用程序提供了双向通信的通道。通过...

    java RMI技术实现的网络聊天室

    Java RMI(Remote Method Invocation)技术是Java平台中用于分布式计算的一种机制,它允许一个Java对象调用远程...通过这个项目,开发者可以学习到如何使用RMI创建分布式应用,以及如何处理并发和网络通信等问题。

    计算机网络-聊天室的设计与实现

    (2)聊天室群聊功能:用户A可以在聊天室中对其余所有人说话 (3)服务器开启与关闭:根据TCPSOCKE管理通信,服务器开启监听用户请求链接,若服务器关闭则用户不能登录。 (4)好友状态实时显示:利用客户端守护线程...

    基于Linux系统的简易网络聊天室

    本项目旨在通过实现这样的聊天室,帮助开发者熟悉这些核心概念,从而提升在实际开发中的能力。 【Linux系统】作为开源的操作系统,其稳定性和灵活性使其成为服务器部署的首选平台。在Linux环境中进行网络编程,可以...

    java实现网络聊天室

    在本文中,我们将深入探讨如何使用Java来实现一个基本的网络聊天室。这对于初学者来说是一个极好的项目,可以帮助他们巩固基础知识,理解网络编程的概念,以及如何构建客户端-服务器通信模型。 首先,让我们理解...

    聊天室实现

    聊天室实现 本文主要介绍了基于 Java 语言的聊天室系统的设计和实现。该系统主要由登录页面、验证页面、聊天室页面、离开页面、查看聊客页面、帮助页面等页面构成。整个设计简洁明了、实用,使用简单。本系统实现了...

    网络编程实现聊天室

    综上所述,"网络编程实现聊天室"项目涵盖了MFC、SOCKET套接字、多线程编程、数据传输和错误处理等多个关键知识点,是学习和实践网络通信技术的一个良好实例。通过这个项目,开发者不仅可以提升网络编程技能,还能...

    Java实现简易网络聊天室

    本课题设计主要是采用传统的Client/Server结构来实现自己的聊天室。其实现的主要功能是:用户可以在聊天室中公开发言,也可以发送给某个用户实现两人间的私聊。程序有着自己的聊天服务器和客户端程序。客户端提供...

    网络聊天室设计说明书

    在通信协议设计方面,采用Socket技术实现了客户端与服务器端的网络通信功能;在数据流处理方面,对数据流进行了合理设计,确保信息的准确传递和长期保存。 系统测试 系统测试阶段是评估网络聊天室设计和实现质量的...

Global site tag (gtag.js) - Google Analytics