首页 > 代码库 > java中的网络编程基本回顾

java中的网络编程基本回顾

网络编程基本概念,TCP/IP协议简介

网络基础知识
     
网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯。网络编程中有两个主要的问题,一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输。在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定Internet上的一台主机。而TCP层则提供面向应用的可靠的或非可靠的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是如何处理数据的。
     
目前较为流行的网络编程模型是客户机/服务器(C/S)结构。即通信双方一方作为服务器等待客户提出请求并予以响应。客户则在需要服务时向服务器提出申请。服务器一般作为守护进程始终运行,监听网络端口,一旦有客户请求,就会启动一个服务进程来响应该客户,同时自己继续监听服务端口,使后来的客户也能及时得到服务。

两类传输协议:TCPUDP
     
尽管TCP/IP协议的名称中只有TCP这个协议名,但是在TCP/IP的传输层同时存在TCPUDP两个协议。
TCP
Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。
      UDP
User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。 
  下面我们对这两种协议做简单比较:
  使用UDP时,每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。对于TCP协议,由于它是一个面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中多了一个连接建立的时间。
 
   使用UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。而TCP没有这方面的限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大量的数据。UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。而TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。
  总之,TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。相比之下UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。
  读者可能要问,既然有了保证可靠传输的TCP协议,为什么还要非可靠传输的UDP协议呢?主要的原因有两个。一是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。二是在许多应用中并不需要保证严格的传输可靠性,比如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。

基于URL的高层次Java网络编程

一致资源定位器URL
      URL(Uniform Resource Locator)
是一致资源定位器的简称,它表示Internet上某一资源的地址。通过URL我们可以访问Internet上的各种网络资源,比如最常见的WWWFTP站点。浏览器通过解析给定的URL可以在网络上查找相应的文件或其他资源。
URL
的组成

      protocol://resourceName
  协议名(protocol)指明获取资源所使用的传输协议,如httpftpgopherfile等,资源名(resourceName)则应该是资源的完整地址,包括主机名、端口号、文件名或文件内部的一个引用。例如:
http://www.sun.com/ 
协议名://主机名
http://home.netscape.com/home/welcome.html 
协议名://机器名+文件名
http://www.gamelan.com:80/Gamelan/network.html#BOTTOM 
协议名://机器名+端口号+文件名+内部引用

创建一个URL

为了表示URL java.net中实现了类URL。我们可以通过下面的构造方法来初始化一个URL对象:
  (1 public URL (String spec);
     通过一个表示URL地址的字符串可以构造一个URL对象。
     URL urlBase=new URL("http://www. 263.net/") 
  (2 public URL(URL context, String spec);
     通过基URL和相对URL构造一个URL对象。
     URL net263=new URL ("http://www.263.net/");
     URL index263=new URL(net263, "index.html")
  (3 public URL(String protocol, String host, String file);
     new URL("http", "www.gamelan.com", "/pages/Gamelan.net. html");
  (4 public URL(String protocol, String host, int port, String file);
     URL gamelan=new URL("http", "www.gamelan.com", 80, "Pages/Gamelan.network.html");

注意:类URL的构造方法都声明抛弃非运行时例外(MalformedURLException),因此生成URL对象时,我们必须要对这一例外进行处理,通常是用try-catch语句进行捕获。格式如下:

try{
      URL myURL= new URL(…)
}catch (MalformedURLException e){ …  
}

解析一个URL

一个URL对象生成后,其属性是不能被改变的,但是我们可以通过类URL所提供的方法来获取这些属性:

public String getProtocol() 获取该URL的协议名。
public String getHost() 获取该URL的主机名。
public int getPort() 获取该URL的端口号,如果没有设置端口,返回-1public String getFile() 获取该URL的文件名。
public String getRef() 获取该URL在文件中的相对位置。
public String getQuery() 获取该URL的查询信息。
public String getPath() 获取该URL的路径
public String getAuthority() 获取该URL的权限信息
public String getUserInfo() 获得使用者的信息
public String getRef() 获得该URL的锚

URL读取WWW网络资源

当我们得到一个URL对象后,就可以通过它读取指定的WWW资源。这时我们将使用URL的方法openStream(),其定义为:InputStream openStream();方法openSteam()与指定的URL建立连接并返回InputStream类的对象以从这一连接中读取数据。

package com.ljq.test;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;

public class URLReader {

    public static void main(String[] args) throws Exception {
        URL url = new URL("http://www.cnblogs.com/linjiqin/");
        BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
        String inputLine;
        while ((inputLine = in.readLine()) != null) {
            System.out.println(inputLine);
        }
        in.close();
    }
}

通过URLConnetction连接WWW

      通过URL的方法openStream(),我们只能从网络上读取数据,如果我们同时还想输出数据,例如向服务器端的CGI程序发送一些数据,我们必须先与URL建立连接,然后才能对其进行读写,这时就要用到类URLConnection了。CGI是公共网关接口(Common Gateway Interface)的简称,它是用户浏览器和服务器端的应用程序进行连接的接口,有关CGI程序设计,请读者参考有关书籍。

      URLConnection也在包java.net中定义,它表示Java程序和URL在网络上的通信连接。当与一个URL建立连接时,首先要在一个URL对象上通过方法openConnection()生成对应的URLConnection对象。例如下面的程序段首先生成一个指向地址http://www.cnblogs.com/linjiqin/的对象,然后用openConnection()打开该URL对象上的一个连接,返回一个URLConnection对象。如果连接过程失败,将产生IOException

Try{
   URL netchinaren = new URL ("http://edu.chinaren.com/index.shtml");
   URLConnectonn tc = netchinaren.openConnection();
}catch(MalformedURLException e){ //创建URL()对象失败
  …
}catch (IOException e){ //openConnection()失败
  …
}

URLConnection提供了很多方法来设置或获取连接参数,程序设计时最常使用的是getInputStream()getOurputStream(),其定义为:
InputSteram getInputSteram();
OutputSteram getOutputStream();
通过返回的输入/输出流我们可以与远程对象进行通信。看下面的例子:

package com.ljq.test;

import java.io.DataInputStream;
import java.io.PrintStream;
import java.net.URL;
import java.net.URLConnection;

public class URLReader {

    public static void main(String[] args) throws Exception {
        //创建URL对象
        URL url=new URL("http://www.javasoft.com/cgi-bin/backwards");
        //由URL对象获取URLConnection对象
        URLConnection conn=url.openConnection();
        //由URLConnection获取输入流,并构造DataInputStream对象
        DataInputStream dis=new DataInputStream(conn.getInputStream()); 
        //由URLConnection获取输出流,并构造PrintStream对象
        PrintStream ps=new PrintStream(conn.getOutputStream());
        String line=dis.readLine(); 
        ps.println("client…"); //向服务器写出字符串 "client…"
    }
}

其中backwards为服务器端的CGI程序。实际上,类URL的方法openSteam()是通过URLConnection来实现的。它等价于openConnection().getInputStream();
基于URL的网络编程在底层其实还是基于下面要讲的Socket接口的。WWWFTP等标准化的网络服务都是基于TCP协议的,所以本质上讲URL编程也是基于TCP的一种应用

======================================================================================================================================================================

以上部分转载自:Java网络编程

基于Socket的低层次Java网络编程

java也为低层次的网络通讯提供了良好的支持,同URL相关类一样,socket编程相关api也放在类库java.net中,我们主要可以关注InetAddress,Socket,ServerSocket,DatagramSocket这几个类。

基础网络类 InetAddress

InetAddress有两个直接的已知子类,分别是Inet4Address和Inet6Address。它的实例包含IP地址,还可能包含响应的主机名。

package me.hays.JavaStudy.net;
import java.io.IOException;
import java.net.InetAddress;
public class InetAddressTest {

      public static void main(String[] args) throws IOException {
           /***
            * InetAddress提供了5个静态方法来获取InetAddress相关实例
            * getByAddress(byte[] addr)
            * getByAddress(String host, byte[] addr)
            * getByName(String host)
            * getLocalHost()
            */
            //使用域名创建对象
        InetAddress inet1 = InetAddress.getByName("127.0.0.1");
        System.out.println(inet1.getHostName());
        InetAddress inet2 = InetAddress.getByName("www.baidu.com");
        System.out.println(inet2.getHostAddress());
        InetAddress inet3 = InetAddress.getLocalHost();
        System.out.println(inet3.getHostAddress());
        //InetAddress实例提供了一些检测方法,一般比较少用到
        System.out.println("是否可达:" + inet3.isReachable(1000));
        System.out.println("是否是通配符地址的实用例行程序:" + inet3.isAnyLocalAddress());
      }

}

基于SocketTCP编程

基于TCP协议的网络编程需要建立可靠的连接进行数据的传输。Jdk提供了两个类,实现客户端的套接字Socket和实现服务端的套接字ServerSocket,而套接字的实际工作由SocketImpl类的实例执行。

服务端的ServerSocket在启动后监听一个指定的端口号并阻塞地等待客户端的连接,当客户端的连接到达时,便和客户端建立一个与之对应的Socket连接返回并释放客户端连接对于服务端端口的占用。此后,两个Socket直接就能进行数据的交换,而ServerSocket便可以等待其他客户端的连接。

客户端的Socket通讯的发起方,需要指定连接某一台主机(ip或者域名)上面的某个端口,因此它的构造方法一般需要传入host和port。当无法连接时抛出异常。建立连接后,便可以获取用于数据交换的流对象。同ServerSocket一样,当数据交换完成时,需要进行关闭操作。

最简单的应用Socket通讯的服务端和客户端代码

package me.hays.JavaStudy.net.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/***
 * 最简单的Socket使用示例
 * 客户端代码
 * 步骤:
 * 1、建立连接
 * 2、从已经连接成功的Socket实例中获得流进行数据交换
 * 3、关闭连接
 * @author hays
 */
public class SocketClientTest1 {

    public static void main(String[] args) {
        Socket socket = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            //指定主机和监听端口8000
            socket = new Socket("localhost", 8000);
            //从socket中获取交换数据使用的流
            byte[] req = "向服务端发送数据:hello world".getBytes();
            out = socket.getOutputStream();
            //向服务端发送数据
            out.write(req);
            //获取服务端反馈的数据
            in = socket.getInputStream();
            byte[] buffer = new byte[1024];
            int readed = in.read(buffer);
            String dataFromClient = new String(buffer, 0, readed);
            System.out.println("接收到服务端响应数据:" + dataFromClient);
        } catch(IOException e){
            e.printStackTrace();
        } finally {
            try {
                if(socket != null){
                    socket.close();
                } 
                if(in != null){
                    in.close();
                } 
                if(out != null){
                    out.close();
                } 
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
package me.hays.JavaStudy.net.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/***
 * 最简单的Socket使用示例
 * 服务端代码
 * 1、监听端口
 * 2、使用accept等待客户端连接,未收到连接时阻塞
 * 3、获取套接字socket,可以和客户端进行数据交换
 * 4、关闭连接
 * @author hays
 */
public class SocketServerTest1 {

    public static void main(String[] args){
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            //指定监听端口8000
            serverSocket = new ServerSocket(8000);
            //未获取连接时一直阻塞,知道accept
            socket = serverSocket.accept();
            //从socket中获取交换数据使用的流
            in = socket.getInputStream();
            byte[] buffer = new byte[1024];
            int readed = in.read(buffer);
            String dataFromClient = new String(buffer, 0, readed);
            System.out.println("接收到客户端数据:" + dataFromClient);
            byte[] resp = "服务端响应数据:hello world".getBytes();
            out = socket.getOutputStream();
            out.write(resp);
        } catch(IOException e){
            e.printStackTrace();
        } finally {
            try {
                if(serverSocket != null){
                    serverSocket.close();
                } 
                if(socket != null){
                    socket.close();
                } 
                if(in != null){
                    in.close();
                } 
                if(out != null){
                    out.close();
                } 
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

上面的例程是最简单的客户端服务端通过socket交换数据的应答程序,服务端监听端口,客户端进行连接,获得连接后,进行数据的交换,然后关闭连接,完成整个过程。但是这里有个问题,如果客户端想通过一次socket连接发送多个数据要怎么处理呢?这就是复用连接的问题,这里通过简单的for循环来处理。

复用Socket连接示例代码

package me.hays.JavaStudy.net.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/***
 * 使用同一个socket发送多组数据
 * 客户端代码使用for循环
 * 服务端也要相应的多次读取
 * @author hays
 */
public class SocketClientTest2 {

    public static void main(String[] args) {
        Socket socket = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            //指定监听端口8000
            socket = new Socket("localhost", 8000);
            //从socket中获取交换数据使用的流
            out = socket.getOutputStream();
            in = socket.getInputStream();
            //这里在SocketServerTest2定了一个结束标记,告知通讯已经结束。
            //实际使用中并不是这么简单,开始结束等有专门的网络协议定义,这里只是做一个简单的示例
            String[] data = http://www.mamicode.com/new String[]{"test1", "test2", "test3", SocketServerTest2.ENDFLAG};
            for(int i = 0; i < data.length; i++){
                out.write(data[i].getBytes());
                byte[] buffer = new byte[1024];
                int readed = in.read(buffer);
                String dataFromClient = new String(buffer, 0, readed);
                System.out.println("接收到服务端响应数据:" + dataFromClient);
            }
        } catch(IOException e){
            e.printStackTrace();
        } finally {
            try {
                if(socket != null){
                    socket.close();
                } 
                if(in != null){
                    in.close();
                } 
                if(out != null){
                    out.close();
                } 
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
package me.hays.JavaStudy.net.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/***
 * 使用同一个socket发送多组数据
 * 客户端代码使用for循环
 * 服务端也要相应的多次读取
 * @author hays
 */
public class SocketServerTest2 {

    public static final String ENDFLAG = "==========end==========";//结束标记
    public static void main(String[] args){
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            //指定监听端口8000
            serverSocket = new ServerSocket(8000);
            //未获取连接时一直阻塞,知道accept
            socket = serverSocket.accept();
            //从socket中获取交换数据使用的流
            in = socket.getInputStream();
            out = socket.getOutputStream();
            String dataFromClient = null;
            //结束标记,告知通讯已经结束
            //实际使用中并不是这么简单,开始结束等有专门的网络协议定义,这里只是做一个简单的示例
            while(!ENDFLAG.equals(dataFromClient)){
                byte[] buffer = new byte[1024];
                int readed = in.read(buffer);
                dataFromClient = new String(buffer, 0, readed);
                System.out.println("接收到客户端数据:" + dataFromClient);
                byte[] resp = "服务端响应数据:hello world".getBytes();
                out.write(resp);
            }
        } catch(IOException e){
            e.printStackTrace();
        } finally {
            try {
                if(serverSocket != null){
                    serverSocket.close();
                } 
                if(socket != null){
                    socket.close();
                } 
                if(in != null){
                    in.close();
                } 
                if(out != null){
                    out.close();
                } 
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在实际使用过程中,一个服务端程序不应该只有一个客户端程序进行连接,并且只进行一次的通讯。那么怎么保持服务端程序能够处理多个客户端连接呢,这里就要用到线程的知识了。服务端的ServerSocket需要一直处于就绪状态等待客户端的连接,接收到客户端连接,产生一个Socket对象放进一个线程中和客户端进行通讯,ServerSocket继续等待下一个连接的到来。这里做一个简单的示例。

使用线程池实现支持多个客户端接入

package me.hays.JavaStudy.net.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/***
 * 模拟多个客户端发送数据
 * @author hays
 */
public class SocketClientTest3 {

    public static void main(String[] args) {
        Socket socket = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            //模拟使用多个客户端进行连接
            for(int i = 0; i < 20; i++){
                //指定监听端口8000
                socket = new Socket("localhost", 8000);
                //从socket中获取交换数据使用的流
                byte[] req = ("第"+ i +"次向服务端发送数据:hello world").getBytes();
                out = socket.getOutputStream();
                out.write(req);
                in = socket.getInputStream();
                byte[] buffer = new byte[1024];
                int readed = in.read(buffer);
                String dataFromClient = new String(buffer, 0, readed);
                System.out.println("接收到服务端响应数据:" + dataFromClient);
            }
        } catch(IOException e){
            e.printStackTrace();
        } finally {
            try {
                if(socket != null){
                    socket.close();
                } 
                if(in != null){
                    in.close();
                } 
                if(out != null){
                    out.close();
                } 
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
package me.hays.JavaStudy.net.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

/***
 * 使用socket结合线程池的简单示例
 * 保持socketServer监听,有客户端连接进来就得到一个socket并丢进线程池进行处理
 * 服务端代码
 * @author hays
 */
public class SocketServerTest3 {

    public static void main(String[] args){
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            //指定监听端口8000
            serverSocket = new ServerSocket(8000);
            System.out.println("服务启动,等待连接...");
            ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
            while(true){//轮询
                //未获取连接时一直阻塞,知道accept
                socket = serverSocket.accept();
                System.out.println("获取socket,开始处理,正在运行线程数:" + threadPoolExecutor.getActiveCount());
                //获取连接就创建丢到线程池里处理
                DealSocketThread dealSocketThread = new SocketServerTest3().new DealSocketThread(socket);
                threadPoolExecutor.execute(dealSocketThread);
            }
        } catch(IOException e){
            e.printStackTrace();
        } finally {
            try {
                if(serverSocket != null){
                    serverSocket.close();
                } 
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
    }
    
    /***
     * 专门处理socket交换数据的线程类
     */
    class DealSocketThread extends Thread{
        private Socket socket;
        public DealSocketThread(Socket socket) {
            this.socket = socket;
        }
        @Override
        public void run() {
            InputStream in = null;
            OutputStream out = null;
            try {
                //从socket中获取交换数据使用的流
                in = socket.getInputStream();
                byte[] buffer = new byte[1024];
                int readed = in.read(buffer);
                String dataFromClient = new String(buffer, 0, readed);
                System.out.println("接收到客户端数据:" + dataFromClient);
                byte[] resp = "服务端响应数据:hello world".getBytes();
                out = socket.getOutputStream();
                out.write(resp);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if(in != null){
                            in.close();
                    } 
                    if(out != null){
                        out.close();
                    } 
                    if(socket != null){
                        socket.close();
                    } 
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
}

基于SocketUDP编程

上文提到了,使用TCP的方式进行数据交换有它的优势,但同时也存在不足,比如需要建立专门的连接,对于服务器压力相对较大等。对于可靠性要求不是那么高的场景,便可以考虑使用UDP的方式。在JDK中,实现UDP编程用到了两个主要的类。分别是DatagramSocket和Datagrampacket类。

DatagramSocket表示是用来发送和接受数据报包的套接字,数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。在 DatagramSocket 上总是启用 UDP 广播发送。为了接收广播包,应该将 DatagramSocket 绑定到通配符地址。在某些实现中,将 DatagramSocket 绑定到一个更加具体的地址时广播包也可以被接收。

DatagramPacket表示数据报包。数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。

简单的使用DatagramSocket通讯的代码

package me.hays.JavaStudy.net.udp;

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

/***
 * 使用DatagramSocket发送和接收数据示例
 * 客户端代码,发送数据
 * 1、将接收方数据封装到datagramPacket
 * 2、使用datagramSocket发送数据包,等待服务端反馈
 * 3、关闭连接
 * @author hays
 */
public class DatagramSocketClientTest {

    public static void main(String[] args) {
        DatagramSocket datagramSocket = null;
        try {
            //提供4个构造方法//无参使用系统分配、指定port、指定port和InetAddress、传入SocketAddress
            datagramSocket = new DatagramSocket();
            //要发送的数据//数据必须发送byte[]//IO流不一定需要了
            byte[] data = "http://www.mamicode.com/hellp DatagramSocket!".getBytes();
            //发送到的主机地址
            String host = "localhost";
            //发送到的端口
            int port = 8000;
            //封装成InetAddress
            InetAddress inetAddress = InetAddress.getByName(host);
            //使用UDP的方式发送数据是将数据和发送方和接收方相关信息使用DatagramPacket包装在一起
            DatagramPacket datagramPacket = new DatagramPacket(data, 0, data.length, inetAddress, port);
            //使用DatagramSocket实例对象发送//不保证数据一定被成功传输
            datagramSocket.send(datagramPacket);
            //接收响应信息
            byte[] recAllData = http://www.mamicode.com/new byte[1024];
            DatagramPacket backDatagramPacket = new DatagramPacket(recAllData, 0, recAllData.length);
            //receive和Socket的accept方法一样也是阻塞方法,直到接收到数据
            datagramSocket.receive(backDatagramPacket);
            System.out.println("接收到服务端反馈:" + new String(backDatagramPacket.getData(), 0, backDatagramPacket.getLength()));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(datagramSocket != null){
                try {
                    datagramSocket.close();
                } catch (Exception e2) {
                }
            }
        }
    }

}
package me.hays.JavaStudy.net.udp;

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

/***
 * 使用DatagramSocket发送和接收数据示例
 * 服务端代码,接收数据,反馈数据
 * 步骤和TCP方式基本一致:
 * 1、监听端口
 * 2、receive方法等待客户端的数据
 * 3、从datagramPacket获取数据可以得到客户端相关数据,再封装成datagramPacket响应给客户端
 * 4、关闭连接
 * @author hays
 */
public class DatagramSocketServerTest {

    public static void main(String[] args) {
        DatagramSocket datagramSocket = null;
        try {
            //和TCP方式一样,服务端的DatagramSocket也需要监听端口
            datagramSocket = new DatagramSocket(8000);
            //使用DatagramPacket去接收数据
            //recAllData接收到整个数据包的数据,必须大于客户端发送的
            byte[] recAllData = http://www.mamicode.com/new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(recAllData, 0, recAllData.length);
            //receive和Socket的accept方法一样也是阻塞方法,直到接收到数据
            datagramSocket.receive(datagramPacket);
            //实际客户端发送的数据长度
            byte[] data =http://www.mamicode.com/ datagramPacket.getData();
            int len = datagramPacket.getLength();
            String recStr = new String(data, 0, len);
            System.out.println("服务端接收到数据:" + recStr);
            int clientPort = datagramPacket.getPort();
            System.out.println("客户端端口:" + clientPort);
            InetAddress clientAddress = datagramPacket.getAddress();
            System.out.println("客户端IP:" + clientAddress);
            //有了客户端的信息就可以构造反馈给客户端的数据包
            byte[] backData = "http://www.mamicode.com/服务端响应".getBytes();
            //使用UDP的方式发送数据是将数据和发送方和接收方相关信息使用DatagramPacket包装在一起
            DatagramPacket backDatagramPacket = new DatagramPacket(backData, 0, backData.length, clientAddress, clientPort);
            //使用DatagramSocket实例对象发送//不保证数据一定被成功传输
            datagramSocket.send(backDatagramPacket);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(datagramSocket != null){
                datagramSocket.close();
            }
        }
    }

}

 

java中的网络编程基本回顾