关闭

黑马程序员_java 网络编程

737人阅读 评论(0) 收藏 举报
------- android培训java培训、期待与您交流! ----------

一、网络编程概述

1、网络模型(两种)

A、OSI(Open System Interconnection 开放系统互联)参考模型

B、TCP/IP 参考模型


2、网络通讯要素

A、IP地址

B、端口号

C、传输协议


3、网络参考模型

七层简述:

1)、物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由10转化为电流强弱来进行传输,到达目的地后再转化为10,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。 

2)、数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。 

3)、网络层:主要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。 

4)、传输层:定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层数据叫做段。 

5)、会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)。

6)、表示层:主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够能识别的东西(如图片、声音等)。

7)、应用层:主要是一些终端的应用,比如说FTP(各种文件下载),WEBIE浏览),QQ之类的(可以把它理解成我们在电脑屏幕上可以看到的东西.就是终端应用)。


4、网络通讯要素

1)IP地址:InetAddress  网络中设备的标示,不易记忆可用主机名。本机会还地址:127.0.0.1 主机名:localhost

2)、端口号:用于标示进程的逻辑地址,也是不同进程的标示。有效端口:0~35535,其中0~1024系统使用或保留端口。

3)、传输协议:通讯的规则。常见协议有:TCPUDP

示例:InetAddress:该类表示互联网协议 (IP) 地址。

InetAddress getLocalHost():返回本地主机名称及IP地址;

InterAddress getByAddress(String host, byte[] addr):根据提供的主机名称及IP地址来创建InterAddress;

InterAddress getByName(String host):通过提供的主机名称来获取器IP地址;

String getHostAddress():返回主机的IP地址;

String getHostName():返回主机的名称。

import java.net.*;
class IPDemo 
{
	public static void main(String[] args) throws UnknownHostException
	{
		//InetAddress ia = InetAddress.getLocalHost();//获取本地主机的名称及IP地址;
		InetAddress ia = InetAddress.getByName("www.baidu.com"); //获取指定主机的IP地址;
		System.out.println(ia.toString());
		System.out.println("Address: "+ia.getHostAddress()); //获取IP地址(字符串形式);
		System.out.println("Name: "+ia.getHostName()); //获取主机名称;
	}
}


二、网络编程

1、Socket

1)、Socket是为网络服务提供的一种机制;

2)、通信的两端都有Socket;

3)、网络通信就是Socket间的通信;

4)、数据在两个Socket间通过IO传输;


2、UDP传输

下面通过UDP的发送与接收演示示例来了解UDP的传输。

DatagramSocket:此类表示用来发送和接收数据报包的套接字。

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

1)、发送端

需求:通过UDP传输方式,将一段文字数据发送出去。

思路:A、建立UDPSocket服务;

B、提供数据,并将数据封装到数据报包中;

C、通过Socket服务的发送功能,把数据包发送出去;

D、关闭资源。

import java.net.*;
class UDPSend 
{
	public static void main(String[] args) throws Exception
	{
		//1.通过DatagramSocket对象创建UDP服务;
		DatagramSocket ds = new DatagramSocket();//10000是给发送端指定的端口,不添加则系统自动分配;
		//2.确定数据,并通过DatagramPacket将其封装成数据报包;
		byte[] buf = "UDP test!!!".getBytes();
		DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.104"),4000);
		//3.通过Socket服务将数据报包发送出去,使用send方法;
		ds.send(dp);
		//4.关闭资源;
		ds.close();
	}
}

2)、接收端

需求:定义一个应用程序,用于接收UDP协议传输的数据并做处理。

思路:A、定义一个UDPSocket服务;通常会监听一个端口,其实就是给这个这个接收网络应用程序定义一个数字标识,方便于明确哪些数据发送过来时该应用程序可以处理;

B、定义一个数据报包用于存储接收到的字节数据,因为数据包对象中有更多功能可以提取字节数据中的不同数据信息;

C、通过Socket服务中的receive方法将接收到的数据存储到定义好的数据报包中;

D、通过数据报包对象的特有功能,将这些不同的数据取出,打印在控制台上;

E、关闭资源。

class  UDPReceive
{
	public static void main(String[] args) throws Exception
	{
		//1.通过DatagramSocket对象创建UDP服务,建立端点;
		DatagramSocket ds = new DatagramSocket(4000);

		while (true)//接收方不关闭,一直处于接受状态;
		{
			//2.定义数据报包用于存储数据;
			byte[] buf = new byte[1024];
			DatagramPacket dp = new DatagramPacket(buf,buf.length);
			//3.通过Socket服务中的receive方法将接收到的数据存储到数据报包中;

			ds.receive(dp);//阻塞式方法;
			//4.通过数据报包的方法获取其中的数据;
			String ip = dp.getAddress().getHostAddress();
			String data = new String(dp.getData(),0,dp.getLength());
			int port = dp.getPort();
			System.out.println(ip+":::"+data+":::"+port);
			//5.关闭资源;
			//ds.close();
		}		
	}
}


3)、通过编写一个聊天程序来进一步了解UDP传输协议的编程过程。

思考:该聊天软件具备两个部分:接收数据部分和发送数据部分。而且这两个部分需要同时执行,所以就需要使用到多线程技术,其中一个线程发送数据,一个线程接收数据。由于接收和发送的动作不一致,所以要定义两个run方法,而且这两个方法要封装到不同类中。

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

class UDPSend implements Runnable
{
	private DatagramSocket ds;
	public UDPSend(DatagramSocket ds)
	{
		this.ds = ds;
	}
	public void run()
	{
		try
		{
			BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
			String line = null;
			while((line=bufr.readLine()) != null)
			{
				if("886".equals(line))
					break;
				 byte[] buf = line.getBytes();
				 DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.141"),20000);
				 ds.send(dp);
			}
		}
		catch (Exception e)
		{
			throw new RuntimeException("发送失败!");
		}
	}
}

class UDPReceive implements Runnable
{
	private DatagramSocket ds;
	public UDPReceive(DatagramSocket ds)
	{
		this.ds = ds;
	}
	public void run()
	{
		try
		{
			while(true)
			{
				byte[] buf = new byte[1024];
				DatagramPacket dp = new DatagramPacket(buf,buf.length);
				ds.receive(dp);
				
				String ip = dp.getAddress().getHostName();
				String data = new String(dp.getData(),0,dp.getLength());
				System.out.println(ip+":::"+data);
			}			
		}
		catch (Exception e)
		{
			throw new RuntimeException("接收失败!");
		}
	}
}

class ChatDemo 
{
	public static void main(String[] args) throws Exception
	{
		DatagramSocket sendSocket = new DatagramSocket();
		DatagramSocket receiveSocket = new DatagramSocket(10000);

		new Thread(new UDPSend(sendSocket)).start();  //创建多线程
		new Thread(new UDPReceive(receiveSocket)).start();
	}	
}


3、TCP传输

TCP传输分为客户端和服务端,客户端对应的对象时Socket,服务端对应的对象时ServerSocket。

下面还是通过一个TCP演示示例来了解TCP的传输过程。

1)、客户端

说明:客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常,连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可,与服务端通讯结束后,关闭Socket
通过查阅Socket对象的API文档,我们知道了在建立对象时,就可以连接指定的主机。由于TCP传输是面向连接的,所以在建立Socket服务时,就要有服务端存在。连接成功形成通路后,数据就可以在该通道上传输。

思路:A、创建Socket服务,并指定要连接的地址和端口;

B、获取Socket中的输出流;

C、关闭资源。

import java.net.*;
import java.io.*;
class  TCPClient
{
	public static void main(String[] args) throws Exception
	{
		//1.创建客户端的Socket服务,指定目的主机和端口;
		Socket s = new Socket("192.168.1.104",10001);
		//2.为了发送数据,获取Socket中的输出流;
		OutputStream out = s.getOutputStream();
		out.write("TCP test!!!".getBytes());
		//3.关闭资源;
		s.close();
	}
}

2)、服务端

思路:A、通过ServerSocket对象建立服务器的Socket服务,并监听一个端口;

B、获取连接过来的客户端对象,通过ServerSocket对象中的accept方法。如果没有连接就会等待,所以该方法是阻塞式的。

C、客户端如果发过来数据,那么服务端就要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发货来的数据,并打印在控制台上;

D、关闭资源(可选操作)。

class TCPServer
{
	public static void main(String[] args) throws Exception
	{
		//1.通过SocketServer对象建立服务端Socket服务,并监听一个端口;
		ServerSocket ss = new ServerSocket(10001);
		//2.通过accept方法获取连接过来的客户端对象;
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"...is Connected.");

		//3.获取客户端发送过来的数据,要使用客户端对象的读取流来读取数据;
		InputStream in = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		System.out.println(new String(buf,0,len));
		//4.关闭客户端;
		s.close();
		ss.close();
	}
}

练习:需求:建立一个文本转换服务器。客户端给服务端发送一个文本,服务端将会把文本转换成大写返回给客户端,而且客户端可以不断地进行文本转换,当客户端输入over时,转换结束。

分析:

客户端:由于要操作的是设备上的数据,所以考虑使用io技术,并按照io的操作规律来思考。

源:键盘录入;

目的:网络设备,网络输出流。

由于操作的都是文本数据,可以选择字符流。加入缓冲,提高效率。

步骤:1)、建立Socket服务;

2)、获取键盘录入;

3)、将数据发送给服务端;

4)、取出服务端返回的大写数据;

5)、关闭资源。


服务端:

源:Socket读取流;

目的:Socket输出流。

该例中容易出现的问题:客户端和服务端都会出现莫名的等待。

原因:因为客户端和服务端都有阻塞式的方法,这些方法如果没有读取到结束标记,就会一直等待。也就导致两端一直的等待。

import java.io.*;
import java.net.*;
class  TransClient
{
	public static void main(String[] args) throws Exception
	{
		Socket s = new Socket("192.168.1.104",10003);
		//定义读取键盘数据的流对象;
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		//定义目的,将数据写入到Socket流,发送给服务端;
		BufferedWriter bufout = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
		//定义一个Socket读取流读取服务端返回的大写数据;
		BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream()));

		String line = null;
		while((line=bufr.readLine()) != null)
		{
			if("over".equals(line))
				break;
			bufout.write(line);
			bufout.newLine();
			bufout.flush();
			String str = bufin.readLine();
			System.out.println("Server:"+str);
		}
		bufr.close();
		s.close();
	}
}

class TransServer
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(10003);
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"is connected.");

		//读取Socket读取流中的数据;
		BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream()));
		//目的,Socket输出流,将大写的数据写入到Socket输出流,并发送给客户端;
		BufferedWriter bufout = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

		String line = null;
		while((line=bufin.readLine()) != null)
		{
			System.out.println(line);
			bufout.write(line.toUpperCase());
			bufout.newLine();
			bufout.flush();
		}
		s.close();
		ss.close();
	}
}

4、TCP上传文件

1)、该示例是使用字符流通过网络来复制上传文本文件。

import java.io.*;
import java.net.*;
class TextClient 
{
	public static void main(String[] args) throws Exception
	{
		Socket s = new Socket("192.168.1.104",10004);
		BufferedReader bufr = new BufferedReader(new FileReader("TCPDemo.java"));
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);

		String line = null;
		while((line=bufr.readLine()) != null)
		{
			out.println(line);
		}

		s.shutdownOutput();//关闭客户端的输出流,相当于给流中加入一个结束标记-1;
		
		BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String str = bufin.readLine();
		System.out.println(str);
		bufr.close();
		s.close();
	}
}

class TextServer
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(10004);
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"...is Connected.");

		BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream()));
		PrintWriter out = new PrintWriter(new FileOutputStream("D:\\Server.txt"),true);

		String line = null;
		while((line=bufin.readLine()) != null)
		{
			out.println(line);
		}

		PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
		pw.println("上传成功!!!");

		out.close();
		s.close();
		ss.close();
	}
}

2)、通过网络来上传复制图片文件。该示例通过字节流来上传文件。

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

class PicClient
{
	public static void main(String[] args) throws Exception
	{
		//建立Socket服务,向ip地址为“192.168.1.102”主机的端口20000发送文件;
		Socket s = new Socket("192.168.1.102",20001);
		//创建一个文件字节流读取本地主机中的图片文件;
		FileInputStream fis = new FileInputStream("D:\\1.jpg");
		//创建一个输出流来传输要发送的数据;
		OutputStream out = s.getOutputStream();

		byte[] buf = new byte[1024];
		int len = 0;
		while((len=fis.read(buf)) != -1)
		{
			out.write(buf,0,len);
		}
		s.shutdownOutput();//告诉服务端数据已写完;
		
		//创建一个输入流来接受服务端发送过来的数据;
		InputStream in = s.getInputStream();
		byte[] bufin = new byte[1024];
		int num = in.read(bufin);
		System.out.println(new String(bufin,0,num));

		fis.close();
		s.close();
	}
}

class PicServer
{
	public static void main(String[] args) throws Exception
	{
		//创建Socket服务,并监听20001端口;
		ServerSocket ss = new ServerSocket(20001);
		while(true)
		{
			//使用accept方法来监听客户端发送过来的请求连接;
			Socket s = ss.accept();
			//创建一个文件输出流来存储客户端上传过来的数据;
			FileOutputStream fos = new FileOutputStream("D:\\Server1.jpg");
			//创建输入流流来接受客户端传送过来的数据;
			InputStream in = s.getInputStream();

			byte[] buf = new byte[1024];
			int len = 0;
			while((len=in.read(buf)) != -1)
			{
				//将客户端发送的数据写入到文件输出流中;
				fos.write(buf,0,len);
			}
			
			OutputStream out = s.getOutputStream();
			out.write("上传成功!".getBytes());//将反馈的数据写入到输出流中发送到客户端;

			fos.close();
			s.close();
			//ss.close();
		}
	}
}

3)、客户端并发上传图片(使用多线程技术)

以上服务端有个局限性。当A客户端连接上以后,被服务端获取到,服务端就执行具体流程。这是当B客户端要来连接时,就只有等待,因为服务端还没有处理完A客户端的请求,还没有循环回来执行accept方法,所以暂时获取不到B客户端对象。为了可以让多个客户端同时并发访问服务端,服务端最好将每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端请求了。

该如何定义线程呢? 只要明确了每一个客户端要在服务端执行的代码即可。将改代码存入run方法中。

演示代码如下:

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

class PicThread implements Runnable
{
	private Socket s;
	PicThread(Socket s)
	{
		this.s = s;
	}
	public void run()
	{
		int count = 1;
		String ip = s.getInetAddress().getHostAddress();
		try
		{
			
			System.out.println(ip+"is Connected.");

			File file = new File("D:\\"+ip+"("+count+")"+".jpg");
			while(file.exists())
				file = new File("D:\\"+ip+"("+(count++)+")"+".jpg");

			FileOutputStream fos = new FileOutputStream(file);
			InputStream in = s.getInputStream();

			byte[] buf = new byte[1024];
			int len = 0;
			while((len=in.read(buf)) != -1)
			{
				fos.write(buf,0,len);
			}

			OutputStream out = s.getOutputStream();
			out.write("上传成功".getBytes());
			fos.close();
			s.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException(ip+"上传失败!");
		}
		
	}
}
class PicClient 
{
	public static void main(String[] args) throws Exception
	{
		if(args.length != 1)
		{
			System.out.println("请选择一个jpg格式的文件!");
			return ;
		}
		File file = new File(args[0]);
		if(!(file.exists() && file.isFile()))
		{
			System.out.println("该文件有问题!不存在或者不是文件!");
			return ;
		}
		if(!file.getName().endsWith(".jpg"))
		{
			System.out.println("该文件格式错误,请重新选择jpg格式文件!");
			return ;
		}
		if(file.length()>1024*1024*5)
		{
			System.out.println("该文件过大,请重新选择!");
		}

		Socket s = new Socket("192.168.1.102",20000);
		FileInputStream fis = new FileInputStream(file);
		OutputStream out = s.getOutputStream();

		byte[] buf = new byte[1024];
		int len = 0;
		while((len=fis.read(buf)) != -1)
		{
			out.write(buf,0,len);
		}
		//告诉服务端数据已写完;
		s.shutdownOutput();

		InputStream in = s.getInputStream();
		byte[] bufin = new byte[1024];
		int num = in.read(bufin);
		System.out.println(new String(bufin,0,num));
		fis.close();
		s.close();
	}
}

class PicServer 
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(20000);
		while(true)
		{
			Socket s = ss.accept();
			new Thread(new PicThread(s)).start();
			
			//ss.close();
		}
	}
}

4)、练习:客户端并发登录

需求:客户端通过键盘录入用户名,服务端对这个用户名进行校验。

如果该用户存在在服务器上显示xxx,已登录。并在客户端显示xxx,欢迎观临。

如果该用户不存在则在服务器端显示xxx,尝试登录。并在客户端显示xxx,该用户不存在。

最多只能登录三次。

import java.io.*;
import java.net.*;
class LoginClient 
{
	public static void main(String[] args) throws Exception
	{
		Socket s = new Socket("192.168.1.102",20002);
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream()));

		for(int x=0; x<3; x++)
		{
			String line = bufr.readLine();
			if(line == null)
				break;
			out.println(line);

			String info = bufin.readLine();
			System.out.println("info: "+info);
			if(info.contains("欢迎"))
				break;
		}
		bufr.close();
		s.close();
	}
}

class UserThread implements Runnable
{
	private Socket s;
	UserThread(Socket s)
	{
		this.s = s;
	}
	public void run()
	{
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+" is Connected.");
		try
		{
			for(int x=0; x<3; x++)
			{
				BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream()));
				String name = bufin.readLine();
				if(name == null)
					break;
				BufferedReader bufr = new BufferedReader(new FileReader("D:\\User.txt"));
				String line = null;
				PrintWriter out = new PrintWriter(s.getOutputStream(),true);
				boolean flag = false;
				while ((line=bufr.readLine()) != null)
				{
					if(line.equals(name))
					{
						flag = true;
						break;
					}
				}
				if(flag)
				{
					System.out.println(name+",已登录!");
					out.println(name+",欢迎光临!");
					break;
				}
				else
				{
					System.out.println(name+",尝试登录!");
					out.println(name+",该用户名不存在!");
				}
			}
			s.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException(ip+"登录失败!");
		}
	}
}
class LoginServer
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(20002);
		while(true)
		{
			Socket s = ss.accept();
			new Thread(new UserThread(s)).start();
		}
	}
}


5)、演示服务端和客户端

A、客户端:浏览器

       服务端:自定义

       操作步骤:编译运行在自己的java文件让服务端开启,再PC浏览器中的地址输入栏输入:ip+端口号(http:\\192.168.1.254:22000)。进入后浏览器就会出现"客户端,您好!!!"字符。

import java.net.*;
import java.io.*;
class ServerDemo 
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(21000);
		Socket s = ss.accept();
		System.out.println(s.getInetAddress().getHostAddress()+" is Connected!");

		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		out.println("<font color='red' size='8'>客户端,您好啊!!!</font>");
		s.close();
		ss.close();
	}
}

B、客户端:浏览器

      服务端:tomcat服务器

      tomcat服务器的好处:能够读取自定义的资源;

      如何通过PC上的浏览器来访问tomcat服务器呢?

      首先我们建立一个html文件,其文件内容如下。并在tomcat的安装目录新建一个文件夹myweb(例如:D:\软件\apache-tomcat-8.0.0-RC5\webapps\myweb)。然后启动tomcat服务器,在PC的浏览器地址栏中输入ip+端口号(如:192.168.0.103:8080),就可以进入tomcat服务器了。

html代码如下:

<html>
	<body>
		<h1>我的主页</h1>
		<font size=6 color=green>欢迎观临</font>
			<div>
			<font size=5 color=orange>
			床前明月光,</br>
			疑是地上霜。</br>
			举头望明月,</br>
			低头思故乡。</br>
			</font>
			</div>
	</body>
</html>

C、客户端:浏览器

       服务端:自定义

import java.net.*;
import java.io.*;
class ServerDemo1
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(22000);
		Socket s = ss.accept();
		System.out.println(s.getInetAddress().getHostAddress()+" is Connected!");

		InputStream in = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		System.out.println(new String(buf,0,len));

		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		out.println("<font color='red' size='8'>客户端,您好啊!!!</font>");
		s.close();
		ss.close();
	}
}


/**
Accept: text/html, application/xhtml+xml, 
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)
Accept-Encoding: gzip, deflate
Host: 192.168.0.103:21000
DNT: 1
Connection: Keep-Alive
*/
通过以上代码我们知道了浏览器每次想服务器发送请求时都会发送如上代码中注释部分的内容,那么我们也可以根据浏览器的模式,建立我们自己的浏览器。

操作步骤:开启tomcat服务器,编译并运行一下代码,就会返回客户端所请求的内容,即以上html代码中的内容。

代码如下:

import java.io.*;
import java.net.*;
class MyIE 
{
	public static void main(String[] args) throws Exception
	{
		Socket s = new Socket("192.168.0.103",8080);
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		out.println("GET /myweb/Demo.html HTTP/1.1");
		out.println("Accept: */*");
		out.println("Accept-Language: zh-CN");
		out.println("Host: 192.168.0.103:22000");
		out.println("Connection: closed");
		out.println();
		out.println();
		
		BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line = null;
		while((line=bufr.readLine()) != null)
		{
			System.out.println(line);//读取tomcat服务端发送回来的客户端请求的内容;
		}
		s.close();
	}
}

D、客户端:自定义窗口浏览器

       服务端:tomcat服务器

       我们可以直接在窗体中的文本框中输入地址路径,点击“转到”,便可以查询到我们需要的内容。

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
class  MyIEByGUI
{
	private Frame f;
	private TextField tf;
	private Button b;
	private TextArea ta;
	private Dialog d;
	private Label lab;
	private Button okBut;

	MyIEByGUI()
	{
		init();
	}

	public void init()
	{
		f = new Frame("Java test");	
		b = new Button("转到");
		tf = new TextField(60);		
		ta = new TextArea(25,80);
		
		f.add(tf);
		f.add(b);		
		f.add(ta);
		f.setBounds(300,100,600,500);
		f.setLayout(new FlowLayout());
		myEvent();
		f.setVisible(true);
	}

	private void myEvent()
	{
		f.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		});

		b.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				try
				{
					showDir();
				}
				catch (Exception ex)
				{
				}				
			}
		});

		tf.addKeyListener(new KeyAdapter()
		{								  
			public void keyPressed(KeyEvent e)
			{
				try
				{
					if(e.getKeyCode() == KeyEvent.VK_ENTER)
					showDir();
				}
				catch (Exception ex)
				{
				}				
			}
		});
		
	}
	private void showDir() throws Exception
	{
		ta.setText("");
		String url = tf.getText();   //"http://27.18.160.44:8080/myweb/Demo.html";
		int index1 = url.indexOf("//")+2;
		int index2 = url.indexOf("/",index1);

		String str = url.substring(index1,index2);
		String[] arr = str.split(":");
		String host = arr[0];
		int port = Integer.parseInt(arr[1]);
		String path = url.substring(index2);

		Socket s = new Socket(host,port);
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		out.println("GET "+path+" HTTP/1.1");
		out.println("Accept: */*");
		out.println("Accept-Language: zh-CN");
		out.println("Host: 27.18.160.44:8080");
		out.println("Connection: closed");
		out.println();
		out.println();
		
		BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line = null;
		while((line=bufr.readLine()) != null)
		{
			ta.append(line+"\r\n");
		}
		s.close();
	}

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


5、URL类

类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。

1)、URL类中的部分方法:

String getFile(): 获取此 URL 的文件名。 
String getHost():获取此 URL 的主机名(如果适用)。 
String getPath(): 获取此 URL 的路径部分。 
int getPort(): 获取此 URL 的端口号。 
String getProtocol(): 获取此 URL 的协议名称。 
String getQuery(): 获取此 URL 的查询部分。

演示示例:

import java.net.*;
class URLDemo 
{
	public static void main(String[] args) throws MalformedURLException
	{
		URL url = new URL("http://192.168.0.103:8080/myweb/Demo.html?name=java&age=20");

		System.out.println("getProtocol() :"+url.getProtocol());
		System.out.println("getHost(): "+url.getHost());
		System.out.println("getPort(): "+url.getPort());
		System.out.println("getPath(): "+url.getPath());
		System.out.println("getFile(): "+url.getFile());		
		System.out.println("getQuery() :"+url.getQuery());
	}
}
/*
打印结果:
getProtocol() :http
getHost(): 192.168.0.103
getPort(): 8080
getPath(): /myweb/Demo.html
getFile(): /myweb/Demo.html?name=java&age=20
getQuery() :name=java&age=20
*/

2)、URLConnection

抽象类 URLConnection 是所有类的超类,它代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源。
通常,创建一个到 URL 的连接需要几个步骤:
A、通过在 URL 上调用 openConnection 方法创建连接对象。 
B、处理设置参数和一般请求属性。 
C、使用 connect 方法建立到远程对象的实际连接。 
D、远程对象变为可用。远程对象的头字段和内容变为可访问。

InputStream getInputStream():返回从此打开的连接读取的输入流。 
OutputStream getOutputStream() :返回写入到此连接的输出流。

URLConnection openConnection:返回一个 URLConnection 对象,它表示到URL 所引用的远程对象的连接。

通过查询API文档,我们知道了URLConnection把Socket中的数据全都封装起来了。所以以上自定义的窗体浏览器可以可以简化为如下代码:

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
class  MyIEByGUI2
{
	private Frame f;
	private TextField tf;
	private Button b;
	private TextArea ta;
	private Dialog d;
	private Label lab;
	private Button okBut;

	MyIEByGUI2()
	{
		init();
	}

	public void init()
	{
		f = new Frame("Java test");	
		b = new Button("转到");
		tf = new TextField(60);		
		ta = new TextArea(25,80);
		
		f.add(tf);
		f.add(b);		
		f.add(ta);
		f.setBounds(300,100,600,500);
		f.setLayout(new FlowLayout());
		myEvent();
		f.setVisible(true);
	}

	private void myEvent()
	{
		f.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		});

		b.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				try
				{
					showDir();
				}
				catch (Exception ex)
				{
				}				
			}
		});

		tf.addKeyListener(new KeyAdapter()
		{								  
			public void keyPressed(KeyEvent e)
			{
				try
				{
					if(e.getKeyCode() == KeyEvent.VK_ENTER)
					showDir();
				}
				catch (Exception ex)
				{
				}				
			}
		});
		
	}
	private void showDir() throws Exception
	{
		ta.setText("");
		String urlPath = tf.getText();   //"http://27.18.160.44:8080/myweb/Demo.html";
		URL url = new URL(urlPath);
		URLConnection conn = url.openConnection();
		System.out.println(conn);

		InputStream in = conn.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		ta.append(new String(buf,0,len));
	}

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


3)、域名解析

当我们访问外部网站的时候使用到该网站的域名(例如:www.baidu.com),其实我们访问的时候还是使用的ip访问的它的。当我们输入网址的时候,浏览器会先把网址发送到域名解析服务器DNS上,DNS再将对应网址上的ip地址返回给浏览器,浏览器通过ip访问其网站。

在本地主机中localhost与127.0.0.1是对应的,都是本地主机的ip。但127.0.0.1和localhost的映射关系就在本地C:\Windows\System32\drivers\etc\hosts中,我们打开hosts文件可以看到localhost对应的ip就是127.0.0.1。  注意:当我们通过域名访问网站时,浏览器会先查找本地主机上有没有与之对应的ip地址,如果本地主机有,则不会访问DNS去寻找对应的ip地址,而是直接使用本地上对应的ip地址(使用本地映射访问速度相对较快)。

通过hosts文件的查看,我们也可以使用这种做法屏蔽一些骚扰网站。做法:我们可以将该网站的域名与本地主机的ip127.0.0.1形成映射关系,当浏览器使用该网站的域名时,就会直接导入本地ip,也就不会通过DNS查询ip去访问外部网站了。


------- android培训java培训、期待与您交流! ----------
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:10002次
    • 积分:297
    • 等级:
    • 排名:千里之外
    • 原创:19篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档