您的位置:首页 > 理论基础 > 计算机网络

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

2014-11-07 14:51 1246 查看

本章目标:

了解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类:各类容纳的地址数目不同。

A类, 保留给政府结构
B类, 分配给中等规模的公司
C类, 分配给任何需要的人
D类, 用于组播
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 本章要点

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

19.7 习题

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


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐