网络编程技术

                                                     网络编程技术

Java 网络编程

                                                                    网络基础知识

  网络编程的目的:直接或间接地通过网络协议与其他计算机进行通讯。

  网络编程中有两个主要的问题:

  1.如何准确地定位网络上一台或多台主机。

  2.找到主机后如何可靠高效地进行数据传输。

  目前较为流行的网络编程模型是客户端/服务器(C/S)结构

  即通信双方一方作为服务器等待客户提出请求并予以相应。客户则在需要服务时向服务器提出申请。

  服务器始终运行,监听网络端口,一旦有客户请求,就会启动一个服务线程来响应该客户,同时自己继续监听服务窗口,使后来的客户也能及时得到服务。

 

IP地址

  IP网络中每台主机都必须有一个唯一的IP地址,IP地址是一个逻辑地址。

  英特网上的IP地址具有全球唯一性。

  32位,四个字节,常用点分十进制的格式表示。

  例如:192.168.0.200

 

协议

  为进行网络中的数据交换(通信)而建立的规则、标准或约定。(=语义+语法+规则)。

  不同层具有各自不同的协议。

 

ISO/OSI七层参考模型

  网络体系结构解决异质性问题采用的是分层的方法——把复杂的网络互联问题划分为若干个较小的、单一的问题,在不同层上予以解决。

  OSI(Open System Interconnection)参考模型将网络的不同功能划分为7层:

  应用层:处理网络应用

  表示层:数据表示

  会话层:主机间通信

  传输层:端到端的连接

  网络层:寻址和最短路径

  数据链路层:介质访问(接入)

  物理层:二进制传输

  

  

  通信实体的对等层之间不允许直接通信,各层之间是严格的单向依赖,上层(Service user)使用下层提供的服务,下层(Service provider)向上层提供服务。

  对等层通信的实质对等层实体之间虚拟通信,下层向上层提供服务,实际通信在最底层完成。

 

OSI各层所使用的协议:

  应用层:Telnet、FTP、HTTP、DNS、SMTP、POP3

  传输层:TCP、UDP

  TCP:面向连接的可靠的传输协议。

  UDP:是无连接的,不可靠的传输协议。

  网络层:IP、ICMP、IGMP

 

端口

  在互联网上传输的数据都包含有用来识别目的地的IP地址和端口号。

  IP地址用来标识网络上的计算机,而端口号用来指明该计算机上的应用程序。

  端口是一种抽象的软件结构(包括一些数据结构和I/O缓冲区)。

  应用程序通过系统调用与某端口建立连接(binding)后,传输层传给该端口的数据都被相应的进程所接收,相应进程发给传输层的数据都通过该端口输出。

  端口用一个整数型标识符来表示,即端口号。

  端口号跟协议相关,TCP/IP传输层的两个协议TCP和UDP是完全独立的两个软件模块,因此各自的端口号也相互独立,端口通常称为协议端口(protocol port,简称端口。

  端口使用一个16位的数字来表示,它的范围是0~65535,1024以下的端口号保留给预定义的服务。例如,http使用80端口。

 

 

Java网络编程基础知识

 

一、网络基本概念

 

1、计算机网络,就是把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息,共享硬件、软件、数据信息等资源。

 

2、网络体系结构:国际标准化组织ISO于l978年提出“开放系统互连参考模型”,即著名的OSI(Open System Interconnection)模型。该模型把计算机网络分成物理层、数据链路层、网络层、传输层、会话层、表示层、应用层等七层。

 

 3、通信协议:计算机网络中实现通信必须有一些约定,即通信协议。对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。

 

TCP协议:提供可靠的数据传输服务;(传输层)

 

IP协议进行IP数据包的分割和组装。(应用层)

 

二、IP地址和端口号

 

1、IP地址:为实现网络中不同的计算机之间的通信,在网络中的每台机器都必须有一个与众不同事标识,这就是IP地址(IP Address)。

 

格式:数字型、32位、由4段8位的二进制数组成。一般表示为十进制形式(4个0~255的十进制整数),中间用圆点隔开。如:166.111.78.98

 

域名地址:也是分段表示的,便于记忆的、字符串形式。

 

2、端口:一个16位的整数,用于表示数据交给哪个通信程序处理。因此,端口就是应用程序与外界交流的出入口,它是一种抽象的软件结构,包括一些数据结构和I/O(基本输入/输出)缓冲区。

 

不同的应用程序处理不同端口上的数据,同一台机器上不能有两个程序使用同一个端口,端口号可以从0到65535,通常将它分为三类:

 

*、公认端口(Well Known Ports):从0到1023,它们紧密绑定(Binding)一些服务。

 

*、注册端口(Registered Ports):从1024到49151。它们松散地绑定一些服务。

 

*、动态和/或私有端口(Dynamic and/or Private Ports):从49152到65535,这些端口是应用程序使用的动态端口,应用程序一般不会主动使用这些端口。

 

三、两类传输协议:TCP;UDP

 

尽管TCP/IP协议的名称中只有TCP这个协议名,但是在TCP/IP的传输层同时存在TCP和UDP两个协议。

 

   1、TCP是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个 socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。TCP是Tranfer Control Protocol的 简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送 或接收操作。

 

2、UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。

 

比较:

 

UDP:

 

1,     每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。

 

2,     UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。

 

3,     UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方

 

TCP:

 

1,  面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中需要连接时间。

 

2,  TCP传输数据大小限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大的数据。

 

3,  TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。

 

应用:

 

1,TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。

 

2,UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。

 

补充:UDP协议的几个特性

 

 

 

(1) UDP是一个无连接协议,传输数据之前源端和终端不建立连接,当

 

  

 

UDP

 

它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。

 

  (2) 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。

 

  (3) UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。

 

  (4) 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。

 

(5)UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态表(这里面有许多参数)。

 

(6)UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。

 

虽然UDP是一个不可靠的协议,但它是分发信息的一个理想协议。例如,在屏幕上报告股票市场、在屏幕上显示航空信息等等。UDP也用在路由信息协议RIP(Routing Information Protocol)中修改路由表。在这些应用场合下,如果有一个消息丢失,在几秒之后另一个新的消息就会替换它。UDP广泛用在多媒体应用中,例如,Progressive Networks公司开发的RealAudio软件,它是在因特网上把预先录制的或者现场音乐实时传送给客户机的一种软件,该软件使用的RealAudio audio-on-demand protocol协议就是运行在UDP之上的协议,大多数因特网电话软件产品也都运行在UDP之上

 

四、在Java中操纵UDP
使用位于JDK中Java.NET包下的DatagramSocket和DatagramPacket类,可以非常方便地控制用户数据报文。

 

DatagramSocket类:创建接收和发送UDP的Socket实例

 

DatagramSocket():创建实例。通常用于客户端编程,它并没有特定监听的端口,仅仅使用一个临时的。

 

DatagramSocket(int port):创建实例,并固定监听Port端口的报文。

 

DatagramSocket(int port, InetAddress localAddr):这是个非常有用的构建器,当一台机器拥有多于一个IP地址的时候,由它创建的实例仅仅接收来自LocalAddr的报文。

 

receive(DatagramPacket d):接收数据报文到d中。receive方法产生一个“阻塞”。

 

send(DatagramPacket d):发送报文d到目的地。

 

setSoTimeout(int timeout):设置超时时间,单位为毫秒。

 

close():关闭DatagramSocket。在应用程序退出的时候,通常会主动释放资源,关闭Socket,但是由于异常地退出可能造成资源无法回收。所以,应该在程序完成时,主动使用此方法关闭Socket,或在捕获到异常抛出后关闭Socket。

 

DatagramPacket:用于处理报文,将byte数组、目标地址、目标端口等数据包装成报文或者将报文拆卸成byte数组。

 

DatagramPacket(byte[] buf, int length, InetAddress addr, int port):从buf数组中,取出length长的数据创建数据包对象,目标是addr地址,port端口。

 

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):从buf数组中,取出offset开始的、length长的数据创建数据包对象,目标是addr地址,port端口。

 

DatagramPacket(byte[] buf, int offset, int length):将数据包中从offset开始、length长的数据装进buf数组。

 

DatagramPacket(byte[] buf, int length):将数据包中length长的数据装进buf数组。

 

getData():它从实例中取得报文的byte数组编码。

 

五、Socket的java网络编程

 

(1)基于Socket的java网络编程

 

1,什么是Socket

 

网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。

 

但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。

 

SOCKET(套接字/管套/插口):标识连接的端点,IP地址 + 端口号。

 

应用程序与网络之间的接口

 

       应用程序创建socket

 

       socket 类型 决定了通信的类型

 

       可靠的 vs. 尽最大努力的

 

        面向连接的 vs. 无连接的

 

一旦socket配置完成,应用程序就可以

 

          把数据传给socket,从而进行网络传输

 

         从socket接收数据(其他主机通过网络发送过来的)

 

 

 

2,Socket通讯的过程

 

Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。

 

对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:

 

  (1) 创建Socket;

 

  (2) 打开连接到Socket的输入/出流;

 

  (3) 按照一定的协议对Socket进行读/写操作;

 

  (4) 关闭Socket.(在实际应用中,并未使用到显示的close,虽然很多文章都推荐如此,不过在我的程序中,可能因为程序本身比较简单,要求不高,所以并未造成什么影响。)

 

3,创建Socket

 

创建Socket

 

ServerSocket:编写TCP网络服务程序,首先要用到java.Net.ServerSocket类用以创建服务器Socket

 

构造方法:

 

ServerSocket(int port):创建绑定到特定端口的服务器套接字

 

ServerSocket(int port, int backlog):利用指定的backlog(服务器忙时保持连接请求的等待客户数量),创建服务器套接字并将其绑定到指定的本地端口号。

 

ServerSocket(int port, int backlog, InetAddress bindAddr):使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。

 

Socket:客户端要与服务器建立连接,必须先创建一个Socket对象

 

常用构造方法

 

Socket(String host, int port):创建一个流套接字并将其连接到指定主机上的指定端口号。

 

Socket(InetAddress address, int port):创建一个流套接字并将其连接到指定 IP 地址的指定端口号。

 

 

 

java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用很方便。其构造方法如下:

 

  Socket(InetAddress address, int port);

 

  Socket(InetAddress address, int port, boolean stream);

 

  Socket(String host, int prot);

 

  Socket(String host, int prot, boolean stream);

 

  Socket(SocketImpl impl)

 

  Socket(String host, int port, InetAddress localAddr, int localPort)

 

  Socket(InetAddress address, int port, InetAddress localAddr, int localPort)

 

  ServerSocket(int port);

 

  ServerSocket(int port, int backlog);

 

  ServerSocket(int port, int backlog, InetAddress bindAddr)

 

  其中address、host和port分别是双向连接中另一方的IP地址、主机名和端 口号,stream指明socket是流socket还是数据报socket,localPort表示本地主机的端口号,localAddr和 bindAddr是本地机器的地址(ServerSocket的主机地址),impl是socket的父类,既可以用来创建serverSocket又可 以用来创建Socket。count则表示服务端所能支持的最大连接数。例如:

 

  Socket client = new Socket("127.0.01.", 80);

 

  ServerSocket server = new ServerSocket(80);

 

  注意,在选择端口时,必须小心。每一个端口提供一种特定的服务,只有给出正确的端口,才 能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23, 所以我们在选择端口号时,最好选择一个大于1023的数以防止发生冲突。

 

  在创建socket时如果发生错误,将产生IOException,在程序中必须对之作出处理。所以在创建Socket或ServerSocket是必须捕获或抛出例外。

 

4,简单的Client/Server程序

 

1. 客户端程序

 

  import java.io.*;

 

  import java.net.*;

 

  public class TalkClient {

 

    public static void main(String args[]) {

 

      try{

 

        Socket socket=new Socket("127.0.0.1",4700);

 

        //向本机的4700端口发出客户请求

 

        BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));

 

        //由系统标准输入设备构造BufferedReader对象

 

        PrintWriter os=new PrintWriter(socket.getOutputStream());

 

        //由Socket对象得到输出流,并构造PrintWriter对象

 

        BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));

 

        //由Socket对象得到输入流,并构造相应的BufferedReader对象

 

        String readline;

 

        readline=sin.readLine(); //从系统标准输入读入一字符串

 

        while(!readline.equals("bye")){

 

        //若从标准输入读入的字符串为 "bye"则停止循环

 

          os.println(readline);

 

          //将从系统标准输入读入的字符串输出到Server

 

          os.flush();

 

          //刷新输出流,使Server马上收到该字符串

 

          System.out.println("Client:"+readline);

 

          //在系统标准输出上打印读入的字符串

 

          System.out.println("Server:"+is.readLine());

 

          //从Server读入一字符串,并打印到标准输出上

 

          readline=sin.readLine(); //从系统标准输入读入一字符串

 

        } //继续循环

 

        os.close(); //关闭Socket输出流

 

        is.close(); //关闭Socket输入流

 

        socket.close(); //关闭Socket

 

      }catch(Exception e) {

 

        System.out.println("Error"+e); //出错,则打印出错信息

 

      }

 

  }

 

}

 

 2. 服务器端程序

 

  import java.io.*;

 

  import java.net.*;

 

  import java.applet.Applet;

 

  public class TalkServer{

 

    public static void main(String args[]) {

 

      try{

 

        ServerSocket server=null;

 

        try{

 

          server=new ServerSocket(4700);

 

        //创建一个ServerSocket在端口4700监听客户请求

 

        }catch(Exception e) {

 

          System.out.println("can not listen to:"+e);

 

        //出错,打印出错信息

 

        }

 

        Socket socket=null;

 

        try{

 

          socket=server.accept();

 

          //使用accept()阻塞等待客户请求,有客户

 

          //请求到来则产生一个Socket对象,并继续执行

 

        }catch(Exception e) {

 

          System.out.println("Error."+e);

 

          //出错,打印出错信息

 

        }

 

        String line;

 

        BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));

 

         //由Socket对象得到输入流,并构造相应的BufferedReader对象

 

        PrintWriter os=newPrintWriter(socket.getOutputStream());

 

         //由Socket对象得到输出流,并构造PrintWriter对象

 

        BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));

 

         //由系统标准输入设备构造BufferedReader对象

 

        System.out.println("Client:"+is.readLine());

 

        //在标准输出上打印从客户端读入的字符串

 

        line=sin.readLine();

 

        //从标准输入读入一字符串

 

        while(!line.equals("bye")){

 

        //如果该字符串为 "bye",则停止循环

 

          os.println(line);

 

          //向客户端输出该字符串

 

          os.flush();

 

          //刷新输出流,使Client马上收到该字符串

 

          System.out.println("Server:"+line);

 

          //在系统标准输出上打印读入的字符串

 

          System.out.println("Client:"+is.readLine());

 

          //从Client读入一字符串,并打印到标准输出上

 

          line=sin.readLine();

 

          //从系统标准输入读入一字符串

 

        }  //继续循环

 

        os.close(); //关闭Socket输出流

 

        is.close(); //关闭Socket输入流

 

        socket.close(); //关闭Socket

 

        server.close(); //关闭ServerSocket

 

      }catch(Exception e){

 

        System.out.println("Error:"+e);

 

        //出错,打印出错信息

 

      }

 

    }

 

  }

 

3、支持多客户的client/server程序

 

前面的Client/Server程序只能实现Server和一个客户的对话。在实际应用 中,往往是在服务器上运行一个永久的程序,它可以接收来自其他多个客户端的请求,提供相应的服务。为了实现在服务器方给多个客户提供服务的功能,需要对上面的程序进行改造,利用多线程实现多客户机制。服务器总是在指定的端口上监听是否有客户请求,一旦监听到客户请求,服务器就会启动一个专门的服务线程来响 应该客户的请求,而服务器本身在启动完线程之后马上又进入监听状态,等待下一个客户的到来。

 

ServerSocket serverSocket=null;

 

    boolean listening=true;

 

    try{

 

      serverSocket=new ServerSocket(4700);

 

      //创建一个ServerSocket在端口4700监听客户请求

 

    }catch(IOException e) {  }

 

    while(listening){ //永远循环监听

 

      new ServerThread(serverSocket.accept(),clientnum).start();

 

      //监听到客户请求,根据得到的Socket对象和

 

       客户计数创建服务线程,并启动之

 

      clientnum++; //增加客户计数

 

    }

 

    serverSocket.close(); //关闭ServerSocket

 

设计ServerThread类

 

 public class ServerThread extends Thread{

 

   Socket socket=null; //保存与本线程相关的Socket对象

 

   int clientnum; //保存本进程的客户计数

 

   public ServerThread(Socket socket,int num) { //构造函数

 

    this.socket=socket; //初始化socket变量

 

    clientnum=num+1; //初始化clientnum变量

 

   }

 

   public void run() { //线程主体

 

    try{//在这里实现数据的接受和发送}

 

 4、   DatagramSocket 类
       要收发DatagramPacket必须打开一个数据报socket ,当服务器构造DatagramSocket时。

 

 

 

1.1 服务器和客户端的服务器 

 

 

 

      两者使用的socket都是一样的,区别仅仅在于服务器端的端口是已知端口,而客户端的端口是系统分配的。

 

     TCP端口和UDP端口之间没有关联,所以两者可以共同绑定在同一个端口上,而不会有相互影响。

 

 

 

5、 DatagramSocket 类的构造函数   

 

 

 

      DatagramSocket创建一个在指定端口监听的入站数据报的 socket  ,使用此构造函数可以编写出在指导的端口监听的服务器。

 

      如果服务器在匿名端口监听,客户端就无法与之联系。

 

     DatagramSocket 中的receive 方法,是阻塞方法,只有当接收到数据的时候,才会进行下面的代码,否则只会阻塞当前的进程。

 

利用UDP协议来收发数据,都是将数据放在DatagramPacket 中,而TCP协议都是放在流中,通过getInputStream 和 getOutputStream 函数来获得流。

 

       在服务器端UDP需要调用含有端口参数的DatagramSocket 构造函数 , 在客户端设置DatagramSocket时,调用匿名端口构造函数。

 

       然后在构造DatagramPacket 构造函数的时候,发送端需要制定发送主机的 主机名 和 端口 。

 

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

 

 8.2.1一致资源定位器URL

 

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

 

  URL是最为直观的一种网络定位方法。使用 URL符合人们的语言习惯,容易记忆,所以应用十分广泛。而且在目前使用最为广泛的TCP/IP中对于URL中主机名的解析也是协议的一个标准,即所谓的域名解析服务。使用URL进行网络编程,不需要对协议本身有太多的了解,功能也比较弱,相对而言是比较简单的,所以在这里我们先介绍在Java中如何使用 URL进行网络编程来引导读者入门。

 

 

 

8.2.2 URL的组成

 

  protocol://resourceName

 

  协议名(protocol)指明获取资源所使用的传输协议,如http、ftp、gopher、file等,资源名(resourceName)则应该是资源的完整地址,包括主机名、端口号、文件名或文件内部的一个引用。例如:

 

   http://www.sun.com/  协议名://主机名

 

   http://home.netscape.com/home/welcome.html  协议名://机器名+文件名

 

   http://www.gamelan.com:80/Gamelan/network.html#BOTTOM  协议名://机器名+端口号+文件名+内部引用

 

  

 

  端口号是和Socket编程相关的一个概念,初学者不必在此深究,在后面会有详细讲解。内部引用是HTML中的标记,有兴趣的读者可以参考有关HTML的书籍。

 

 

 

8.2.3 创建一个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){

 

  …

 

  //exception handler code here

 

  …

 

  }

 

8.2.4 解析一个URL

 

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

 

   public String getProtocol() 获取该URL的协议名。

 

   public String getHost() 获取该URL的主机名。

 

   public int getPort() 获取该URL的端口号,如果没有设置端口,返回-1。

 

   public 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 对象,并获取它的各个属性。

 

  import java.net.*;

 

  import java.io.*;

 

  public class ParseURL{

 

  public static void main (String [] args) throws Exception{

 

  URL Aurl=new URL(" http://java.sun.com:80/docs/books/ ");

 

  URL tuto=new URL(Aurl,"tutorial.intro.html#DOWNLOADING");

 

   System.out.println("protocol="+ tuto.getProtocol());

 

   System.out.println("host ="+ tuto.getHost());

 

   System.out.println("filename="+ tuto.getFile());

 

   System.out.println("port="+ tuto.getPort());

 

   System.out.println("ref="+tuto.getRef());

 

   System.out.println("query="+tuto.getQuery());

 

   System.out.println("path="+tuto.getPath());

 

   System.out.println("UserInfo="+tuto.getUserInfo());

 

   System.out.println("Authority="+tuto.getAuthority());

 

  }

 

  }

 

  执行结果为:

 

    protocol=http host =java.sun.com filename=/docs/books/tutorial.intro.html

 

   port=80

 

    ref=DOWNLOADING

 

   query=null

 

    path=/docs/books/tutorial.intro.html

 

   UserInfo=null

 

    Authority=java.sun.com:80

 

 

 

 

 

8.2.5 从URL读取WWW网络资源

 

  当我们得到一个URL对象后,就可以通过它读取指定的WWW资源。这时我们将使用URL的方法openStream(),其定义为:

 

         InputStream openStream();

 

  

 

  方法openSteam()与指定的URL建立连接并返回InputStream类的对象以从这一连接中读取数据。

 

  public class URLReader {

 

  public static void main(String[] args) throws Exception {

 

                      //声明抛出所有例外

 

    URL tirc = new URL(" http://www.tirc1.cs.tsinghua.edu.cn/ ");

 

                      //构建一URL对象

 

    BufferedReader in = new BufferedReader(new InputStreamReader(tirc.openStream()));

 

    //使用openStream得到一输入流并由此构造一个BufferedReader对象

 

    String inputLine;

 

    while ((inputLine = in.readLine()) != null)

 

                 //从输入流不断的读数据,直到读完为止

 

       System.out.println(inputLine); //把读入的数据打印到屏幕上

 

    in.close(); //关闭输入流

 

  }

 

  }

 

 8.2.6 通过URLConnetction连接WWW

 

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

 

  类URLConnection也在包 java.net中定义,它表示Java程序和URL在网络上的通信连接。当与一个URL建立连接时,首先要在一个URL对象上通过方法 openConnection()生成对应的URLConnection对象。例如下面的程序段首先生成一个指向地址 http://edu.chinaren.com/index.shtml 的对象,然后用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();

 

  通过返回的输入/输出流我们可以与远程对象进行通信。看下面的例子:

 

  URL url =new URL (" http://www.javasoft.com/cgi-bin/backwards ");

 

  //创建一URL对象

 

  URLConnectin con=url.openConnection();

 

  //由URL对象获取URLConnection对象

 

   DataInputStream dis=new DataInputStream (con.getInputSteam());

 

  //由 URLConnection获取输入流,并构造DataInputStream对象

 

  PrintStream ps=new PrintSteam(con.getOutupSteam());

 

  //由URLConnection获取输出流,并构造 PrintStream对象

 

  String line=dis.readLine(); //从服务器读入一行

 

   ps.println("client…"); //向服务器写出字符串 "client…"

 

  其中backwards为服务器端的CGI程序。实际上,类URL的方法openSteam()是通过URLConnection来实现的。它等价于

 

     openConnection().getInputStream();

  基于URL的网络编程在底层其实还是基于下面要讲的 Socket接口的。WWW,FTP等标准化的网络服务都是基于TCP协议的,所以本质上讲URL编程也是基于TCP的一种应用。

 

 

java网络编程之socket网络编程示例(服务器端/客户端)

 

 

 

Java为TCP协议提供了两个类,分别在客户端编程和服务器端编程中使用它们。在应用程序开始通信之前,需要先创建一个连接,由客户端程序发起;而服务器端的程序需要一直监听着主机的特定端口号,等待客户端的连接。在客户端中我们只需要使用Socket实例,而服务端要同时处理ServerSocket实例和Socket实例;二者并且都使用OutputStream和InpuStream来发送和接收数据。

 

学习一种知识最好的方式就是使用它,通过前面的笔记,我们已经知道如何获取主机的地址信息,现在我们通过一个简单的程序来初步学习传输层使用了TCP协议的Socket编程。

 

TCP服务器端

 

在Socket编程中,服务器端远比客户端要复杂得多。服务器端的工作就是建立一个通信终端,被动的等待客户端的连接。下面这个服务器端程序的示例的作用是:监听从控制台输入获取的端口号,并且将客户端发送过来的消息,再发送回去。

 

复制代码 代码如下:

 


importjava.net.*;
importjava.text.MessageFormat;
importjava.io.*;

publicclassTCPEchoServer{

privatestaticfinalintBUFSIZE=32;

publicstaticvoidmain(String[]args)throwsIOException{
//TODOAuto-generatedmethodstub
    //从控制台获取需要监听的端口号
if(args.length!=1)
thrownewIllegalArgumentException("Parameter(s):<Port>");
//获取端口号
intservPort=Integer.parseInt(args[0]);
//实例化一个ServerSocket对象实例
ServerSocketservSocket=newServerSocket(servPort);
System.out.println(MessageFormat.format("开始启动监听,端口号:{0}",args[0]));

//初始接收数据的总字节数
intrecvMsgSize;
//接收数据的缓冲区
byte[]receiveBuf=newbyte[BUFSIZE];

//循环迭代,监听端口号,处理新的连接请求
while(true){
//阻塞等待,每接收到一个请求就创建一个新的连接实例
SocketclntSocket=servSocket.accept();
//获取连接的客户端的SocketAddress
SocketAddressclientAddress=clntSocket.getRemoteSocketAddress();
//打印输出连接客户端地址信息
System.out.println("Handlingclientat"+clientAddress);
//从客户端接收数据的对象
InputStreamin=clntSocket.getInputStream();
//向客户端发送数据的对象
OutputStreamout=clntSocket.getOutputStream();
//读取客户端发送的数据后,再发送到客户端
while((recvMsgSize=in.read(receiveBuf))!=-1){
out.write(receiveBuf,0,recvMsgSize);
}
//客户端关闭连接时,关闭连接
System.out.println("客户端关闭连接");
clntSocket.close();
}
}
}

 


TCP客户端

在Socket编程中,首先客户端需要向服务器端发送,然后被动的等待服务器端的响应。下面的示例中:我们向服务器端发送信息,等待服务器端发送的消息,并打印显示出来。 

 

复制代码 代码如下:

 


importjava.io.*;
importjava.net.Socket;
importjava.net.SocketException;

publicclassTCPEchoClient{
publicstaticvoidmain(String[]args)throwsIOException{
//TODOAuto-generatedmethodstub
    //判断从控制台接受的参数是否正确
if((args.length<2)||(args.length>3))
thrownewIllegalArgumentException(
"Parameter(s):<Server><Word>[<Port>]]");
//获取服务器地址
Stringserver=args[0];
//获取需要发送的信息
byte[]data=args[1].getBytes();
//如果有三个从参数那么就获取发送信息的端口号,默认端口号为8099
intservPort=(args.length==3)?Integer.parseInt(args[2]):8099;
//根据服务器地址和端口号实例化一个Socket实例
Socketsocket=newSocket(server,servPort);
System.out.println("Connectedtoserver...sendingechostring");
//返回此套接字的输入流,即从服务器接受的数据对象
InputStreamin=socket.getInputStream();
//返回此套接字的输出流,即向服务器发送的数据对象
OutputStreamout=socket.getOutputStream();
//向服务器发送从控制台接收的数据
out.write(data);
//接收数据的计数器,将写入数据的初始偏移量
inttotalBytesRcvd=0;
//初始化接收数据的总字节数
intbytesRcvd;
while(totalBytesRcvd<data.length){
//服务器关闭连接,则返回-1,read方法返回接收数据的总字节数
if((bytesRcvd=in.read(data,totalBytesRcvd,data.length
-totalBytesRcvd))==-1)
thrownewSocketException("与服务器的连接已关闭");
totalBytesRcvd+=bytesRcvd;
}
//打印服务器发送来的数据
System.out.println("Received:"+newString(data));
//关闭连接
socket.close();
}
}

 

首先运行服务器端,监听8099端口:

 

接着运行客户端程序,并且向服务器端发送消息:

 

再次查看我们的服务器端控制台,我们可以看到前面客户端连接的地址信息:

 

posted @ 2017-05-17 22:48  未来IT追随者  阅读(5765)  评论(0编辑  收藏  举报