利用UDP实现用户聊天程序

UDP是面向非连接的,UDP传输的是数据报只负责传输信息,不保证信息一定收到,虽然安全性不如TCP(面向连接、用Socket进行通信),但是性能较好。

从简单到复杂,首先简单介绍一下怎么利用UDP实现客服端发送消息给服务器端吧:

发送、接受消息过程大概如下:

服务器端:创建DatagramSocket对象用于打开指定端口并监听,然后用创建一个DatagramPacket,利用DatagramSocket中的receive(ds)方法接收数据并放到刚创建的DatagramPacket对象中;
这样就完成了简单的发送,接收过程。
客户端:创建DatagramSocket对象,创建好IP地址和端口号后,利用DatagramSocket中的connect(ip,port)方法和服务端建立连接,然后利用DatagramSocket中的send(dp)方法发送早已准备好的数据。
服务器端代码:
package Socket;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class server {
	private DatagramSocket ds = null;
	private int Port = 9999; 
	public server(){	
		try{
			ds = new DatagramSocket(Port);
			byte[] data = new byte[255];
			DatagramPacket dp = new DatagramPacket(data,data.length);
			ds.receive(dp);
			String str = new String(dp.getData());
			System.out.println(str);
		}catch(Exception e){			
		}	
	}
	public static void main(String[] args){
		new server();
	}
}
客户端代码:
package Socket;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class client{	
	private DatagramSocket ds;
	private int Port = 9999;
	public client(){		
		try{		
			ds = new DatagramSocket();
			InetAddress address = InetAddress.getByName("Localhost");
			ds.connect(address,Port);
			String str = "客户端连接";
			byte[] data = str.getBytes();
			DatagramPacket dp = new DatagramPacket(data,data.length);
			ds.send(dp);			
		}catch(Exception e){		
		}
	}
	public static void main(String[] args){
		new client();
	}
}
现在都了解了UDP是怎么实现发送和接收信息的了,怎样实现客户端和服务器端之间的相互聊天呢?这就需要服务器端和客户端能够自动的读取对方传过来的信息了,接受消息是通过调用receive函数,该函数的特点是如果未收到消息就会死等,这样就容易造成程序的阻塞,解决方法是将读取信息的代码写到专门的线程内,这里用Runnable接口。为了使聊天过程更加清楚易懂,加入GUI。

客户端和服务器端聊天过程大概如下:

服务器端:编写一个简单的聊天图形界面,并在构造函数中用已经创建好的DatagramSocket对象打开特点的端口监听,并用Thread开启Runnable线程。重写run()函数,将接受数据的receive函数写在这里,使程序在任何时候都能够接收客户端发送的数据, 注意数据中包含客户端的IP地址,将它保存起来。另外加一个按钮实现服务器端发送数据,过程和上述客户端发送数据差不多,区别在于上述客户端和服务器端已经connect而现在没有,所以创建DatagramPacket对象使需要在后面加上已经保存的客户端IP地址,然后用send发送数据。
客户端:客户端和服务器端都差不多,区别在于,客户端需要在构造函数中先和服务器端建立连接,给服务器端发送一个数据包,表示已经建立连接(其实是告诉服务器端自己的IP地址)然后再打开线程。在run()函数中不用保存服务器地址。在发送消息按钮处不需要给DatagramPacket对象加IP地址,因为客户端早已和服务器端建立连接。
服务器端代码:
package alone;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class Server extends JFrame implements Runnable,ActionListener{
	
	private SocketAddress clientip = null;
	private DatagramSocket DS;
	private int Port = 9999;
	
	private JButton JB = new JButton("发送");
	private JTextField field = new JTextField();
	private JTextArea area = new JTextArea("聊天内容:\n");
	
	public Server(){
		
		this.setTitle("服务器");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.add(JB,BorderLayout.SOUTH);
		JB.addActionListener(this);
		this.add(field, BorderLayout.NORTH);
		this.add(area, BorderLayout.CENTER);
		this.setSize(180, 220);
		this.setVisible(true);
		
		try {
			
			DS = new DatagramSocket(Port);
			new Thread(this).start();
	
		} catch (Exception ex) {
			ex.printStackTrace();
		} 
	}
	
	public void run(){
		try{
			while(true){	
				
				byte[] data = new byte[255];
				DatagramPacket DP = new DatagramPacket(data,data.length);				
				DS.receive(DP);			
				clientip = DP.getSocketAddress(); 				
				String str = new String(DP.getData(),0,DP.getLength());
				area.append(str + '\n');
			}			
		}catch(Exception ex){			
		}		
	}
	public void actionPerformed(ActionEvent e){
		try{
			String str = "服务端说:" + field.getText();
			byte[] dd = str.getBytes();
			DatagramPacket Data = new DatagramPacket(dd,dd.length,clientip);
			DS.send(Data);
		}catch(Exception ex){			
		}
	}
	public static void main(String[] args){
		new Server();
	}
}
客服端代码:
package alone;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class Client extends JFrame implements Runnable,ActionListener {
	
	private JButton JB = new JButton("发送");
	private JTextField field = new JTextField();
	private JTextArea area = new JTextArea("聊天内容:\n");
	
	private int Port = 9999;
	private DatagramSocket DS;
	
	public Client(){
		
		this.setTitle("客户端");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.add(area, BorderLayout.CENTER);
		this.add(JB, BorderLayout.SOUTH);
		JB.addActionListener(this);
		this.add(field, BorderLayout.NORTH);
		this.setSize(180, 220);
		this.setVisible(true);
		
		try {
			DS = new DatagramSocket();
			InetAddress address = InetAddress.getByName("Localhost");
			DS.connect(address,Port);
			
			String str = "客户连接";
			byte[] data = str.getBytes();
			DatagramPacket DP = new DatagramPacket(data,data.length);
			
			DS.send(DP);
			new Thread(this).start();
			
		} catch (Exception e) {			
		}		
	}
	public void run(){
		try{			
			while(true){
				byte[] data = new byte[255];
				DatagramPacket DP = new DatagramPacket(data,data.length);				
				DS.receive(DP);				
				String str = new String(DP.getData());
				area.append(str + '\n');
			}			
		}catch(Exception ex){				
		}
	}
	public void actionPerformed(ActionEvent e){
		try{
			String str = "客户端说:" + field.getText();
			byte[] dd = str.getBytes();
			DatagramPacket Data = new DatagramPacket(dd,dd.length);
			DS.send(Data);
		}catch(Exception ex){			
		}	
	}
	public static void main(String[] args){
		new Client();
	}
}

演练一下:
过程:先打开服务器端,然后在打开客户端,这时候服务器GUI中会显示客户连接,然后就可以在两边的输入框中输入聊天信息。
现在我们已经了解了服务器端和单个客户端是怎么样通信的,但是实际应用中应该是客户端与客户端进行聊天通信的啊,那怎样实现呢?客户端与客户端聊天的本质就是服务器端将收到的信息转发给每一个客户端。

客户端和客户端聊天过程大概如下:

服务器端:服务器端不需要知道有多少客户端要连接,所以就不需要多个线程负责接收客户端连接。服务器端不知道客户端什么时候需要连接,所以就需要开启一个线程来接受客户端发送的消息,注意只需要一个线程即可。服务器端收到消息后,需要发给各个客户端,这就需要发送客户端的IP地址,我们知道用receive()方法可以接受数据,DatagramPacket中就包含了IP地址,由于是多个客户端就需要一个集合专门保存各个客户端的IP地址。
由于在这里服务器端只进行转发数据的作用,所以我减去了不必要的GUI,所以运行完客户端后需要手动关闭服务端程序。
客户端:因为是多个客户端之间进行聊天,所以就需要一个name来区别每一个客户端,打开客户端需要输入昵称。连接服务器、发送消息、接收服务器传来的消息,这些操作和上述的类似,这里不在介绍。( 彩蛋)在客户端GUI中我实现了用回车发送消息,并清空输入框的效果。
服务器端代码:
package group;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.ArrayList;

public class gServer implements Runnable{
	
	private DatagramSocket DS;
	private int Port = 9998;
	
	private ArrayList<SocketAddress> clients = new ArrayList<SocketAddress>(); //保存客户端IP地址
	
	public gServer() throws Exception{		
		try {			
			DS = new DatagramSocket(Port);
			new Thread(this).start();	
		} catch (Exception ex) {
		} 
	}	
	public void run(){
		try{
			while(true){			
				byte[] data = new byte[255];
				DatagramPacket DP = new DatagramPacket(data,data.length);				
				DS.receive(DP);
				
				SocketAddress clientip = DP.getSocketAddress(); 
				
				if(!clients.contains(clientip)){
					clients.add(clientip);
				}
				this.sendAll(DP);
			}		
		}catch(Exception ex){			
		}		
	}	
	private void sendAll(DatagramPacket dp) throws Exception {
		for(SocketAddress sa : clients){
			DatagramPacket dd = new DatagramPacket(dp.getData(),dp.getLength(),sa);				
			DS.send(dd);				
		}
	}
	public static void main(String[] args) throws Exception{
		new gServer();
	}
}

客户端代码:
package group;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class gClient extends JFrame implements Runnable,ActionListener {
	
	private JTextField field = new JTextField();
	private JTextArea area = new JTextArea("聊天内容:\n");
	
	private String name = null;
	
	private int Port = 9998;
	private DatagramSocket DS;
	
	public gClient(){
		
		this.setTitle("客户端");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.add(area, BorderLayout.CENTER);
		this.add(field, BorderLayout.SOUTH);
		field.addActionListener(this);
		this.setSize(220, 290);
		this.setVisible(true);
		
		name = JOptionPane.showInputDialog("输入昵称");
		
		try {
			DS = new DatagramSocket();
			InetAddress address = InetAddress.getByName("Localhost");
			DS.connect(address,Port);
			
			String str = name + "登录!";
			byte[] data = str.getBytes();
			DatagramPacket DP = new DatagramPacket(data,data.length);
			
			DS.send(DP);
			new Thread(this).start();
			
		} catch (Exception e) {
		}		
	}	
	public void run(){
		try{			
			while(true){
				byte[] data = new byte[255];
				DatagramPacket DP = new DatagramPacket(data,data.length);				
				DS.receive(DP);				
				String str = new String(DP.getData(),0,DP.getLength());
				area.append(str + '\n');
			}			
		}catch(Exception ex){				
		}
	}
	public void actionPerformed(ActionEvent e){
		try{
			String str = name + "说:" + field.getText();
			byte[] dd = str.getBytes();
			DatagramPacket Data = new DatagramPacket(dd,dd.length);
			DS.send(Data);
			field.setText("");
		}catch(Exception ex){			
		}		
	}
	public static void main(String[] args){
		new gClient();
	}
}


演练一下:
过程:先打开服务器端,然后逐次打开客户端,弹出输入框输入昵称这里是Dkangel、yc、xx,然后在各自的GUI输入框中输入想发送的消息,回车就可以了。
到这里我所要分享的内容就已经完了,由于自己比较懒所以代码基本没有什么注释,就这样吧。good night!
  • 32
    点赞
  • 143
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 为了使用Qt框架实现局域网聊天室,我们可以使用UDP协议进行通信。UDP是一种面向无连接的传输协议,适用于传输不需要高可靠性和有序到达的数据。 首先,我们需要创建一个UDP的Socket,并绑定到一个合适的端口。接下来,我们可以使用Qt提供的信号与槽机制来处理接收到的消息。 为了实现聊天室功能,我们需要至少两个应用实例,一个用作服务器,另一个用作客户端。当客户端启动时,它将向服务器发送一个加入聊天室的请求。 当服务器接收到一个加入请求时,它可以将发送者的IP地址和端口存储在一个列表中,以便之后可以将消息广播给所有的聊天室成员。 当一个客户端发送一条消息时,它将将消息发送给服务器,服务器收到消息后,将消息广播给所有已连接的客户端。客户端接收到广播后,可以将消息显示在聊天窗口中。 需要注意的是,UDP是无连接的协议,因此在实现聊天室时,需要处理丢包和乱序的问题。可以使用一些技术,如序列号、心跳检测等来解决这些问题。 此外,为了提高用户体验,我们还可以在聊天室中实现一些额外的功能,如私、文件传输等。 总之,通过使用Qt和UDP协议,我们可以很容易地实现一个简单的局域网聊天室。使用UDP协议可以简化通信过程,并提供轻量级的通信方式。不过也要注意到UDP的不可靠性,需要在实现中考虑丢包和乱序问题。 ### 回答2: Qt是一个跨平台的C++应用程序开发框架,可以用于开发各种类型的应用程序,包括局域网聊天室。首先要实现局域网聊天室,需要使用UDP协议进行通信,因为UDP是无连接的、不可靠的传输协议,适合于实时性要求较高的应用。 在Qt中,可以使用QUdpSocket类来实现UDP通信。首先,需要创建一个QUdpSocket对象,并绑定到本地IP和端口上。接下来,可以通过该对象的bind函数将socket绑定到指定的本地IP和端口上。然后,可以使用该对象的readDatagram函数接收其他主机发送过来的消息,并使用writeDatagram函数发送消息给其他主机。 在局域网聊天室中,需要实现多个主机之间的消息传递。可以使用多线程来处理消息接收和发送的操作,以免阻塞主线程。可以创建一个接收线程,通过QUdpSocket来接收其他主机发送的消息,并将接收到的消息发送给主线程进行处理。同时,可以创建一个发送线程,用于向其他主机发送消息。 在主线程中,可以使用Qt的信号和槽机制来处理接收到的消息。当接收线程接收到消息后,可以通过信号将消息传递给主线程,并在主线程中的槽函数中进行处理,例如显示在用户界面上。当用户在界面上输入消息后,可以通过发送线程将消息发送给其他主机。 总结起来,使用Qt的QUdpSocket类可以方便地实现局域网聊天室。通过创建多线程利用信号和槽机制处理接收和发送的消息,可以实现实时的消息传递。使用UDP协议可以满足聊天室的实时性要求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值