第十章 Java网络编程入门

《Java程序设计实例与操作》(丁永卫)目录

10.1 了解Java网络编程基础知识

一、TCP/IP协议

   TCP/IP是Transmission Control Protocol/Internet Protocol的缩写,中文译名为传输控制协议/因特网互联协议,又叫网络通讯协议。这个协议是Internet最基本的协议,是供已连接因特网的计算机进行通信的通信协议。
   TCP协议为传输控制协议,它负责聚集信息或把文件拆分成更小的包。这些包通过网络传送到接收端的TCP层,接收端的TCP层把包还原为原始文件。
   IP协议是网际协议,它处理每个包的地址部分,使这些包能正确到达目的地。网络上的网关计算机根据信息的地址来进行路由选择。即使来自同一文件的分包路由也有可能不同,但最后会在目的地汇合。

二、TCP协议与UDP协议

   TCP协议:TCP协议是一种面向连接的、保证可靠传输的协议。要使用TCP协议传输数据,客户端和服务器之间必须先建立连接,然后才能进行通信。通过TCP协议传输得到的是一个顺序的、无差错的数据流。
   UDP协议:UDP是User Datagram Protocol(用户数据报协议)的简称,是一种面向无连接的协议,它把信息包装成数据报进行传输,数据报中包含完整的源地址或目的地址。采用UDP协议进行传输数据,不能保证数据报是否到达目的地、到达目的地的时间以及内容的正确性。因此,UDP协议是一个不可靠的传输协议。

三、IP地址

   Internet中的每一台主机都分配有一个唯一的32位地址,该地址称为IP地址。IP地址由32位二进制数表示,这个数是用“.”分隔的4个十进制数标识,如202.114.64.32或218.98.131.253。
   在Java中,用InteAddress类来描述IP地址。这个类没有公共的构造方法,但是它提供了三个用来获得InetAddress对象的静态方法。
 InetAddress getLocalHost():返回一个本地主机的InetAddress对象。
 InetAddress getByName(String host):返回主机名指定的InetAddress对象。
 InetAddress[] getAllByName():对于某个多IP地址主机,可用该方法得到一个IP地址数组。

// InetAddressTest.java
package Chapter10;

import java.net.InetAddress;

public class InetAddressTest {
	public static void main(String[] args) throws Exception {
		InetAddress ia;
		// 获得本地主机的InetAddress对象
		ia = InetAddress.getLocalHost();
		// 输出本地主机IP地址
		System.out.println("本地主机IP地址:" + ia.getHostAddress());
		// 输出本地主机名
		System.out.println("本地主机名:" + ia.getHostName());
		// 获得百度网主机的InetAddress对象
		ia = InetAddress.getByName("www.baidu.com");
		// 输出百度网主机IP地址
		System.out.println("百度网主机IP地址:" + ia.getHostAddress());
		// 输出百度网主机名
		System.out.println("百度网主机名:" + ia.getHostName());
	}
}

四、端 口

   通常,一台主机上总有许多进程需要与网络资源进行网络通信。网络通信的对象是主机中运行的进程,显然,此时只用IP地址来标识这么多个进程显然是不够的,而端口号就是为了在一台主机上提供更多的网络资源而采取的一种手段。也就是说,只有通过IP地址和端口号才能唯一地确定网络通信中的进程。
   如果把IP地址比作一间房子,那么端口就是出入这间房子的门。不过,真正的房子只有几个门,但一个IP地址的端口却有65536之多。端口是用端口号来标记的,端口号用0到65535的整数表示。

10.2 使用URL访问网络资源

   URL(Uniform Resource Locator)是统一资源定位符的简称,它是对Internet资源的一个引用。在大多数的情况下,资源表示为一个文件,如一个HTML文档,一个图像文件,或一个声音片段等。因此,可以将URL理解为一个Internet资源的地址,它的通用的格式为:

 <PROTOCOL>://<HOSTNAME:PORT>/<PATH>/<FILE>

   其中,PROTOCOL表示Internet协议,常用的协议有HTTP、FTP与SMTP等。
   HOSTNAME表示资源所在的Internet主机名。主机名和IP地址是一一对应的,通过域名解析可以由主机名得到IP地址。
   PORT表示端口号。每一个Internet协议都有自己对应的端口号。
   PATH和FILE分别表示资源的路径名和文件名。

一、创建URL对象

  URL类的构造方法如下:
 URL(String spec):使用表示URL地址的字符串spec创建URL
对象。例如:

  URL urlBase=new URL("http://www.sina.com.cn/");

 URL(Url baseURL,String relativeURL):使用绝对地址baseURL与相对地址relativeURL创建URL对象。
 URL(String prorocol,String host,int port,String file):使用网络协议prorocol、主机名host、端口号port、文件名file创建URL对象。例如:

URL buaa=new URL("http","www.buaa.edu.cn",80,"/library/library.htm"); 

 URL(String prorocol,String host,String file):使用网络协议prorocol、主机名host、文件名file创建URL对象。例如:

URL buaa=new URL("http","www.buaa.edu.cn","/library/library.htm"); 

二、直接通过URL对象读取内容

   在成功建立一个URL对象后,我们就可以调用它的openStream()方法从网络资源中读取内容了 。

// OpenStreamTest.java
package Chapter10;

import java.io.*;
import java.net.*;

public class OpenStreamTest {
	public static void main(String[] args) {
		try {
			URL google = new URL("http://www.google.com/");
			DataInputStream dis = new DataInputStream(google.openStream());
			String inputLine;
			while ((inputLine = dis.readLine()) != null) {
				System.out.println(inputLine);
			}
			dis.close();
		} catch (MalformedURLException me) { // 创建一个URL对象失败
			System.out.println("MalformedURLException:" + me);
		} catch (IOException ioe) { // 打开一个连接失败
			System.out.println("IOException:" + ioe);
		}
	}
}

   当运行这个程序时,我们可以得到位于http://www.google.com网站中的index.html文件中的HTML标记和内容。

<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; harset=Big5">
<title>Google</title>
<script>window.google=
{kEI:"oHkgTP7UIMyHkAWEyb0i",kEXPI:"24891,25013,25164,25233",
kCSI: {e:"24891,25013,25164,25233",ei:"oHkgTP7UIMyHkAWEyb0i",
expi:"24891,25013,25164,25233"}, ml:function(){},kHL:"zh-TW",
time:function(){return(new ate).getTime()},log:function(b,d,c)
{var a=new Image,e=google,g=e.lc,f=e.li;a.onerror=(a.onload=(a.onabort=function()
{delete g[f]}));g[f]=a;c=c||"/gen_204?atyp=i&ct="+b+"&cad="+d+"&zx="+google.time();
a.src=c;e.li=f+1},lc:[],li:0,Toolbelt:{}};
.....
</script>
</head>
<body bgcolor=#ffffff text=#000000 link=#0000cc vlink=#551a8b alink=#ff0000 
onload="document.f.q.focus();if(document.images)
new Image().src='/images/srpr/nav_logo13.png'">
<textarea id=csi style=display:none></textarea>
<div id=ghead>
..... 

三、建立一个URL连接并从中读取内容

   如果我们同时还想向其输出数据,则必须建立一个URL连接,然后才能对其进行读写,这时就要用到URLConnection类了。
   当与一个URL对象建立连接时,首先要通过URL对象的openConnection()方法生成URLConnection对象。

// ConnectionTest.java
package Chapter10;

import java.io.*;
import java.net.*;

public class ConnectionTest {
	public static void main(String[] args) {
		try {
			URL baidu = new URL("http://www.google.com/");
			URLConnection baiduConnection = baidu.openConnection();
			// 创建数据输入流
			DataInputStream dis = new DataInputStream(baiduConnection
					.getInputStream());
			String inputLine;
			while ((inputLine = dis.readLine()) != null) {
				System.out.println(inputLine);
			}
			dis.close();
		} catch (MalformedURLException me) { // 创建一个URL对象失败
			System.out.println("MalformedURLException:" + me);
		} catch (IOException ioe) { // 打开一个连接失败
			System.out.println("IOException:" + ioe);
		}
	}
}

10.3 掌握使用Socket进行网络通信的方法

   多个TCP连接或多个应用程序进程可能需要通过同一个端口传输数据。为了区别不同的应用程序进程或连接,计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。
   每个Socket主要有3个参数,即IP地址、传输层协议(TCP或UDP)和端口号。换句话说,一个Socket由这3个参数唯一确定。

   使用Socket进行服务器/客户端的通信时,可以分为三个步骤:
 服务器监听:服务器端Socket实时监听某个端口是否有连接请求。
 客户端请求:是指由客户端的Socket提出连接请求,要连接的目标是服务器端的Socket。
 连接确认:是指当服务器端Socket监听到或者接收到客户端Socket的连接请求时,它就响应客户端的请求,并建立一个新的线程,然后把服务器端Socket的描述发送给客户端。

一、流式Socket编程

   流式Socket是一种基于TCP协议的通信,即在通信开始前先由通信双方确认身份并建立一条连接通道,然后通过这条通道传送数据。在java.net包中定义了Socket类和ServerSocket类,它们是实现流式Socket通信的主要工具。

1.Socket类

   Socket类的构造方法有如下几种:
 Socket(String host,int port)
 Socket(InetAddress address,int port)
 Socket(InetAddress address,int port, InetAddress localAddr,int localPort)
   其中,host、port和address分别表示连接主机的主机名、端口号和IP地址;localAddr表示本地主机的IP地址;localPort表示本地主机端口号。
例如:

Socket myClient=new Socket("www.shu.edu",3000);
// GetDayTime.java
package Chapter10;
import java.io.*;
import java.net.*;

public class GetDayTime {
	public static void main(String[] args) {
		try {
			Socket conn = new Socket("stdtime.gov.hk", 13); // 创建Socket对象
			BufferedReader in = new BufferedReader(new InputStreamReader(conn
					.getInputStream()));
			String daytime = in.readLine();
			System.out.println("DayTime received:" + daytime);
			conn.close();
		} catch (IOException e) {
			System.out.println("Error:" + e);
		}
	}
}

程序的运行结果:DayTime received:24 JUN 2010 14:07:00 HKT

2.ServerSocket类

   ServerSocket类的构造方法如下:
 ServerSocket(int port);
 ServerSocket(int port,int count);
   其中,port表示端口号,count表示服务器所能支持的最大连接数。
例如:

ServerSocket myServer=new ServerSocket(5000);

   这里指定服务器监听的端口号是5000。当创建一个ServerSocket对象后,就可以调用accept()方法接受来自客户端的请求,其格式为:

Socket linkSocket=myServer.accept();

实例10-1 基于流式Socket的client/server编程

【实例描述】
   首先建立客户端和服务器的Socket,然后客户端向服务器发出“你好”信息,每隔800毫秒后,再向服务器发送一个随机数。而服务器将客户端发送的请求重新返回给客户端。
【技术要点】
   ① 创建ServerSocket对象时,端口号必须与Socket对象中的一致,这样服务器才能监听到来自客户端的请求。
   ② 在关闭Socket与ServerSocket时,应首先关闭与Socket、ServerSocket相关的输入/输出流,然后再关闭它们。

// Client.java
package Chapter10;

import java.io.*;
import java.net.Socket;

public class Client {
	public static void main(String[] args) {
		String clientInfo = null; // 定义客户端收到的信息
		Socket mySocket; // 声明socket对象
		DataInputStream in = null; // 声明数据输入流
		DataOutputStream out = null; // 声明数据输出流
		try {

			mySocket = new Socket("localhost", 4212); // 创建本地主机的Socket对象
			in = new DataInputStream(mySocket.getInputStream());
			out = new DataOutputStream(mySocket.getOutputStream());
			out.writeUTF("你好!"); // 向服务器输出 “你好”
			// 每隔800毫秒从输入流中读取数据,并向服务器端写随机数
			for (int i = 0; i < 4; i++) {
				clientInfo = in.readUTF(); // 读取服务器端的数据
				out.writeUTF(":" + Math.random()); // 向服务器写随机数
				System.out.println("客户端收到:" + clientInfo);
				Thread.sleep(800); // 线程休眠800毫秒
			}
			out.close(); // 关闭输出流
			in.close(); // 关闭输入流
			mySocket.close(); // 关闭客户端socket
		} catch (IOException e) {
			System.out.println("无法连接");
		} catch (InterruptedException e) {
		}
	}
}

// Server.java
package Chapter10;

import java.io.*;
import java.net.*;

public class Server {
	public static void main(String[] args) {
		ServerSocket server = null; // 声明ServerSocket对象
		Socket you; // 声明Socket对象
		String serverInfo = null; // 定义服务器收到的信息
		DataOutputStream out = null;
		DataInputStream in = null;
		try {
			server = new ServerSocket(4212); // 创建ServerSocket对象
		} catch (IOException e) {
			System.out.println("error:" + e);
		}
		try {
			you = server.accept(); // 监听客户端的请求
			in = new DataInputStream(you.getInputStream());
			out = new DataOutputStream(you.getOutputStream());
			// 每隔800毫秒从输出流读取数据,并向客户端写数据
			for (int i = 0; i < 4; i++) {
				serverInfo = in.readUTF(); // 读取客户端的数据
				// 向客户端写数据
				out.writeUTF("你好,我是服务器,你的请求是:" + serverInfo);
				System.out.println("服务器收到:" + serverInfo);
				Thread.sleep(800); // 线程休眠
			}
			in.close();
			out.close();
			you.close();
			server.close();
		} catch (IOException e) {
			System.out.println("error:" + e);
		} catch (InterruptedException e) {
		}
	}
}

   首先运行服务器类Server,然后再运行客户端类Client,客户端程序运行结果如下。
客户端收到:你好,我是服务器,你的请求是:你好!
客户端收到:你好,我是服务器,你的请求是:0.1451227473569784,
客户端收到:你好,我是服务器,你的请求是:0.2921835817805166,
客户端收到:你好,我是服务器,你的请求是:0.8486651853626107,

   服务器端程序运行结果如下。
服务器收到:你好!
服务器收到:0.1451227473569784,
服务器收到:0.2921835817805166,
服务器收到:0.8486651853626107,

二 数据报Socket编程

   在java.net包中提供了DatagramPacket和DatagramSocket类用来支持无连接的数据报Socket通信,DatagramSocket类用于在程序之间建立传送数据报的通信连接,而DatagramPacket类则用于存储数据报等信息。

1.DatagramSocket类

  DatagramSocket类的构造方法如下:
 DatagramSocket();
 DatagramSocket(int port);
 DatagramSocket(int port,InetAddress localAddr);
   其中,prot表示端口号;localAddr表示本地地址。

2.DatagramPacket类

   DatagramPacket对象是数据报传输的载体。
DatagramPacket类的构造方法如下:

 DatagramPacket(byte[] buf,int length);
 DatagramPacket(byte[] buf,int length,InetAddress address,int port);

   在客户端或服务器端接收数据之前,应该采用DatagramPacket类的第一种构造方法创建一个DatagramPacket对象,然后调用DatagramSocket类的receive()方法等待数据报的到来。
例如:

DatagramSocket socket = new DatagramSocket();	
DatagramPacket package=new DatagramPacket(buf,256);
socket.receive(); 

   在发送数据前,需要使用DatagramPacket类的第二种构造方法创建一个新的DatagramPacket对象 。
例如:

DatagramSocket socket = new DatagramSocket();	
DatagramPacket package=new DatagramPacket(buf,length,address,port);
socket.send(package); 

实例10-2 基于数据报Socket的client/server编程

【实例描述】
   实例中包含客户端和服务器。客户端每发送一个请求给服务器,服务端的客户端请求数目将加1,并把内容类似于“Hi,你是第1个访问者”的数据报返回给客户端。
【技术要点】
   在服务器端的程序通过InetAddress类的getAddress()与getPort()方法分别获得客户端的IP地址与端口号,并以它们作为参数创建DatagramPacket对象,这样才能保证把服务器端从客户端读取的文件内容返回给客户端。

// UDPClient.java
package Chapter10;

import java.io.*;
import java.net.*;

public class UDPClient {
	public static void main(String[] args) throws IOException {
		DatagramSocket socket = new DatagramSocket(); // 创建DatagramSocket对象
		byte[] buf = new byte[256];
		InetAddress address = InetAddress.getByName("localhost");
		// 创建DatagramPacket对象
		DatagramPacket packet = new DatagramPacket(buf, buf.length, address,
				2556);
		socket.send(packet); // 客户端发送数据报
		// 创建接收数据报的DatagramPacket对象
		packet = new DatagramPacket(buf, buf.length);
		socket.receive(packet); // 客户端接收数据报
		// 将接收的数据报转换为字符串
		String recevied = new String(packet.getData());
		System.out.println("客户端接收的信息:" + recevied);
		socket.close();
	}
}

// UDPServer.java
package Chapter10;

import java.io.IOException;

public class UDPServer {
	public static void main(String[] args) throws IOException {
		new ServerThread().start(); // 启动线程
	}
}

综合实例——模拟网络聊天

// ChatClient.java
package Chapter10;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;

public class ChatClient extends Frame implements ActionListener {
	Label lb = new Label("聊天"); // 创建标签
	Panel pn = new Panel(); // 创建窗口
	TextField tf = new TextField(10); // 创建文本框
	TextArea ta = new TextArea(); // 创建文本区
	Socket client; // 定义socket对象
	InputStream in;
	OutputStream out;

	public ChatClient() {
		super("客户机");
		this.setSize(250, 250); // 设置窗口大小
		pn.add(lb); // 添加标签到面板
		pn.add(tf); // 添加文本框到面板
		tf.addActionListener(this); // 对文本框注册事件监听器
		this.add("North", pn); // 设置面板的位置
		this.add("Center", ta); // 设置文本区的位置
		// 接收窗口事件的监听器,并以窗口事件的适配器作为参数
		this.addWindowListener(new WindowAdapter() { // 关闭窗口
					public void windowClosing(WindowEvent e) {
						System.exit(0);
					}
				});
		setVisible(true); // 显示窗口
		try {
			client = new Socket(InetAddress.getLocalHost(), 5007);
			// 文本区显示连接的服务器主机名
			ta.append("已连接的服务器:" + client.getInetAddress().getHostName()
					+ "\n\n");
			in = client.getInputStream();
			out = client.getOutputStream();
		} catch (IOException e) {
			System.out.println("error:" + e);
		}
		// 循环接收服务器端发送的信息并显示在文本区
		while (true) {
			try {
				byte[] buf = new byte[256];
				in.read(buf);
				String str = new String(buf); // 将字节数组转换为字符串
				ta.append("服务器说:" + str); // 文本区显示服务器发来的内容
				ta.append("\n");
			} catch (IOException e) {
				System.out.println("error:" + e);
			}
		}
	}

	public void actionPerformed(ActionEvent e) { // 实现接口ActionListener的方法
		try {
			String str = tf.getText(); // 得到文本框输入的内容
			byte[] buf = str.getBytes(); // 将字符串转换为字节数组
			tf.setText(null); // 设置文本框显示为空
			out.write(buf); // 向服务器端发送内容
			ta.append("我说:" + str); // 在文本区中显示发送内容
			ta.append("\n");
		} catch (IOException e1) {
			System.out.println("error:" + e1);
		}
	}

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

// ChatServer.java
package Chapter10;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;

public class ChatServer extends Frame implements ActionListener {
	Label lb = new Label("聊天");
	Panel pn = new Panel();
	TextField tf = new TextField(10);
	TextArea ta = new TextArea();
	ServerSocket server; // 声明ServerSocket对象
	Socket client; // 声明Socket对象
	InputStream in;
	OutputStream out;

	public ChatServer() {
		super("服务器");
		this.setSize(250, 250);
		pn.add(lb);
		pn.add(tf);
		tf.addActionListener(this); // 对文本框注册事件监听器
		this.add("North", pn);
		this.add("Center", ta);
		this.addWindowListener(new WindowAdapter() { // 接收窗口事件的监听器
					public void windowClosing(WindowEvent e) {
						System.exit(0);
					}
				});
		show();
		try {
			// ServerSocket对象
			server = new ServerSocket(5007);
			client = server.accept(); // 监听客户端发来的请求
			// 文本区显示客户端的IP地址
			ta.append("已连接的客户机:" + client.getInetAddress().getHostAddress()
					+ "\n\n");
			in = client.getInputStream();
			out = client.getOutputStream();
		} catch (IOException e) {
			System.out.println("error:" + e);
		}
		// 循环接收客户端发送的信息并显示在文本区
		while (true) {
			try {
				byte[] buf = new byte[256];
				in.read(buf);
				String str = new String(buf);
				ta.append("客户机说:" + str);
				ta.append("\n");
			} catch (IOException e) {
				System.out.println("error:" + e);
			}
		}
	}

	public void actionPerformed(ActionEvent e) {
		try {
			String str = tf.getText();
			byte[] buf = str.getBytes();
			tf.setText(null);
			out.write(buf); // 向客户端发送信息
			ta.append("我说:" + str);
			ta.append("\n");
		} catch (IOException e1) {
			System.out.println("error:" + e1);
		}
	}

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

本章小结

   本章主要介绍了Java语言网络编程的相关内容,主要包括网络的基本知识,URL编程以及Socket编程。
   学习本章时,大家应着重掌握如下一些内容:
 了解网络中的一些基本概念;
 了解使用URL类访问网络资源的方法;
 掌握流式Socket编程的方法;
 掌握数据报Socket编程的方法;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值