19. JAVA 网络编程 Part 1 (IP与InetAddress类、URL与URLConnection类、URLEncoder与URLDecoder类、TCP&UDP)---- 学习笔记

本章目标:

  • 了解IP地址与InetAddress类的关系
  • 了解如何使用URL定位网络资源
  • 了解编码和解码的操作
  • 了解ServerSocket类与Socket类的关系、客户端与服务器端的通信模式
  • 了解如何将多线程机制应用在服务器开发上
  • 了解UDP程序与TCP程序的实现区别

     网络可以使不同物理位置上的计算机达到资源共享和通信的目的,在Java中也提供了专门的网络开发程序包 ---  java.net, 来进行网络程序的开发。

     Java的网络编程提供了两种通信协议: TCP(传输控制协议) 和 UDP(数据报协议)。

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

关于TCP及UDP协议

      TCP和UDP都属于传输层协议,TCP(Transmission Control Protocol)是可靠的传输协议,传输前会采用“三方握手” 的方式建立连接,以保证传输的可靠性;UDP(User Datagram Protocol)协议是不可靠的传输协议,即发送出去的数据不一定接收得到,网上的聊天工具一般采用此种协议!!

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

19.1 IP(Internet Protocol) 与  InetAddress类

        19.1.1 IP地址(Internet Protocol)简介

        互联网上的每一台计算机都有一个唯一表示自己的标记,这个标记就是IP地址。

        IP地址使用32位长度二进制数据表示,一般在实际中看到的大部分IP地址都是以十进制的数据形式表示的,如192.168.1.3

【IP地址格式】

IP地址 = 网络地址 + 主机地址
  • 网络号:  用于识别主机所在的网络
  • 主机号:  用于识别该网络中的主机

       IP地址分为5类:各类容纳的地址数目不同。

  1. A类, 保留给政府结构
  2. B类, 分配给中等规模的公司
  3. C类, 分配给任何需要的人
  4. D类, 用于组播
  5. E类, 用于实验

      5类IP地址的范围如下图所示:

        在以上的地址分类中可以发现: 没有 127.X.X.X的表示,因为其实保留地址(用作循环测试)在开发中经常使用127.0.0.1表示本机的IP地址!!!

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

IP地址有IPv4和IPv6两类

     IPv4(Internet Protocol version 4)是互联网协议的第4个版本,也是使用最广泛的版本。但是IPv4已经无法满足当今互联网上的主机数量,所以在此基础上又产生了新的版本IPv6,使用IPv6可以比IPv4容纳更多的主机

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

        19.1.1 InetAddress类

       InetAddress类主要表示IP地址,这个类有两个子类: Inet4Address、Inet6Address, 一个用于表示IPv4;另一个用于表示IPv6.

       InetAddress类的常用方法如下:

范例:测试InetAddress类

package org.forfan06.inetaddressdemo;
import java.net.InetAddress;
public class InetAddressDemo01{
	public static void main(String args[]) throws Exception{
		InetAddress locAddress = null;
		InetAddress remAddress =null;
		locAddress = InetAddress.getLocalHost();  //得到本地InetAddress对象
		remAddress = InetAddress.getByName("www.csdn.net");  //取得远程InetAddress对象

		System.out.println("本机IP地址是:" + locAddress.getHostAddress());  //得到本机的IP地址
		System.out.println("CSDN的IP地址:" + remAddress.getHostAddress());
		System.out.println("本机是否可达:" + locAddress.isReachable(5000));
	}
}

19.2 URL与URLConnection类

        19.2.1 URL

          URL(Uniform Resource Locator)统一资源定位符,可以直接使用此类找到互联网上的资源(如一个简单的网页)

          URL类的常用方法如下所示

范例:使用URL读取内容

package org.forfan06.urldemo;
import java.net.URL;
import java.io.InputStream;
import java.util.Scanner;
public class URLDemo01{
	public static void main(String args[]) throws Exception{
		URL url = new URL("http", "localhost", 80, "default.html");  //指定操作的URL
		InputStream  input = url.openStream();  //打开输入流,读取URL内容
		Scanner scan = new Scanner(input);  //实例化Scanner对象
		scan.useDelimiter("\n");  //设置读取分隔符
		while(scan.hasNext()){
			System.out.println(scan.next());   //不断输入读取到的内容
		}
	}
}
上面程序运行时,使用URL找到了指定主机上的default.html页面资源,并使用Scanner将页面中的内容下载下来直接显示在屏幕上。

        19.2.2 URLConnection

         URLConnection是封装访问远程网络资源一般方法类,通过它可以建立与远程服务器的链接,检查远程资源的一些属性。

         URLConnection类的常用方法

      URLConnection对象可以通过URL类的openConnection()方法取得。 下面通过URLConnection对象取得一个URL的基本信息

范例:取得URL的基本信息

package org.forfan06.urlconnectiondemo;
import java.net.URL;
import java.net.URLConnection;
public class URLConnectionDemo01{
	public static void main(String args[]) throws Exception{
		URL url = new URL("http://www.csdn.net"); //指定操作的URL
		URLConnection urlCon = url.openConnection();   //建立连接
		System.out.println("内容大小:" + urlCon.getContentLength());
		System.out.println("内容类型:" + urlCon.getContentType());
	}
}

19.3 URLEncoder与URLDecoder

        在使用URL访问时,经常会看到在地址后面会有很多其他的附带信息。 例如,在www.baidu.com上搜索 csdn, 观察地址栏:http://www.baidu.com/s?wd=csdn&rsv_spt=1&issp=1&f=8&rsv_bp=0&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_sug3=4&rsv_sug4=164&rsv_sug1=4&rsv_sug2=0&inputT=2278

       后面的一长串的附带信息。如果该附带信息上英文单词是可以正常显示的,但是对于中文,则会将其进行一系列的编码操作。

       在Java中如果要显示中文信息的编码和解码操作就必须使用URLEncoder和URLDecoder两个类。

       URLEncoder可以为传递的内容进行编码;而URLDecoder可以为传递的内容进行解码。

       URLEncoder类和URLDecoder类的常用方法:

范例:编码及解码操作

package org.forfan06.urlcoderdemo;
import java.net.URLEncoder;
import java.net.URLDecoder;
public class URLCoderDemo{
	public static void main(String args[]) throws Exception{
		String keyWord = "csdn 中文网";
		String encode = URLEncoder.encode(keyWord, "UTF-8");  //对内容进行编码
		System.out.println("编码之后的内容:" + encode);
		String decode = URLDecoder.decode(encode, "UTF-8");  //进行解码
		System.out.println("解码之后的内容:" + decode);
	}
}

程序运行结果:

编码之后的内容:csdn+%E4%B8%AD%E6%96%87%E7%BD%91
解码之后的内容:csdn 中文网

19.4 TCP程序设计

      在Java中使用Socket(即套接字)完成TCP程序的开发,使用此类可以方便地建立可靠的、双向的、持续的、点对点的通信连接

      在Socket的程序开发中,服务器端使用ServerSocket等待客户端的连接,对于Java的网络程序来讲,每一个客户端都使用一个Socket对象表示!!!!!

                                                           

        19.4.1 ServerSocket类与Socket类

        ServerSocket类主要用在服务器端程序的开发上,用于接收客户端的连接请求。

        ServerSocket类的常用方法如下:

        在服务器端每次进行时都要使用accept()方法等待客户端连接,此方法执行之后服务器端将进入阻塞状态,直到客户端连接之后程序才可以向下继续执行。此方法的返回值类型是Socket,每一个Socket都表示一个客户端对象!!

       Socket类的常用方法:

        在客户端,程序可以通过Socket类的getInputStream()方法取得服务器的输出信息,在服务器端可以通过getOutputStream()方法取得客户端的输出信息。

                             

        从上图可以看出,在网络程序中要使用输入及输出流得形式完成信息的传递,所以需要导入java.io包

        19.4.2 第一个TCP程序

       下面通过ServerSocket类及Socket类完成一个服务器的程序开发,此服务器向客户端输出“hello world!”的字符串信息

范例:建立服务器程序:

package org.forfan06.netdemo;
import java.io.*;
import java.net.*;
public class MyServer{
	public static void main(String args[]) throws Exception{
		ServerSocket server = null;  //声明ServerSocket对象
		Socket client = null; //一个Socket对象表示一个客户端
		PrintStream out = null;   //声明打印流,向客户端输出
		server = new ServerSocket(8888);  //此时服务器在8888端口上等待客户端的访问
		System.out.println("服务器运行,等待客户端连接");
		client = server.accept();   //程序阻塞,等待客户端连接
		String str = "hello world!";    //要向客户端输出的信息
		out = new PrintStream(client.getOutputStream());    //实例化打印流对象,输出信息
		out.println(str);   //输出信息
		out.close();      //关闭打印流
		client.close();   //关闭客户端连接
		server.close();  //关闭服务器连接
	}
}

        程序执行到accept()方法后,程序进入到阻塞状态,此阻塞状态会在客户端连接之后改变。。。

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

可以使用talnet命令进行验证

       服务器端程序建立完成之后,因为其是以TCP为通信协议的,所以可以直接使用telnet命令进行连接测试,输入“open localhost 8888”即可取得服务器的输出信息

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

      服务器程序编写完成之后,下面介绍如何通过客户端访问服务器,直接使用Socket类指定连接的地址及端口号,并通过输入流读取服务器的输出信息

范例:编写客户端程序

package org.forfan06.netdemo;
import java.io.*;
import java.net.*;
public class ClientCode{
	public static void main(String args[]) throws Exception{
		Socket client = null;     //声明Socket对象
		client = new Socket("localhost", 8888);    //指定连接的主机和端口
		BufferedReader buf = null;    //声明BufferedReader对象,接收信息
		buf = new BufferedReader(new InputStreamReader(client.getInputStream()));   //取得客户端的输入流
		String str = buf.readLine();   //读取信息
		System.out.println("服务器端输出内容:" + str);
		client.close();   //关闭Socket
		buf.close();     //关闭输入流
	}
}
        此时客户端从服务器端将信息读取进来,读取完毕后,因为服务器端此时只能处理一次连接请求,所以也将关闭。

        19.4.3 案例: Echo程序

        Echo程序是一个网络编程通信交互的一个经典案例,称为回应程序。 即客户端输入哪些内容,服务器端会在这些内容前加上“ECHO:” 并将信息发回给客户端,下面就完成这样的一个程序

       之前的程序代码,服务器端每次执行完毕后服务器都会推出,这是因为服务器端只能接收一个客户端的连接,主要是由于accept()方法只能使用一次!!!

       本程序中将通过循环的方式使用accept(), 这样,每一个客户端执行完毕后,服务器端都可以重新等待用户连接!!

范例:EchoServer

package org.forfan06.netdemo;
import java.net.*;
import java.io.*;
public class EchoServer{
	public static void main(String args[]) throws Exception{
		ServerSocket server = null;   //定义ServerSocket对象
		Socket client = null;     //定义Socket对象,表示客户端。一个Socket对象表示一个客户端
		PrintStream out = null;     //定义输出流
		BufferedReader buf = null;   //用于接收客户端发送来的信息
		server = new ServerSocket(8888);   //此服务器在8888端口上进行监听
		boolean f = true;     //定义一个标记位     ****这里没有停止的语句啊!!!!!!!!!!****
		while(f){          //无限制接收客户端连接
			System.out.println("服务器运行,等待客户端连接...");
			client = server.accept();    //接收客户端连接
			buf = new BufferedReader(new InputStreamReader(client.getInputStream()));   //得到客户端的输入信息
			out = new PrintStream(client.getOutputStream());     //实例化客户端的输出流
			boolean flag = true;    //标记位,表示一个客户端是否操作完毕
			while(flag){    //客户端循环操作
				String str = buf.readLine();   //在此处不断地接收信息
				if(str == null || "".equals(str)){   //判断输入的信息是否为空
					flag = false;    //客户端操作结束
				}else{
					if("bye".equals(str)){     //如果输入信息为bye表示结束
						flag = false;    //客户端操作结束
					}else{
						out.println("ECHO:" + str);   //向客户端回显信息
					}
				}
			}
			out.close();     //关闭输入流
			client.close();  //关闭客户端
		}
		server.close();   //关闭服务器端
	}
}
        服务器运行之后,要等待客户端的连接才能解除阻塞状态。。。。

范例:EchoClient

package org.forfan06.netdemo;
import java.net.*;
import java.io.*;
public class EchoClient{
	public static void main(String args[]) throws Exception{
		Socket client = null;     /声明Socket对象
		client = new Socket("localhost", 8888);    //指定连接主机及端口
		BufferedReader buf = null;    //接收服务器端发送回来的信息
		PrintStream out = null;   //输出流,向服务器端发送信息
		BufferedReader input = null;   //声明BufferedReader对象
		input = new BufferedReader(new InputStreamReader(System.in));  //从键盘接收数据
		out = new PrintStream(client.getOutputStream());     //向服务器端输出信息
		buf = new BufferedReader(new InputStreamReader(client.getInputStream()));    //接收服务器端输入信息
		boolean flag = true;   //定义标志位
		while(flag){    //不断发送和接收数据
			System.out.print("输入信息:");
			String str = input.readLine();    //从键盘接收数据
			out.println(str);      //向服务器端输入信息
			if("bye".equals(str)){     //如果输入信息为bye表示结束
				flag = false;    //客户端操作结束
			}else{
				String echo = buf.readLine();   //接收ECHO信息
				System.out.println(echo);       //输出ECHO信息
			}
		}
		client.close();   //关闭Socket
		buf.close();      //关闭输入流
	}
}

        运行程序后,所有的输入信息最终都会通过回显的方式发回给客户端,并且前面加上了“ECHO”的信息。

        另外在该程序中,当一个客户端结束之后,服务器端并不会退出,会等待下一个用户连接,继续执行。

        在该程序中,存在一个严重的问题:现在的服务器端每次只能有一个用户连接,属于单线程的处理机制。其他客户端要想连接服务器,要等待当前服务器出现空闲才可以连接。为了保证服务器可以同时连接多个客户端,可以加入多线程机制,即每一个客户端连接之后都启动一个县城。这样一个服务器就可以同时支持多个客户端连接。如下图所示:

                                      

        19.4.4 在服务器上应用多线程

        对于服务器端来说,如果要加入多线程机制,则应该在每个用户连接之后启动一个新的线程。

        下面建立一个EchoThread类,此类专门用于处理多线程操作,此时的多线程使用Runnable接口实现。

范例:EchoThread

package org.forfan06.netdemo;
import java.net.*;
import java.io.*;
public class EchoThread implements Runnable{   //extends Threadable实现Runnable接口
	private Socket client = null;     //接收客户端
	public EchoThread(Socket client){    //通过构造方法设置Socket对象
		this.client = client;
	}
	public void run(){   //覆写run()方法
		PrintStream out = null;    //定义输出流
		BufferedReader buf = null;   //用于接收客户端发送来的信息
		try{
			buf = new BufferedReader(new InputStreamReader(client.getInputStream()));   //得到客户端的输入信息
			out = new PrintStream(client.getOutputStream());     //实例化客户端的输出流
			boolean flag = true;   //标志位,表示一个客户端是否操作完毕
			while(flag){   //客户端循环操作
				String str = buf.readLine();  //接收信息
				if(str == null || "".equals(str)){    //判断输入的信息是否是空
					flag = flase;   //客户端操作结束
				}else{
					if("bye".equals(str)){   //如果输入信息为bye表示结束
						flag = false;     //客户端操作结束
					}else{
						out.println("ECHO:" + str);   //向客户端回显信息
					}
				}
			}
			out.close();     //关闭输出流
			client.close();  //关闭客户端
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

        在上面线程类,主要功能是接收每一个客户端的Socket,并通过循环的方式接收客户端的输入信息及向客户端输出信息。

        下面编写EchoThreadServer类,并使用上面的EchoThread类。

范例:EchoThreadServer类

package org.forfan06.netdemo;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoThreadServer{
	public static void main(String args[]) throws Exception{
		ServerSocket server = null;    //定义ServerSocket对象
		Socket client = null;   //定义Socket对象,表示客户端
		server = new ServerSocket(8888);   //此服务器在8888端口上进行监听
		boolean f = true;     //标记位
		while(f){    //无限制接受客户端连接
			System.out.println("服务器运行,等待客户端连接....");
			client = server.accept();   //接收客户端连接
			new Thread(new EchoThread(client)).start();   //实例化并启动一个线程对象
		}
		server.close();   //关闭服务器
	}
}
       在服务器端,每一个连接到服务器的客户端Socket都会以一个线程的方式运行,这样无论有多个客户端连接都可以同时完成操作。

19.5  UDP程序设计

        19.5.1 UDP简介

       TCP的所有操作都必须建立可靠的连接,这样肯定会浪费大量的系统性能。为了减少这种开销,在网络中又提供了另外一种传输协议  ---- UDP,是不可靠的连接,这种协议在各种聊天工具中被广泛地应用。

       使用UDP发送的信息,对方不一定会接收到。所有的信息使用数据报的形式发送出去,所以这就要求客户端要始终等待服务器发送的消息才能进行接收,在Java中使用DatagramSocket类和DatagramPacket类完成UDP程序的开发。

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

关于UDP开发中服务器和客户端的解释

      使用UDP开发的网络程序类似于平常使用的手机,手机实际上相当于一个客户端,如果手机要想正常地接收到信息,则手机肯定要先开机才行!!!!!!

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

        19.5.2 UDP程序实现

         在UDP开发中使用DatagramPacket类包装一条要发送的信息,之后使用DatagramSocket类用于完成信息的发送操作,这两个类的常用方法分别如下所示:

       要想实现UDP程序,则首先应该从客户端编写,在客户端指定要接收数据的端口和取得数据。

范例:UDP客户端 -- UDPClient

package org.forfan06.netdemo;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UPDClient{
	public static void main(String args[]) throws Exception{
		DatagramSocket ds = null;   //声明DatagramSocket对象
		byte[] buf = new byte[1024];  //定义接收数据的字节数组
		DatagramPacket dp = null;  //声明DatagramPacket对象
		ds = new DatagramSocket(9000); //此客户端在9000端口监听
		dp = new DatagramPacket(buf, 1024);  //指定接收数据的长度为1024
		System.out.println("等待接收数据....");   //输出信息
		ds.receive(dp);   //接收数据
		String str = new String(dp.getData(), 0, dp.getLength()) + " from" +
			dp.getAddress().getHostAddress() + " : " + dp.getPort();   //接收数据
		System.out.println(str);  //输出数据
		ds.close();  //关闭
	}
}
          程序运行后,客户端程序已经打开了监听端口,等待服务器端向客户端发送信息。

范例:编写UDP发送的服务器端程序 ---  UDPServer

package org.forfan06.netdemo;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPServer{
	public static void main(String args[]) throws Exception{
		DatagramSocket ds = null;       //声明DatagramSocket对象
		DatagramPacket dp = null;       //声明DatagramPacket对象
		ds = new DatagramSocket(3000);  //服务器端在3000端口监听
		String str = "hello world!!";  //准备要发送的信息
		//实例化DatagramPacket对象,指定数据内容、数据长度、要发送的目标地址、发送端口
		dp = new DatagramPacket(str.getBytes(), str.length(), InetAddress.getByName("localhost"), 9000);   //此处向客户端所在的9000端口发送信息
		System.out.println("发送信息...");   //信息输出
		ds.sent(dp);  //发送数据报
		ds.close();
	}
}
        服务器端运行完成后,客户端可以接收服务器端发送过来的信息。。。。

19.6 本章要点

  1. InetAddress表示IP地址的操作类,可以通过此类指定要连接的主机名称
  2. 在开发中如果要取得一个网络资源可以使用URL类进行定位,之后使用IO流的方式进行读取
  3. 使用URLEncoder可以为信息编码,使用URLDecoder可以为编码的内容进行解码操作
  4. ServerSocket主要用在TCP协议的服务器程序开发上,使用accept()方法等待客户端连接,每一个连接的客户端都使用一个Socket表示
  5. 服务器端加入多线程机制之后,就可以同时为多个用户提供服务
  6. UDP属于不可靠的连接协议,采用数据报的形式,对于服务器发送的信息,客户端不一定能接收到

19.7 习题

  1. 将Echos程序进行扩充,通过图形界面编写服务器和客户端,实现通过按钮控制服务器的开启与关闭,并使用界面发送和接收服务器的返回信息
  2. 使用网络程序完成一个多用户的用户注册操作,所有的用户只要连接到服务器上,就可以对数据库进行增加数据的操作
  3. 使用TCP程序完成以下功能:  在服务器端的数据库中建立部门表(部门编号、部门名称、部门位置), 当客户端连接到服务器之后,服务器将这些信息全部返回到客户端进行输出

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值