分类 后端 下的文章

Java实现聊天室功能

前提

1. UDP(用户数据报协议)

使用UDP协议时,在客户和服务器程序都可以放置一个DatagramSocket(数据报套接字),DatagramSocket 用于收发数据包,而DaragramPacket包含了具体的信息。准备接收一个数据报时,只需提供一个缓冲区,以便安置接收到的数据。数据包抵达时,通过DatagramSocket,作为信息起源地的因特网地址以及端口编号会自动得到

2. IntAddress类

用于描述和包装一个Internet IP 地址,通过三个方法返回 InetAddress 实例

3. Swing类 和 AWT

构建图形界面

 

服务器端

1. 发送/接收消息

创建 DatagramSocket 对象用于打开指定端口并监听,然后用创建一个 DatagramPacket,利用 DatagramSocket 中的 receive(ds)方法接收数据并放到刚创建的 DatagramPacket 对象中

2. 客户端和服务器端聊天

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

3. 客户端和客户端聊天

服务器端不需要知道有多少客户端要连接,所以就不需要多个线程负责接收客户端连接。服务器端不知道客户端什么时候需要连接,所以就需要开启一个线程来接受客户端发送的消息,注意只需要一个线程即可。服务器端收到消息后,需要发给各个客户端,这就需要发送客户端的IP地址,我们知道用receive()方法可以接受数据,DatagramPacket中就包含了IP地址,由于是多个客户端就需要一个集合专门保存各个客户端的IP地址

代码

/**
 *@author big_fw
 *@version 1.0
 */
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();
	}
}

 

客户端

1. 发送/接收消息

创建DatagramSocket对象,创建好IP地址和端口号后,利用DatagramSocket中的connect(ip,port)方法和服务端建立连接,然后利用DatagramSocket中的send(dp)方法发送早已准备好的数据。

2. 客户端和服务器端

客户端和服务器端都差不多,区别在于,客户端需要在构造函数中先和服务器端建立连接,给服务器端发送一个数据包,表示已经建立连接(其实是告诉服务器端自己的IP地址)然后再打开线程。在run()函数中不用保存服务器地址。在发送消息按钮处不需要给DatagramPacket对象加IP地址,因为客户端早已和服务器端建立连接

3. 客户端和客户端

因为是多个客户端之间进行聊天,所以就需要一个name来区别每一个客户端,打开客户端需要输入昵称。连接服务器、发送消息、接收服务器传来的消息,这些操作和上述的类似,这里不在介绍。( 彩蛋)在客户端GUI中我实现了用回车发送消息,并清空输入框的效果。

代码

客户端1

 

package group;

import java.awt.BorderLayout; //边界布局设置了一个容器,安排和调整其组件,以适应五个区域:北,南,东,西和中心
import java.awt.event.ActionEvent;//指示组件定义的动作发生的语义事件,当事件发生时,实现ActionListener接口的对象获得此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); //private JTextArea area = new JTextArea("聊天内容:\n");
        this.add(field, BorderLayout.SOUTH); //private JTextField field = new JTextField();
        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();
    }
}

 

客户端2

 

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 gClient2 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 gClient2(){

        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 group.gClient();
    }
}

 

 

--> Java实现聊天室功能前提1. UDP(用户数据报协议)使用UDP协议时,在客户和服务器程序都可以放置一个DatagramSocket(数据报套接字),DatagramSocket 用于收发数据包,而DaragramPacket包含了具体的信息。准备接收一个数据报时,只需提供一个缓冲区,以便安置接收到的数据。数据包抵达时,通过DatagramSocket,作为信息起源地的因特网地址以及端口编...

public class MyThread2 implements Runnable{
	int count = 1, number;

	public MyThread2(int i){
		number = i;
		System.out.println("创建线程"+number);
	}

	public void run(){
		while(true){
			System.out.println("线程"+number+":计数"+count);
			if(++count == 6)
				return;
		}
	}

	public static void main(String[] args) {
		for(int i=0; i<5; i++){
			new Thread(new MyThread2(i+1)).start();
		}
	}
}

结果

创建线程1
创建线程2
创建线程3
线程1:计数1
线程1:计数2
线程2:计数1
线程2:计数2
线程2:计数3
线程3:计数1
线程1:计数3
线程3:计数2
线程3:计数3

 

--> 前提            通过实现接口创建线程的方法通过生成实现java.lang.Runnable接口的类创建多线程。该接口只定义了一个方法run(),所以必须在新类中实现它。但是Runnable接口并没有任何对线程的支持,还必须创建Thread类的实例,这一:点可以通过Thread类的构造方法public Thread( Runnable target)来实现。通过这种方式实现多线程还...

     

前提

主要通过继承java.lang.Thread类,并覆盖Thread类的run()方法完成线程的创建

Thread类是一个具体的类,即不是抽象类,该类封装了线程的行为。要创建线程,需先创建一个Thread类的子类,Thread类中有两个最重要的方法run()和start()

run()方法必须重写,把线程所要的代码加入到这个方法中,也就是线程体

虽然run()方法是线程体,但不能直接调用,而是通过调用start()方法来启动线程。在调用start()的时候,start()方法会首先进行与多线程相关的初始化,然后调用run()方法

start和run方法

start: 用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法run称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随机终止。


run:run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的

代码

public class MyThread extends Thread{
	//count变量用于统计打印次数并共享变量
	private static int count = 0;
	public MyThread(String name)
	{
		//super 关键字可以在子类的构造方法中显式地调用父类的构造方法
		//访问父类的成员方法和变量
		super(name);
	}
	public static void main(String[] args) {
		//main方法开始
		//实例化线程
		MyThread p = new MyThread("t1");
		p.start();
		//主线程main方法执行一个循环
		for(int i=1; i<4; i++){
			count++;
			//主线程中打印count+"main"变量的值,并换行
			System.out.println("主线程 "+ count +" main");
		}
	}

	public void run(){
		//线程类必须有run()方法
		for(int i=1; i<4; i++){
			count++;
			System.out.println("run方法 "+count+": "+this.getName());
		}
	}
}

运行结果

主线程 1 main
run方法 2: t1
主线程 3 main
run方法 4: t1
主线程 5 main
run方法 6: t1

这段程序用Java虚拟机启动程序后,main方法生成新线程t1,并通过for循环输出变量count的值和线程名称

-->      前提主要通过继承java.lang.Thread类,并覆盖Thread类的run()方法完成线程的创建Thread类是一个具体的类,即不是抽象类,该类封装了线程的行为。要创建线程,需先创建一个Thread类的子类,Thread类中有两个最重要的方法run()和start()run()方法必须重写,把线程所要的代码加入到这个方法中,也就是线程体虽然run...

运用继承,接口,包等技术,编写求解几何图形(包括三角形,矩形,圆)的周长,面积

//package oop123;              //包
import java.io.*;              //引入键盘输入所需要的类所在的包
interface getProperty{         //接口定义,方法声明
	double Pi = 3.1415926;
	double getArea();          //面积
	double getCircum();        //周长
	String getName();          //名称
}

class mpoint{                  //定义点
	static int i = 0;
	double x,y;
	mpoint(double a,double b){
		this.x = a;
		this.y = b;
	}
	double getX(){
		return x;
	}
	double getY(){
		return y;
	}

}

class disp{                  //定义屏幕输出所需要的类
	double area;             //面积
	double circum;           //周长
	String drawingName;      //名称
	disp(double a,double b,String ss){
		this.area = a;
		this.circum = b;
		this.drawingName = ss;
	}
	public void display(){
		System.out.println("图像 是 "+ drawingName);
		System.out.println("面积 = "+area+'\n'+"周长 = "+circum);
	}
}

class triangle implements getProperty{    //定义三角形
	mpoint p1,p2,p3;                      //三点
	double s1,s2,s3;                      //三边 
	String drawingName = "triangle";
	triangle(mpoint p1,mpoint p2, mpoint p3){ //构造方法
		this.p1 = p1;
		this.p2 = p2;
		this.p3 = p3;
		this.s1 = Math.sqrt((p1.x - p2.x)*(p1.x - p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
		this.s2 = Math.sqrt((p3.x - p2.x)*(p3.x - p2.x)+(p3.y-p2.y)*(p3.y-p2.y));
		this.s3 = Math.sqrt((p1.x - p3.x)*(p1.x - p3.x)+(p1.y-p3.y)*(p1.y-p3.y));
	}
	 //方法实现
	public double getArea(){      
		double ss,ssa;
		ss = (s1+s2+s3)*Pi/2.0/Pi;
		ssa = Math.sqrt(ss*(ss-s1)*(ss-s2)*(ss-s3));
		return ssa;
	}
	public double getCircum(){
		return s1+s2+s3;
	}
	public String getName(){
        return drawingName;
	}
	public boolean tline(){
		return true;
	}
}

class circle implements getProperty{     //定义圆形
	mpoint p1;                           //圆心
	double radius;                       //半径
	String drawingName = "Circle";
	circle(mpoint p1,double radius){    //构造方法
		this.p1 = p1;
		this.radius = radius;
	}
	public double getArea(){
		double ssa;
		ssa = Math.PI*radius*radius;
		return ssa;
	}
	public double getCircum(){
		return Math.PI*2.0*radius;
	}
	public String getName(){
		return drawingName;
	}
	public boolean tcircle(){
		return true;
	}
}

class rectangle implements getProperty{   //定义长方形
	mpoint p1,p2;
	double s1,s2;
	String drawingName = "Rectangle";
	rectangle(mpoint p1,mpoint p2){
		this.p1 = p1;
		this.p2 = p2;
		this.s1 = Math.sqrt((p1.x-p2.x)*(p1.x-p2.x));
		this.s2 = Math.sqrt((p1.y-p2.y)*(p1.y-p2.y));
	}
	public double getArea(){
		return s1*s2;	
	}
	public double getCircum(){
		return s1+s2+s1+s2;
	}
	public String getName(){
		return drawingName;
	}
	public boolean rline(){
		return true;
	}
}
public class drawing extends disp{      //定义 main() 所在的类
	drawing (double a, double b, String ss){
		super(a,b,ss);
//由于子类不能继承父类的构造方法,因此,如果要调用父类的构造方法,
//可以使用 super 关键字。super 可以用来访问父类的构造方法、普通方法和属性。
	}
	public static void main(String[] args) throws IOException {
         BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
//new BufferedReader 将字符流放到字符流缓冲区之中
//new InputStreamReader 将字节流变为字符流
//System.in 字节输入流
         String strxx;
         for(;true;){
         	System.out.println("输入字符串如 Triangle,Rectangle,Circle:");
         	strxx = keyin.readLine();  //读取输入
         	if(strxx.length() == 0) continue;
         	char charxx;
         	charxx = strxx.toUpperCase().charAt(0);  //大小写转换
         	switch(charxx)
         	{
             	case'T':         //三角形
             	System.out.println("请输入(三角形) 第一个点 x(enter) y(enter)");
             	mpoint p1 = new mpoint(aVar(keyin),aVar(keyin));
             	System.out.println("请输入(三角形) 第二个点 x(enter) y(enter)");
             	mpoint p2 = new mpoint(aVar(keyin),aVar(keyin));
             	System.out.println("请输入(三角形) 第三个点 x(enter) y(enter)");
             	mpoint p3 = new mpoint(aVar(keyin),aVar(keyin));
             	triangle t1 = new triangle(p1,p2,p3);  //构造三角形
             	disp tdisp = new drawing(t1.getArea(),t1.getCircum(),t1.getName());
             	tdisp.display();
             	break;
             	
             	case'C':        //圆形
             	System.out.println("请输入(圆形) 圆心 x(enter) y(enter)");
             	mpoint p4 = new mpoint(aVar(keyin),aVar(keyin));
             	System.out.println("请输入半径 x(enter)");
             	double radius = aVar(keyin);
             	circle t2 = new circle(p4,radius);   //构造圆形
             	disp cdisp = new drawing(t2.getArea(),t2.getCircum(),t2.getName());
             	cdisp.display();
             	break;
             	
             	case'R':        //长方形
                System.out.println("请输入(长方形)第一个点 x(enter) y(enter)");
                mpoint p6 = new mpoint(aVar(keyin),aVar(keyin));
                System.out.println("请输入(长方形)第一个点 x(enter) y(enter)");
                mpoint p7 = new mpoint(aVar(keyin),aVar(keyin));
                rectangle t3 = new rectangle(p6,p7);   //构造长方形
                disp rdisp = new drawing(t3.getArea(),t3.getCircum(),t3.getName());
                rdisp.display();
                break;
                
                default:System.out.println("错误,请输入 t(T),c(C),r(R)");
         	}//switch
         }//for循环
	}//main 方法
	static double aVar(BufferedReader keyin) throws IOException
	{//得到一个双变量
		String xx;
		xx = keyin.readLine();
		return Double.parseDouble(xx);

	}
}

--> 运用继承,接口,包等技术,编写求解几何图形(包括三角形,矩形,圆)的周长,面积//package oop123; //包import java.io.*; //引入键盘输入所需要的类所在的包interface getProperty{ //接口定义,方法声明 double Pi = 3.1415926; double get...