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

news/2024/5/18 12:08:06 标签: java, socket, 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;

两线程大致框架:

java">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界面,有几个按钮和文本区,对对应的按钮进行监听,按下某个按钮进行使用者想要的操作,所以我们的代码应该变成这样:

java">//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;
    }
}

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

java">//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.


http://www.niftyadmin.cn/n/1403684.html

相关文章

注册表自举

什么是注册表自举?它在系统中的作用是什么? n注册表自举是由设备管理器(Device.exe)在系统启动时加载的。 n注册表自举时,通过读取注册表来寻找系统中的新设备。设置新设备的注册表信息应当是在安装驱动程序时&#…

使用不可靠的UDP设计可靠的文件传输协议

一、了解UDP协议一些具体细节 (可以直接跳到二、三看协议设计部分) UDP协议的工作是将待发送的网络数据流量压缩成数据报的形式,然后由服务器端发送给客户端。但是UDP协议是面向无连接的,它只提供最大努力的服务,也就…

wince中文件关联的问题

Platform Builder for Microsoft Windows CE 5.0 1、通常情况下,点击一个mp3文件,会自动地调用Mediaplayer播放。 如果需要,我们也可以实现当点击一个特定格式的文件时,调用我们自己的程序来处理。 先对注册表进行一些处理。 比如…

八皇后问题详解(四种解法)

所有源码都在github上(https://github.com/seasonyao/eight_queen_question) 如果你去百度百科八皇后这个问题,你会发现人家也是历史上有头有脸的一个问题,最后一句“计算机发明后就有一万种方式解决这个问题”读起来也让程序猿们…

SerialPort类源代码分析

前几篇串口编程大致讲述了Windows下串口的大致操作,接下来分析流行的SerialPort类,它把Windows API封装好,方便开发利用 1、Win32下串口大致操作流程(1)打开串口:CreateFile函数(2)建立串口通信事件:CreateEvent函数(3)初始化串口…

企业常用网管软件介绍及配置说明

企业内最苦最累的应该就是网管了,俗称“救火队员”,往往一个企业配有几个网络管理员,他们负责着企业内部所有电脑的正常运行,网络的稳定畅通,如此多的工作如果要完全人工来完成的话,会花费大量的时间与精力…

HTTP学习与Web服务器编程

这次的主题是查找HTTP协议的相关资料,基于此编写一个简单的Web服务器。 需要完成的几大主要的要求有: 1)编写一个简单的Web服务器; 2)实现的服务器应能与标准的浏览器进行简单的交互; 3)记录…

参加全国IT博客大赛了,请您投上宝贵一票,多谢

参加全国IT博客大赛了,请您投上宝贵一票,多谢 投票地址: http://2010blog.51cto.com/350944 2010中国十大杰出IT博客大赛(http://2010blog.51cto.com/index.php),大家一起报名吧。