java实现基于UDP协议网络Socket编程(C/S通信)

一、前言:认识UDP

UDP,全称User Datagram Protocol(用户数据报协议),是Internet 协议集支持一个无连接的传输协议。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。

UDP主要用于不要求分组顺序到达的传输中,分组传输顺序的检查与排序由应用层完成,提供面向报文的简单不可靠信息传送服务。UDP 协议基本上是IP协议与上层协议的接口,适用端口分别运行在同一台设备上的多个应用程序。

二、UDP的特点(与TCP相比)

正是UDP提供不可靠服务,具有了TCP所没有的优势。无连接使得它具有资源消耗小,处理速度快的优点,所以音频、视频和普通数据在传送时经常使用UDP,偶尔丢失一两个数据包,也不会对接收结果产生太大影响。

UDP有别于TCP,有自己独立的套接字(IP+Port),它们的端口号不冲突。和TCP编程相比,UDP在使用前不需要进行连接,没有流的概念。

如果说TCP协议通信与电话通信类似,那么UDP通信就与邮件通信类似:不需要实时连接,只需要目的地址;

UDP通信前不需要建立连接,只要知道地址(ip地址和端口号)就可以给对方发送信息;

基于用户数据报文(包)读写;

UDP通信一般用于线路质量好的环境,如局域网内,如果是互联网,往往应用于对数据完整性不是过于苛刻的场合,例如语音传送等。

以上是对UDP的基本认识,与以前学习的理论相比,接下来的实践更加有趣,实践出真知。

三、UDP网络Socket编程(Java实现)

首先,熟悉java中UDP编程的几个关键类:DatagramSocket(套接字类),DatagramPacket(数据报类),MulticastSocket。本篇主要使用前两个。

1、创建客户端

第一步,实例化一个数据报套接字,用于与服务器端进行通信。与TCP不同,UDP中只有DatagramSocket一种套接字,不区分服务端和客户端,创建的时候并不需要指定目的地址,这也是TCP协议和UDP协议最大的不同点之一。

public UDPClient(String remoteIP,String remotePort) throws IOException{
    this.remoteIP=InetAddress.getByName(remoteIP);
    this.remotePort=Integer.parseInt(remotePort);
    //创建UDP套接字,系统随机选定一个未使用的UDP端口绑定
    socket=new DatagramSocket();
}

第二步, 创建UDP数据报,实现发送和接收数据的方法。UDP发送数据是基于报文DatagramPacket,网络中传递的UDP数据都要封装在这种自包含的报文中。

实现DatagramPacket发送数据的方法:

//定义一个数据的发送方法
public void send(String msg){
  try {
    //将待发送的字符串转为字节数组
    byte[] outData=msg.getBytes("utf-8");
    //构建用于发送的数据报文,构造方法中传入远程通信方(服务器)的ip地址和端口
    DatagramPacket outPacket=new DatagramPacket(outData,outData.length,remoteIP,remotePort);
    //给UDP发送数据报
    socket.send(outPacket);
  }catch (IOException e){
      e.printStackTrace();
   }
}

DatagramPacket接收数据的方法:

//定义一个数据的发送方法
public void send(String msg){
  try {
    //将待发送的字符串转为字节数组
    byte[] outData=msg.getBytes("utf-8");
    //构建用于发送的数据报文,构造方法中传入远程通信方(服务器)的ip地址和端口
    DatagramPacket outPacket=new DatagramPacket(outData,outData.length,remoteIP,remotePort);
    //给UDP发送数据报
    socket.send(outPacket);
  }catch (IOException e){
      e.printStackTrace();
   }
}

可以看到,发送和接收数据中使用DatagramSocket的实例的send和receive方法,这就是数据报套接字的两个重要方法。

通信结束,销毁Socket的方法如下:

public void close(){
  if (socket!=null)
    socket.close();
}

到这里,客户端已全部完成,等待接下来与服务端的通信...

2、客户端图形界面

现在,设计客户端通信的简单界面,一方面可以更方便的和服务器连续对话通信,另一方面,有了图形界面,体验感更加!图形化界面主要使用JavaFX实现,代码容易看懂。

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;

public class UDPClientFX extends Application {

  private Button btnExit=new Button("退出");
  private Button btnSend = new Button("发送");

  private TextField tfSend=new TextField();//输入信息区域

  private TextArea taDisplay=new TextArea();//显示区域
  private TextField ipAddress=new TextField();//填写ip地址
  private TextField tfport=new TextField();//填写端口
  private Button btConn=new Button("连接");
  private UDPClient UDPClient;

  private String ip;
  private String port;

  @Override
  public void start(Stage primaryStage) {
    BorderPane mainPane=new BorderPane();

    //连接服务器区域
    HBox hBox1=new HBox();
    hBox1.setSpacing(10);
    hBox1.setPadding(new Insets(10,20,10,20));
    hBox1.setAlignment(Pos.CENTER);
    hBox1.getChildren().addAll(new Label("ip地址:"),ipAddress,new Label("端口:"),tfport,btConn);
    mainPane.setTop(hBox1);

    VBox vBox=new VBox();
    vBox.setSpacing(10);

    vBox.setPadding(new Insets(10,20,10,20));
    vBox.getChildren().addAll(new Label("信息显示区"),taDisplay,new Label("信息输入区"),tfSend);

    VBox.setVgrow(taDisplay, Priority.ALWAYS);
    mainPane.setCenter(vBox);

    HBox hBox=new HBox();
    hBox.setSpacing(10);
    hBox.setPadding(new Insets(10,20,10,20));
    hBox.setAlignment(Pos.CENTER_RIGHT);
    hBox.getChildren().addAll(btnSend,btnExit);
    mainPane.setBottom(hBox);

    Scene scene =new Scene(mainPane,700,500);
    primaryStage.setScene(scene);
    primaryStage.show();

    //连接服务器之前,发送bye后禁用发送按钮,禁用Enter发送信息输入区域,禁用下载按钮
    btnSend.setDisable(true);
    tfSend.setDisable(true);

    //连接按钮
    btConn.setOnAction(event -> {
      ip=ipAddress.getText().trim();
      port=tfport.getText().trim();

      try {
        UDPClient = new UDPClient(ip,port);
        //连接服务器之后未结束服务前禁用再次连接
        btConn.setDisable(true);
        //重新连接服务器时启用输入发送功能
        tfSend.setDisable(false);
        btnSend.setDisable(false);
      } catch (IOException e) {
        e.printStackTrace();
      }
    });

    //发送按钮事件
    btnSend.setOnAction(event -> {
      String msg=tfSend.getText();
      UDPClient.send(msg);//向服务器发送一串字符
      taDisplay.appendText("客户端发送:"+msg+"\n");

      String Rmsg=null;
      Rmsg=UDPClient.receive();
//      System.out.println(Rmsg);
      taDisplay.appendText(Rmsg+"\n");

      if (msg.equals("bye")){
        btnSend.setDisable(true);//发送bye后禁用发送按钮
        tfSend.setDisable(true);//禁用Enter发送信息输入区域
        //结束服务后再次启用连接按钮
        btConn.setDisable(false);
      }
      tfSend.clear();
    });
    //对输入区域绑定键盘事件
    tfSend.setOnKeyPressed(new EventHandler<KeyEvent>() {
      @Override
      public void handle(KeyEvent event) {
        if(event.getCode()==KeyCode.ENTER){
          String msg=tfSend.getText();
          UDPClient.send(msg);//向服务器发送一串字符
          taDisplay.appendText("客户端发送:"+msg+"\n");

          String Rmsg=null;
          Rmsg=UDPClient.receive();
          taDisplay.appendText(Rmsg+"\n");

          if (msg.equals("bye")){
            tfSend.setDisable(true);//禁用Enter发送信息输入区域
            btnSend.setDisable(true);//发送bye后禁用发送按钮
            //结束服务后再次启用连接按钮
            btConn.setDisable(false);
          }
          tfSend.clear();
        }
      }
    });

    btnExit.setOnAction(event -> {
      try {
        exit();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

    });
    //窗体关闭响应的事件,点击右上角的×关闭,客户端也关闭
    primaryStage.setOnCloseRequest(event -> {
      try {
        exit();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });

    //信息显示区鼠标拖动高亮文字直接复制到信息输入框,方便选择文件名
    //taDispaly为信息选择区的TextArea,tfSend为信息输入区的TextField
    //为taDisplay的选择范围属性添加监听器,当该属性值变化(选择文字时),会触发监听器中的代码
    taDisplay.selectionProperty().addListener(((observable, oldValue, newValue) -> {
      //只有当鼠标拖动选中了文字才复制内容
      if(!taDisplay.getSelectedText().equals(""))
        tfSend.setText(taDisplay.getSelectedText());
    }));
  }

  private void exit() throws InterruptedException {
    if (UDPClient!=null){
      //向服务器发送关闭连接的约定信息
      UDPClient.send("bye");
      UDPClient.close();
    }
    System.exit(0);
  }

  public static void main (String[] args) {
      launch(args);
  }
}

重点在各个控件的事件处理逻辑上,需避免要一些误操作导致异常抛出,如:连接服务器前禁用发送按钮,在连接服务器成功后禁用连接按钮,禁用输入区等。另外,实现了回车发送的快捷功能,详见代码的键盘事件绑定部分。

还有,约定发送"bye"或者退出按钮结束通信关闭Socket。

成功连接后:

3、创建服务器端

服务器端为客户端提供服务,实现通信。这里包括了几个方法Service(),udpSend()和udpReceive().

首先,我将UDP数据报发送和接收写成一个方法,作为整体方便多次调用。

public DatagramPacket udpReceive() throws IOException {
  DatagramPacket receive;
  byte[] dataR = new byte[1024];
  receive = new DatagramPacket(dataR, dataR.length);
  socket.receive(receive);
  return receive;
}

public void udpSend(String msg,InetAddress ipRemote,int portRemote) throws IOException {
  DatagramPacket sendPacket;
  byte[] dataSend = msg.getBytes();
  sendPacket = new DatagramPacket(dataSend,dataSend.length,ipRemote,portRemote);
  socket.send(sendPacket);
}

与TCP的Socket通信不同,需要将数据转化成字节数据形式,封装成数据报进行传输,接收时解析数据为字节,再进行读取。

服务器端核心部分为Service()方法,实例化一个DatagramSocket类套接字,实现循环与客户端的通信。

与客户端约定的结束标志"bye"进行处理,结束对话。

public DatagramPacket udpReceive() throws IOException {
  DatagramPacket receive;
  byte[] dataR = new byte[1024];
  receive = new DatagramPacket(dataR, dataR.length);
  socket.receive(receive);
  return receive;
}

public void udpSend(String msg,InetAddress ipRemote,int portRemote) throws IOException {
  DatagramPacket sendPacket;
  byte[] dataSend = msg.getBytes();
  sendPacket = new DatagramPacket(dataSend,dataSend.length,ipRemote,portRemote);
  socket.send(sendPacket);
}

四、服务器端和客户端完整代码

服务器端:

public DatagramPacket udpReceive() throws IOException {
  DatagramPacket receive;
  byte[] dataR = new byte[1024];
  receive = new DatagramPacket(dataR, dataR.length);
  socket.receive(receive);
  return receive;
}

public void udpSend(String msg,InetAddress ipRemote,int portRemote) throws IOException {
  DatagramPacket sendPacket;
  byte[] dataSend = msg.getBytes();
  sendPacket = new DatagramPacket(dataSend,dataSend.length,ipRemote,portRemote);
  socket.send(sendPacket);
}

客户端:

//UDPClient.java

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

public class UDPClient {
  private int remotePort;
  private InetAddress remoteIP;
  private DatagramSocket socket;
  //用于接收数据的报文字节数组缓存最最大容量,字节为单位
  private static final int MAX_PACKET_SIZE=512;

  public UDPClient(String remoteIP,String remotePort) throws IOException{
    this.remoteIP=InetAddress.getByName(remoteIP);
    this.remotePort=Integer.parseInt(remotePort);
    //创建UDP套接字,系统随机选定一个未使用的UDP端口绑定
    socket=new DatagramSocket();

  }

  //定义一个数据的发送方法
  public void send(String msg){
    try {
      //将待发送的字符串转为字节数组
      byte[] outData=msg.getBytes("utf-8");
      //构建用于发送的数据报文,构造方法中传入远程通信方(服务器)的ip地址和端口
      DatagramPacket outPacket=new DatagramPacket(outData,outData.length,remoteIP,remotePort);
      //给UDP发送数据报
      socket.send(outPacket);
    }catch (IOException e){
      e.printStackTrace();
    }
  }

  public String receive(){
    String msg;
    //准备空的数据报文
    DatagramPacket inPacket=new DatagramPacket(new byte[MAX_PACKET_SIZE],MAX_PACKET_SIZE);
    try {
      //读取报文,阻塞语句,有数据就装包在inPacket报文中,以装完或装满为止
      socket.receive(inPacket);
      //将接收到的字节数组转为对应的字符串
      msg=new String(inPacket.getData(),0,inPacket.getLength(),"utf-8");
    } catch (IOException e) {
      e.printStackTrace();
      msg=null;
    }
    return msg;
  }

  public void close(){
    if (socket!=null)
      socket.close();
  }
}

五、效果展示

六、总结

这一篇详细记录学习运用java进行网络编程,基于UDP套接字(Socket)实现服务器与客户端间的通信,在实战案例中更深刻理解UDP的实现原理,掌握UDP实践应用步骤。

起初完成UDP通信时,遇到了几个问题,相比较TCP的实现,确实体会到数据传输的过程的不同,UDP服务和客户端共用了一个DatagramSocket,另外需要DatagramPacket数据报的协作。另外,UDP没有数据流的概念,所以读写不同于TCP,需要以字节数据进行读取。

到此这篇关于java实现基于UDP协议网络Socket编程(C/S通信)的文章就介绍到这了,更多相关java UDP协议Socket编程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java Socket编程实例(二)- UDP基本使用

    一.服务端代码: import java.io.*; import java.net.*; public class UDPEchoServer { private static final int ECHOMAX = 255; // Maximum size of echo datagram public static void main(String[] args) throws IOException { int servPort = 5500; // Server port Datagr

  • Java Socket编程实例(五)- NIO UDP实践

    一.回传协议接口和UDP方式实现: 1.接口: import java.nio.channels.SelectionKey; import java.io.IOException; public interface EchoProtocol { void handleAccept(SelectionKey key) throws IOException; void handleRead(SelectionKey key) throws IOException; void handleWrite(

  • java实现基于UDP协议网络Socket编程(C/S通信)

    一.前言:认识UDP UDP,全称User Datagram Protocol(用户数据报协议),是Internet 协议集支持一个无连接的传输协议.UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法. UDP主要用于不要求分组顺序到达的传输中,分组传输顺序的检查与排序由应用层完成,提供面向报文的简单不可靠信息传送服务.UDP 协议基本上是IP协议与上层协议的接口,适用端口分别运行在同一台设备上的多个应用程序. 二.UDP的特点(与TCP相比) 正是UDP提供不可靠服务

  • java实现基于TCP协议网络socket编程(C/S通信)

    一.前言:TCP原理简介 首先,保证文章完整性,TCP的理论原理还是需要简介一下,略显枯燥๑乛◡乛๑. TCP(传输控制协议,Transmission Control Protocol)是一种面向连接的.可靠的.基于字节流的传输层通信协议.TCP旨在适应支持多网络应用的分层协议层次结构.也就是说,TCP是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议. 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务. 以上TCP的特点,也正是与UD

  • 解析C语言基于UDP协议进行Socket编程的要点

    两种协议 TCP 和 UDP 前者可以理解为有保证的连接,后者是追求快速的连接. 当然最后一点有些 太过绝对 ,但是现在不需熬考虑太多,因为初入套接字编程,一切从简. 稍微试想便能够大致理解, TCP 追求的是可靠的传输数据, UDP 追求的则是快速的传输数据. 前者有繁琐的连接过程,后者则是根本不建立可靠连接(不是绝对),只是将数据发送而不考虑是否到达. 以下例子以 *nix 平台的便准为例,因为 Windows平台需要考虑额外的加载问题,稍作添加就能在 Windows 平台上运行UDP. U

  • Java多线程实现TCP网络Socket编程(C/S通信)

    开篇必知必会 在前一篇<基于TCP协议网络socket编程(java实现C/S通信)>,实际存在一个问题,如果服务器端在建立连接后发送多条信息给客户端,客户端是无法全部接收的,原因在于客户端为单线程,只接受了第一条信息,剩余信息阻塞等待下一次发送.所以,这造成了客户端无法处理消息队列,每次只接收并输出一条服务器信息,出现信息不同步问题. 本篇将解决这个问题,详细记录实现java多线程通信,目标是使客户端可以一次接收服务器发送的多条信息,避免阻塞.方法是将客户端接收信息功能独立为一个线程来完成,

  • Java基于Tcp协议的socket编程实例

    本文实例讲述了Java基于Tcp协议的socket编程方法,分享给大家供大家参考.具体分析如下: 以下是一对一的通信编程实现,后续会继续学习一个服务器监听多个客户端的实现. 这里用到的主要步骤如下: 第一步:以特定端口(如4800)新建socket对象 第二步:以系统输入设备构造BufferedReader对象,该对象用于接收系统键盘输入的字符 第三步:以socket对象 得到输出流来构造PrintWriter 第四步:以socket对象得到输入流来构造相应的BufferedReader对象,该

  • java实现基于UDP协议的聊天小程序操作

    UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议.它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去! UDP适用于一次只传送少量数据.对可靠性要求不高的应用环境.正因为UDP协议没有连接的过程,所以它的通信效率高:但也正因为如此,它的可靠性不如TCP协议高.QQ就使用UDP发消息,因此有时会出现收不到消息的情况. 利用UDP协议的发送和接收,模拟聊天小程序 创建聊天程序的A端: 1.发送信息到接收端                 1 .准

  • Linux中使用C语言实现基于UDP协议的Socket通信示例

    linux下udp服务器端源码示例: #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h> #include <stdio.h> #include <un

  • Java编程实现基于TCP协议的Socket聊天室示例

    本文实例讲述了Java编程实现基于TCP协议的Socket聊天室.分享给大家供大家参考,具体如下: 这里使用Socket套接字进行编程,完成的是基于TCP可靠服务实现服务器与客户端的双通信. Server服务器端: package com.han; import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.Win

  • 详解Android 基于TCP和UDP协议的Socket通信

    本来想讲一下基础的网络通信方面的知识点,发现太枯燥乏味了,不过笔试中也经常会问到这方面的问题,所以关于通信方面的知识点,小编会放到面试中去,因为实战中也就面试会用到这方面知识点 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是"请求-响应方式",即在请求时建立连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据. 而Socket通信中基于TCP/IP协议的通信则是在双方建立起连接后就可以直接进行数

  • java编程实现基于UDP协议传输数据的方法

    本文实例讲述了java编程实现基于UDP协议传输数据的方法.分享给大家供大家参考,具体如下: UDP协议(User Datagram Protocol,用户数据报协议)不同于TCP协议,它是不可能靠的,但是它比TCP协议具有更快的传输速度,UDP发送的数据单元称为数据报,当网络传输UDP传输UDP数据报是无法保证数据能够到达目的地,也无法保证按发送的顺序到达目的地,也就是说先发送了"hello",再发送了"world",但接收方可能会先收到"world&q

随机推荐