java使用UDP协议进行服务器客户端通信

先唠叨一些基础东西:

1、两台计算机间进行通讯需要以下三个条件:
IP地址、协议、端口号

2、IP地址、端口
为实现网络中不同计算机之间的通信,每台计算机都必须有一个唯一的标识—IP地址。而区分一台主机的多个不同应用程序,则是用端口标识,端口号范围为0-65535,其中0-1023位为系统保留。IP地址+端口号组成了所谓的Socket。

3、Socket套接字:
网络上具有唯一标识的IP地址和端口组合在一起才能构成唯一能识别的标识符套接字。
Socket的原理机制:通信的两端都有Socket,网络通信其实就是Socket间的通信,而数据在两个Socket间通过IO传输

4、Java中的网络支持
针对网络通信的不同层次,Java提供了不同的API,其提供的网络功能有四大类:
InetAddress:用于标识网络上的硬件资源,主要是IP地址
URL:统一资源定位符,通过URL可以直接读取或写入网络上的数据
Sockets:使用TCP协议实现的网络通信Socket相关的类
Datagram:使用UDP协议,将数据保存在用户数据报中,通过网络进行通信。

接下来一步步和大家分享的我的小代码来帮助大家轻松愉快弄懂这东西:
demo:利用socket实现客户端获取服务器当前的时间、日期和指定目录下的文件列表(UDP)

首先第一个问题,怎么在一个Java程序中假装出一个客户端和一个服务器?很容易想到把他们丢在两个线程上运行,这样就好像两方在一唱一和互相进行通信。话不多说,建两个class:client和serve,继承Thread类,重写它的构造函数和run方法。

client头文件:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

server头文件:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

两线程大致框架:

public class XXX extends Thread{
    public ClientThread(String threadName){
    super(threadName);
    }
    public void run(){
        balabala
    }
}

当然还需要一个来跑着两个线程的东西,于是我们再新建class取个名MultiThread,具体内容:

public class TestMultiThread{
    public static void main(String[] args){
    new ServerThread("server").start();
    new Client("client").start();
    }
}

接下来考虑两个线程run里的内容,也是本文核心部分。
第一步,我们应该从server入手,先要有一个服务器出现在那里,你才有目标访问。所以server里率先进行一波操作:
Serve:
1.创建服务器端DatagramSocket,指定端口

DatagramSocket socket;
socket = new DatagramSocket(8800);

2.创建数据报,用于接收客户端发送的数据

byte[] data = new byte[1024];// 创建字节数组,指定接收的数据包的大小
DatagramPacket packet = new DatagramPacket(data, data.length);

3.接收客户端发送的数据

System.out.println("服务器:服务器端已经启动,等待客户端发送数据");
socket.receive(packet);// 此方法在接收到数据报之前会一直阻塞

第二步,现在服务器处于阻塞状态,也就是正在等待客户端一个数据报来使它运作起来,所以这里我们转移阵地到client里向server发送一个请求连接的socket:
Client:
1.定义服务器的地址、端口号、数据

InetAddress address;
address = InetAddress.getByName("localhost");
int port = 8800;
byte[] data = "用户名:admin;密码:123".getBytes();

2.创建数据报,包含发送的数据信息

DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
3.创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket();
4.向服务器端发送数据报
socket.send(packet);

第三步,回到服务器端,要做的就是拿到刚刚receive会得到的数据报并反馈给客户端信息
Serve:
1.读取数据

String info = new String(data, 0, packet.getLength());
System.out.println("服务器:客户端说:" + info);

2.定义客户端的地址、端口号、数据

InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "欢迎您!".getBytes();
3.创建数据报,包含响应的数据信息
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);

4.响应客户端

socket.send(packet2);

5.等待客户端下一次请求

System.out.println("服务器:等待客户端发送请求");
socket.receive(packet);// 此方法在接收到数据报之前会一直阻塞

第四步,客户端收到响应回来的信息再进行处理,完成一次交互

Client:
String reply = new String(data, 0, packet.getLength());
System.out.println("客户端:服务器说:" + reply);

上边我们试验了一下UDP传递数据报在服务器和客户端分别是怎么操作的了,那么接下来还是先来用这种最简单的方法把日期时间和指定目录的路径的操作按照上面的操作来一遍:
Client的run:

try {
InetAddress address;
address = InetAddress.getByName("localhost");
int port = 8800;
byte[] data = "用户名:admin;密码:123".getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
DatagramSocket socket = new DatagramSocket();
socket.send(packet);


socket.receive(packet);
String reply = new String(data, 0, packet.getLength());
System.out.println("客户端:服务器说:" + reply);
data = "客户端:请求服务器当前日期".getBytes();
packet = new DatagramPacket(data, data.length, address, port);
socket.send(packet);
socket.receive(packet);
reply = new String(data, 0, packet.getLength());
System.out.println("客户端:服务器当前日期:" + reply);


data = "客户端:请求服务器当前时间".getBytes();
packet = new DatagramPacket(data, data.length, address, port);
socket.send(packet);
socket.receive(packet);
reply = new String(data, 0, packet.getLength());
System.out.println("客户端:服务器当前时间:" + reply);



data = "客户端:请求服务器指定文件夹列表->C:".getBytes();
packet = new DatagramPacket(data, data.length, address, port);
socket.send(packet);
int count=0;
while(true){
    socket.receive(packet);
    reply = new String(data, 0, packet.getLength());
    if(reply.equals("send all"))break;
    if(count==0)System.out.println("客户端:得到的C:下的文件列表是");
    count++;
    System.out.println(reply);
}
//关闭资源
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

Serve的run:

try {
DatagramSocket socket;
socket = new DatagramSocket(8800);
byte[] data = new byte[1024];// 创建字节数组,指定接收的数据包的大小
DatagramPacket packet = new DatagramPacket(data, data.length);
System.out.println("服务器:服务器端已经启动,等待客户端发送数据");
socket.receive(packet);// 此方法在接收到数据报之前会一直阻塞


String info = new String(data, 0, packet.getLength());
System.out.println("服务器:客户端说:" + info);
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "欢迎您!".getBytes();
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
socket.send(packet2);


System.out.println("服务器:等待客户端发送请求");
socket.receive(packet);// 此方法在接收到数据报之前会一直阻塞


info = new String(data, 0, packet.getLength());
System.out.println("服务器:客户端说:" + info);
address = packet.getAddress();
port = packet.getPort();
data2 = new GetDate().getDate2().getBytes();
packet2 = new DatagramPacket(data2, data2.length, address, port);
socket.send(packet2);


System.out.println("服务器:等待客户端发送请求");
socket.receive(packet);
info = new String(data, 0, packet.getLength());
System.out.println("服务器:客户端说:" + info);
address = packet.getAddress();
port = packet.getPort();
data2 = new GetDate().getTime2().getBytes();
packet2 = new DatagramPacket(data2, data2.length, address, port);
socket.send(packet2);


System.out.println("服务器:等待客户端发送请求");
socket.receive(packet);
info = new String(data, 0, packet.getLength());
System.out.println("服务器:客户端说:" + info);
address = packet.getAddress();
port = packet.getPort();
String[] file=info.split("->");
String [] fileName = new GetFileName().getFileName(file[1]);
for(String name:fileName)
{
data2=name.getBytes();
packet2 = new DatagramPacket(data2, data2.length, address, port);
socket.send(packet2); 
}
data2="send all".getBytes();
packet2 = new DatagramPacket(data2, data2.length, address, port);
socket.send(packet2); 

// 关闭资源
socket.close();
} catch (IOException e) {
e.printStackTrace();
}

这里注意一下得到指定目录的文件列表这项操作,在发送和接受的时候都使用了循环发送接受的方法,当然这里我在客户端使用的收到“send all”就跳出循环的做法会不会提前收到send all的数据报导致少收数据报无伤大雅先跳过。还要注意的是这里面使用的得到日期时间路径的方法都是再建对应的class里写的函数,与主题无关不详述,最后会给上所以文件的代码的。

上边我们像流水账一样写好了几个操作,然而我这是作弊啊,这特么服务器都懂得你要做什么才能对应send receive和调用对应函数,现实怎么可能嘛。而且你也得给我客户提供一个UI界面来几个按钮我想获取什么你服务器响应给我什么才是真理嘛。所以在上面的基础上,我们要做的操作应该是:
服务器端:使用一个while循环receive别人发送的数据报,根据里面的数据信息做相应的操作进行反馈
客户端:写一个简单的UI界面,有几个按钮和文本区,对对应的按钮进行监听,按下某个按钮进行使用者想要的操作,所以我们的代码应该变成这样:

//ServeThread.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class ServerThread extends Thread{
public ServerThread(String threadName){
super(threadName);
}
public void run(){
//创建服务器端DatagramSocket,指定端口
try {
DatagramSocket socket;
socket = new DatagramSocket(8800);
//创建数据报,用于接收客户端发送的数据
byte[] data = new byte[1024];// 创建字节数组,指定接收的数据包的大小
DatagramPacket packet = new DatagramPacket(data, data.length);
//.接收客户端发送的数据
System.out.println("服务器:服务器端已经启动,等待客户端发送数据");
while(true){
socket.receive(packet);// 此方法在接收到数据报之前会一直阻塞
//.读取数据
String info = new String(data, 0, packet.getLength());
System.out.println("服务器:客户端说:" + info);
if(info.equals("断开连接"))break;
InetAddress address = packet.getAddress();
byte[] data2;
DatagramPacket packet2;
int port = packet.getPort();
if(info.equals("客户端:请求服务器当前日期")){                   
data2 = new GetDate().getDate2().getBytes();
packet2 = new DatagramPacket(data2, data2.length, address, port);
socket.send(packet2);
}
if(info.equals("客户端:请求服务器当前时间")){
data2 = new GetDate().getTime2().getBytes();
packet2 = new DatagramPacket(data2, data2.length, address, port);
socket.send(packet2);
}
if(info.contains("客户端:请求服务器指定文件夹列表")){
String[] file=info.split("->");
String [] fileName = new GetFileName().getFileName(file[1]);
for(String name:fileName){
data2=name.getBytes();   
packet2 = new DatagramPacket(data2, data2.length, address, port);
socket.send(packet2); 
}    

data2="send all".getBytes();
packet2 = new DatagramPacket(data2, data2.length, address, port);
socket.send(packet2); 
}
}
socket.close();
System.out.println("服务器已断开连接");
} catch (IOException e) {
e.printStackTrace();
}}}

服务器的这段代码没什么好说的,只是得把GetDate.java和GetFileName.java两个包含getDate()和getFileName()的获取本地时间日期和指定文件夹的目录的文件在这里给出来:

//GetDate.java
import java.util.*;  

public class GetDate {  
    Calendar calendar = null;  

    public GetDate() {  
        calendar = Calendar.getInstance();  
        calendar.setTime(new Date());  
    }  

    public int getYear() {  
        return calendar.get(Calendar.YEAR);  
    }  

    public int getMonth() {  
        return 1 + calendar.get(Calendar.MONTH);  
    }  

    public int getDay() {  
        return calendar.get(Calendar.DAY_OF_MONTH);  
    }  

    public int getHour() {  
        return calendar.get(Calendar.HOUR_OF_DAY);  
    }  

    public int getMinute() {  
        return calendar.get(Calendar.MINUTE);  
    }  

    public int getSecond() {  
        return calendar.get(Calendar.SECOND);  
    }  

    public String getDate() {  
        return getMonth() + "/" + getDay() + "/" + getYear();  
    }  

    public String getTime() {  
        return getHour() + ":" + getMinute() + ":" + getSecond();  
    }  

    public String getDate2() {  
        String yyyy = "0000", mm = "00", dd = "00";  
        yyyy = yyyy + getYear();  
        mm = mm + getMonth();  
        dd = dd + getDay();  
        yyyy = yyyy.substring(yyyy.length() - 4);  
        mm = mm.substring(mm.length() - 2);  
        dd = dd.substring(dd.length() - 2);  
        return yyyy + "/" + mm + "/" + dd;  
    }  

    public String getTime2() {  
        String hh = "00", mm = "00", ss = "00";  
        hh = hh + getHour();  
        mm = mm + getMinute();  
        ss = ss + getSecond();  
        hh = hh.substring(hh.length() - 2, hh.length());  
        mm = mm.substring(mm.length() - 2, mm.length());  
        ss = ss.substring(ss.length() - 2, ss.length());  
        return hh + ":" + mm + ":" + ss;  
    }  
}
//GetFileName.java
import java.io.File;
public class GetFileName
{
    public String [] getFileName(String path)
    {
        File file = new File(path);
        String [] fileName = file.list();
        return fileName;
    }
}

最后,给出的是客户端代码,里面添加了四个按钮分别是“获取服务器日期”、“获取服务器时间”、“获取服务器指定目录的文件名”以及一个退出的按钮,文本框用来输入指定的目录(记住用英文的:和\)

//Client.java
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


import javax.swing.JTextField;


public class Client extends Thread{
    public Client(String threadName){
        super(threadName);
    }
    public void run(){
        String content="";
        // 创建窗体对象
        Frame f = new Frame("添加按钮");
        // 设置属性
        f.setBounds(400, 200, 400, 300);
        // 设置布局为流式布局
        f.setLayout(new GridLayout(5,1) );

        // 创建按钮对象
        Button bu1 = new Button("向服务器获取日期");
        f.add(bu1);
        Button bu2 = new Button("向服务器获取时间");
        f.add(bu2);
        Button bu3 = new Button("向服务器获取文件夹列表");
        f.add(bu3);
        JTextField direction;
        direction = new JTextField("输入指定目录(注意用英文格式的符号),比如“C:”");
        f.add(direction);
        Button bu4 = new Button("退出");
        f.add(bu4);
        // 设置窗体可以关闭
        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                try {
                    InetAddress address;
                    address = InetAddress.getByName("localhost");
                    int port = 8800; 

                    byte[] data = "断开连接".getBytes();
                    DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
                    DatagramSocket socket = new DatagramSocket();
                    socket.send(packet);
                    socket.close();
                }catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                System.exit(0);//退出JVM
            }
        });
        bu4.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    InetAddress address;
                    address = InetAddress.getByName("localhost");
                    int port = 8800; 

                    byte[] data = "断开连接".getBytes();
                    DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
                    DatagramSocket socket = new DatagramSocket();
                    socket.send(packet);
                    socket.close();
                }catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                System.exit(0);//退出JVM
            }});
        bu1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    InetAddress address;
                    address = InetAddress.getByName("localhost");
                    int port = 8800; 

                        byte[] data = "客户端:请求服务器当前日期".getBytes();
                        DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
                        DatagramSocket socket = new DatagramSocket();
                        socket.send(packet);



                        socket.receive(packet);
                        String reply = new String(data, 0, packet.getLength());
                        System.out.println("客户端:服务器当前日期:" + reply);
                        socket.close();
                }catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }});

        bu2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    InetAddress address;
                    address = InetAddress.getByName("localhost");
                    int port = 8800; 

                        byte[] data = "客户端:请求服务器当前时间".getBytes();
                        DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
                        DatagramSocket socket = new DatagramSocket();
                        socket.send(packet);



                        socket.receive(packet);
                        String reply = new String(data, 0, packet.getLength());
                        System.out.println("客户端:服务器当前时间:" + reply);
                        socket.close();
                }catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }});

        bu3.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    InetAddress address;
                    address = InetAddress.getByName("localhost");
                    int port = 8800; 
                    String temp="客户端:请求服务器指定文件夹列表->"+direction.getText();
                    System.out.println(temp);
                    byte[] data = temp.getBytes();
                    DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
                    DatagramSocket socket = new DatagramSocket();
                    socket.send(packet);
                    int count=0;
                    while(true){
                        socket.receive(packet);
                        String reply = new String(data, 0, packet.getLength());
                        if(reply.equals("send all"))break;
                        if(count==0)System.out.println("客户端:指定路径下的文件列表是");
                        count++;
                        System.out.println(reply);
                    }
                    socket.close();
                }catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }});


        // 窗体显示
        f.setVisible(true);
    }
}
运行前面给出的TestMultiThread.java
//TestMultiThread.java
public class TestMultiThread{
    public static void main(String[] args){
        new ServerThread("服务器").start();
        new Client("client").start();
    }
}

然后对应的操作会给出对应的结果,比如获取时间:

这就是今天的分享
That’s all.Thank you.


版权声明:本文为codes_first原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。