黑马程序员——Java基础知识——网络编程

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! 

一、概述

         首先通过下面的图例简单演示不同主机的程序之间的数据传输过程:


           在整个信息发送过程中,要找到对方的IP地址,将数据逐层封包,通过主机至网络层发送到对方主机上,然后向上逐层拆包,找到对方的指定应用程序,将数据发到该指定应用程序上。这样信息就在不同主机间发送成功了。整个过程就涉及到了我们要学习的网络通信的重要知识点:网络模型、网络通信要素(IP地址、端口、传输协议)。

          网络模型:有OSI参考模型和TCP/IP参考模型。二者的结构如下图:

 

       我们接下来学习的网络编程都处于传输层和网际层,每一个层都有对应的通信协议。应用层对应的是FTP和HTTP协议,传输层是TCP和UDP,网际层的协议为IP。从上面的QQ发送信息的过程中可以看到,用户在应用层传入数据,经过逐层封包,最后到达主机至网络层发送给另一台主机,然后再逐级向上拆包。

        网络通讯三要素:IP地址、端口、传输协议。

         (1)IP地址:它是网络中的设备标识,可以用主机名来表示,两者存在映射关系。本机的回环地址是:127.0.0.1 (主机名:localhost)。Java中把IP地址封装成了一个InetAddress类,存放在java.net包中。

       通过这个类的方法,可以实现对IP地址、主机名的获得等功能,常用功能如下:

       1.获得该类对象: InetAddress i=InetAddress.getByName(String host);获取指定主机的InetAddress对象,注意最好用IP地址获取,因为主机名需要解析。

                                    InetAddress i=InetAddress.getByName(String host);获取指定主机的所有InetAddress对象,返回一个数组

                                    InetAddress  i=InetAddress.getLocalHost();获取本机的InetAddress对象。

       2.获取IP地址和主机名

                                   i.getHostAddress();返回IP地址字符串(文本形式)。

                                   i.getHostName();返回IP地址对应的主机名。

       注意,通过上面的方法,传入一个指定的主机名,就可以获得它的IP地址对象,获取其具体的一个或多个地址。但如果IP地址和对应的主机名的映射关系在网络上不存在,就会解析失败。

       下面通过一段代码进行演示:

	public static void main(String[] args) throws UnknownHostException {
		//获取sohu的IP地址对象
		InetAddress[] i=InetAddress.getAllByName("www.sohu.com");
		//获取具体的IP地址
		for(InetAddress ia:i)
			System.out.println(ia.getHostAddress());
		//获取本机的IP对象,并获取具体IP地址和主机名
		InetAddress i1=InetAddress.getLocalHost();
		String name=i1.getHostName();
		String  ip=i1.getHostAddress();
		System.out.println(name+":"+ip);
	}
        (2)端口号:用于标示进程的逻辑地址,不同进程的标识。有效端口的设置范围为0~65535,注意系统会保留0~1024端口。所以设置时要避开。

        (3)传输协议: 就是通讯规则,主要是TCP和UDP协议。下面对这两种协议的特点和应用分别进行介绍:

        UDP协议:面向无连接,不建立连接,将数据及源和目的封装在数据包中来发送,每个数据包的大小限制住在64K以内,因无连接,速度快,但可能出现数据丢失,是不可靠协议。应用于聊天程序、桌面共享、视频会议等。

        TCP协议:面向连接,在建立连接后,形成传输数据的通道,可以进行大数据量的传输。因通过三次握手建立连接,速度稍慢,但能保证数据的安全性,是可靠的协议。应用于下载文件、文件传输等程序中。

         三次握手:第一次本机发送连接请求,第二次对方确认连接,第三次本方再次确认连接成功。

         网络通信的步骤:

         (1)找到IP地址。

         (2)数据要发送到对方的指定应用程序上,这时就要用数字标识该应用程序上,就是用到端口。如果没有定义端口,数据就无法发送到该应用程序上。

         (3)定义通信规则,国际组织定义了通用协议 TCP/IP。

、TCP和UDP传输的建立与应用

       1.Socket:端点,就相当于港口,是网络服务提供的一种机制,通信两端都要有Socket对象,才能建立通信服务。网络通信就是Socket之间的通信,数组在两个Socket之间通过IO传输。

       2.UDP传输

      DatagramSocket类,表示用来发送和接收数据报包的套接字,即Socket;DatagramPacket类,表示数据报包。下面就通过建立这个两个类的对象,调用它们的特有方法,实现数据的UDP传输。具体步骤如下:

       (1)发送端:

            1.通过创建DatagramSocket对象,建立Socket服务。可以指定本程序端口,也可不指定根据系统自动分配。

            2.确定数据,将要发送的数据封装到数据报包DatagramPacket对象中,数据报包对象中还要指定接收数据的主机和端口。

            3.通过Socket服务中的send(DatagramPacket  dp),将数据报包发送出去。

            4.关闭资源。

          如同下面这段代码:

class UDPSend {

	public static void main(String[] args) throws IOException {
	   //创建DatagramSocket对象,建立Socket服务,并指定本程序端口。
		DatagramSocket ds=new DatagramSocket(10000);
	   //将提供的数据封装成数据报包,并在数据报包中指定要发送到的主机和端口。
		byte[]by="大河向东流,天上的星星参北斗".getBytes();
		DatagramPacket dp=new DatagramPacket(by,by.length,InetAddress.getLocalHost(),10001);
	   //通过send方法,将数据报包发送出去
		 ds.send(dp);
	   //关闭资源
		 ds.close();
	}
	
}
        (2)接收端:
             1.创建DatagramSocket对象,建立Socket服务,定义时要监听一个端口,就是给应用程序定义数字标识,用来接收发送

给该应用程序的数据。  

               2. 定义数据报包,用来存储接收到的数据,

              3.通过Socket服务中的receive方法,将接收到的数据存入数据报包中。

              4.利用DatagramPacket中的特有功能提取数据信息。

              5.关闭资源。

         具体如下:

 class UDPRec {

	public static void main(String[] args) throws IOException {
		//1,创建udp socket,定义一个监听端口。
		DatagramSocket ds = new DatagramSocket(10001);
		//2,定义数据包。用于存储数据。
		byte[] buf = new byte[1024];
		DatagramPacket dp = new DatagramPacket(buf,buf.length);
        //3,通过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();
	}

}
        要注意的是,发送端和接收端是两个独立的程序,定义发送端时,要在数据报包中明确接收数据的主机和端口;定义接收端时要定义一个监听的端口。
       下面利用UDP传输协议的获取键盘录入,发送数据的程序。如下:

//发送端
class UDPSend {
        public static void main(String[] args) throws IOException  {
	   //创建DatagramSocket对象,建立Socket服务,并指定本程序端口。
		DatagramSocket ds=new DatagramSocket(10000);
	    //建立流对象,与键盘录入相关联,加入缓冲技术
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		String line=null;
		while((line=br.readLine())!=null){
	       if("over".equals(line))
	        	 break;
	       byte[]by=line.getBytes();  
	     //将获取的数据封装成数据报包,并在数据报包中指定要发送到的主机和端口。
	       DatagramPacket dp=new DatagramPacket(by,by.length,InetAddress.getLocalHost(),10001);
	      //通过send方法,将数据报包发送出去
		   ds.send(dp);
		}
	   //关闭资源
		 ds.close();
	}
}
//接收端
class UDPRec {
       public static void main(String[] args) throws IOException {
		//1,创建udp socket,定义一个监听端口。
		DatagramSocket ds = new DatagramSocket(10001);
		//2,定义数据包。用于存储数据。
		while(true){
		   byte[] buf = new byte[1024];
		  DatagramPacket dp = new DatagramPacket(buf,buf.length);
          //3,通过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);
		}
	}

}
       定义一个聊天程序,实现信息发送和收到的同时运行,这时就需要用到多线程来实现。如下:

class UDPTest {

 	public static void main(String[] args) throws IOException {
        //建立DatagramSocket对象
 		DatagramSocket ds1=new DatagramSocket();
 		DatagramSocket ds2=new DatagramSocket(10002);
 		//启动程序
 		new Thread(new SendDemo(ds1)).start();
 		new Thread(new RecDemo(ds2)).start();
	}

}
//定义一个类,表示发送,实现Runnable接口
class  SendDemo implements Runnable{
    //定义DatagramSocket的引用
	private DatagramSocket ds;
	SendDemo(DatagramSocket ds){
	   this.ds=ds;
	}
	//复写run方法,定义要运行的代码
	public void run(){
		BufferedReader br=null;
		try {
			br=new BufferedReader(new InputStreamReader(System.in));
			String line=null;
			//获取键盘录入,封装成数据报包,并发送出去
			while((line=br.readLine())!=null){
				if("88".equals(line))
					break;
				byte[]by=line.getBytes();
				DatagramPacket dp=new DatagramPacket(by,by.length,InetAddress.getLocalHost(),10002);
				ds.send(dp);
			}
			//关闭资源
			ds.close();
		} catch (Exception e) {
			throw new RuntimeException("获取键盘录入失败");
		}
	}
}
//定义一个类表示接收,实现Runnable接口
class RecDemo implements Runnable{
	//定义DatagramSocket的引用
	private DatagramSocket ds;
	RecDemo(DatagramSocket ds){
	   this.ds=ds;
	}
	//复写run方法,定义要运行的代码
	public void run(){
		while(true){
			  byte[] buf = new byte[1024];
			  DatagramPacket dp = new DatagramPacket(buf,buf.length);
	          //通过receive方法将收到数据存入数据包中。
			  try {
				ds.receive(dp);
			} catch (IOException e) {
				throw new RuntimeException("数据接收失败");
			}
			 //通过数据包的方法获取其中的数据。
			  String ip=dp.getAddress().getHostAddress();
			  String data=new String(dp.getData(),0,dp.getLength());
			  if("88".equals(data))
				   break;
			  int port=dp.getPort();
			  System.out.println(ip+":"+data);

          }
       }
}

  3.TCP传输:

        使用TCP传输,要建立客户端和服务器端,分别用Socket和ServerSocket的对象表示。建立连接后,通过Socket中的IO流进行数据传输。客户端与服务器端是两个独立的应用程序。Socket对象建立时就可以连接指定的主机,因为TCP是面向连接的,所以在建立Socket服务时,就要有服务器端存在,连接成功,形成通路后,就可以在该通道进行数据传输了。注意,空参数Socket对象可以通过connect方法连接服务器端。设置基本的TCP传输步骤如下:

         (1)服务器端:

                 1.创建SeverSocket对象,建立Socket服务,监听一个端口。

                 2.通过accept方法,获取链接过来的客户端对象。这个方法是阻塞式的,没有连接就会等。

                 3.客户端如果过来数据,那么服务端就使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据。

                 4.关闭服务器(可选操作)。

               如下:

class TCPServer {

	public static void main(String[] args) throws IOException {
		 //创建ServerSocket对象,建立Socket服务,指定监听端口
		  ServerSocket  ss=new ServerSocket(10004);
		 while(true){
		  //获取连接的客户端对象。
		  Socket s=ss.accept();
		 //通过获取客户端对象的读取流来读取客户端发送来的数据。
		  InputStream is=s.getInputStream();
		  byte[]by=new byte[1024];
		  int len=is.read(by);
		  System.out.println(new String(by,0,len));
		 }
	}

}
    (2)客户端:

          1.建立Socket服务,指定要连接的主机和端口。

          2.获取Socket服务中的输出流,将数据写入到流中,通过网络发送个服务器端。

          3.如果服务器端有反馈信息,获取Socket流中的输入流,获取服务器端反馈的数据信息。

          4.关闭资源。

         如下:

class TCPClient {

	public static void main(String[] args) throws Exception {
		 //建立Socket服务,指定要连接的主机和端口
		Socket s=new Socket(InetAddress.getLocalHost(),10004);
         //获取输出流,写入数据。
		OutputStream os=s.getOutputStream();
		os.write("大河向东流,天上的星星参北斗".getBytes());
		//关闭资源
		s.close();
	}

}
   当客户端进行数据传输给服务器端时,数据传输成功后,服务器端一般会有信息反馈给客户端。程序如下:

//定义服务端
public class TCPServer {

	public static void main(String[] args) throws Exception {
		 //创建ServerSocket对象,建立Socket服务,指定监听端口
		  ServerSocket  ss=new ServerSocket(10004);
		 while(true){
		  //获取连接的客户端对象。
		  Socket s=ss.accept();
		 //通过获取客户端对象的读取流来读取客户端发送来的数据。
		  InputStream is=s.getInputStream();
		  byte[]by=new byte[1024];
		  int len=is.read(by);
		  System.out.println(new String(by,0,len));
		 //通过获取客户端对象的输出流给客户端发送反馈信息
		    OutputStream out = s.getOutputStream();
	        
			out.write("数据已接收".getBytes());
            s.close();
	     }
	}
}
//定义客户端
class TCPClient {

	public static void main(String[] args) throws Exception {
		 //建立Socket服务,指定要连接的主机和端口
		Socket s=new Socket(InetAddress.getLocalHost(),10004);
         //获取输出流,写入数据。
		OutputStream os=s.getOutputStream();
		os.write("大河向东流,天上的星星参北斗".getBytes());
		//获取输入流,读取服务器端的反馈信息
		InputStream in = s.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        System.out.println(new String(buf,0,len));
		
    }

}

       下面是一个TCP传输的练习:

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

分析:
客户端:
既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考。
源:键盘录入。
目的:网络设备,网络输出流。
而且操作的是文本数据。可以选择字符流。

步骤
1,建立服务。
2,获取键盘录入。
3,将数据发给服务端。
4,服务端返回大写数据。
5,关闭资源。

都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。


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

class  TransClient
{
	public static void main(String[] args) throws Exception
	{
		Socket s = new Socket(InetAddress.getLocalHost(),10005);


		//定义读取键盘数据的流对象。
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));


		//定义目的,将数据写入到socket输出流。发给服务端。
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);



		//定义一个socket读取流,读取服务端返回的大写信息。
		BufferedReader bufIn = 
			new BufferedReader(new InputStreamReader(s.getInputStream()));

		String line = null;
		
		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			
			out.println(line);

			String str =bufIn.readLine();
			System.out.println(str);
			
		}
                
		//关闭资源
		bufr.close();
		s.close();


	}
}
/*

服务端:
源:socket读取流。
目的:socket输出流。
都是文本,装饰。



*/

class  TransServer
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(10005);

		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"....connected");

		//读取socket读取流中的数据。
		BufferedReader bufIn =
			new BufferedReader(new InputStreamReader(s.getInputStream()));

		//目的。socket输出流。将大写数据写入到socket输出流,并发送给客户端。
	
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);

		String line = null;
		while((line=bufIn.readLine())!=null)
		{

		    System.out.println(line);
                    out.println(line.toUpperCase());
		}

		s.close();
		ss.close();

	}
}
      在这个程序中加入缓冲技术,提高了程序的运行效率。要注意的是read方法是阻塞式方法,当没有读到数据或没有读到结束标记时两边read方法都会等待。

       通过TCP传输,可以实现不同主机之间的文件拷贝,就是把客户端的文件上传到服务端上,一般上传成功后,服务器端会提示上传成功,如下面的这个程序:

/**
 客户端向服务器上传一个文件。上传完毕后,服务器向客户端反馈上传成功。
 */
//定义客户端
class  TextClient
{
	public static void main(String[] args) throws Exception
	{
		Socket s = new Socket(InetAddress.getLocalHost(),10006);
        //建立读取流,和要上传的文件相关连
		BufferedReader bufr = 
			new BufferedReader(new FileReader("F:\\123.txt"));
        //获取输出流。
        PrintWriter out = new PrintWriter(s.getOutputStream(),true);
        //实现上传     
        String line = null;
		while((line=bufr.readLine())!=null)
		{
			out.println(line);
		}
		//关闭客户端的输出流。相当于给流中加入一个结束标记-1.
		s.shutdownOutput();
        //获取读取流,为了提高效率,加入转换流和缓冲技术
		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(10006);

		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"....connected");
        //获取连接对象的输入流,加入转换流和缓冲技术,提高效率
        BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
         //定义输出流,指定目的文件
        PrintWriter out  = new PrintWriter(new FileWriter("F:\\234.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();

	}
}

       在这个程序中,要注意的是在客户端的上传数据上传完毕后,要通过shutdownOutput()方法告诉服务器端,数据已全部上传完毕。否则服务器端一直继续等待客户端上传数据。shutdownOutput()方法,相当于给输出流加了一个结束标记。

       这个程序是单线程的,当一个客户端连接上服务器,被服务器获取到后,其他的客户端就必须等待,而在实际生活中每个服务器都有大量的客户端进行连接,这时为了实现客户端的并发使用服务器,就需要使用多线程技术,将每一个客户端对象都封装在一个线程里,实现客户端的并发使用。下面的程序就是服务器端实现了多线程,客户端可以并发使用,如下:

//将每一个客户端对象封装到线程里
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+"....connected");

			InputStream in = s.getInputStream();

			File dir =  new File("d:\\pic");

			File file = new File(dir,ip+"("+(count)+")"+".jpg");

			while(file.exists())
				file = new File(dir,ip+"("+(count++)+")"+".jpg");

			FileOutputStream fos = new FileOutputStream(file);

			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  PicServer
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(10007);

		//多个客户端可以同时访问
                 while(true)
		{
			Socket s = ss.accept();

			new Thread(new PicThread(s)).start();

		
		}

	
	}
}

下面是一个登陆服务器端的程序:

/*
客户端通过键盘录入用户名。
服务端对这个用户名进行校验。

如果该用户存在,在服务端显示xxx,已登陆。
并在客户端显示 xxx,欢迎光临。

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

最多就登录三次。
*/
import java.io.*;
import java.net.*;

//定义客户端
class  LoginClient
{
	public static void main(String[] args) throws Exception
	{
		Socket s = new Socket(InetAddress.getLocalHost(),10008);
        //建立流对象,获取键盘录入,加入缓冲
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));
        //获取Socket输出流
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
        //获取Socket输入流,加入缓冲
		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();
		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("F:\\list.txt"));

				PrintWriter out = new PrintWriter(s.getOutputStream(),true);

				String line = null;
                //定义判断标识
				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(10008);

		//客户端可并发访问
         while(true)
		  {
			Socket s = ss.accept();

			new Thread(new UserThread(s)).start();
		 }
	}
}

三、浏览器和Tomcat服务器

          浏览器就是最常见的客户端,它能够对html代码进行解析。下面自定义一个服务器,通过浏览器对其进行访问。程序如下:

public static void main(String[] args) throws Exception {
		//建立ServerSocket对象,创建Socket服务
         ServerSocket ss=new ServerSocket(10009);
        //获取连接客户端对象
         Socket s=ss.accept();
        //获取Socket服务的输出流。  
         PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
        //输出数据
         pw.println("<font color='black' size='8'>欢迎光临服务器</font>");
        //关闭资源
         s.close();
         ss.close();
	}
    在浏览器输入IP地址和端口号,就能看到服务端的输出数据。效果如下:


         还可以通过浏览器访问Tomcat服务器,Tomcat是常用的一种服务器,它内部封装了ServerSocket对象,可以读取自定义的资源。可以把自定义的资源存储在Tomcat目录中的myweb目录里。下面我们就通过定义程序,模拟浏览器,访问Tomcat服务器,获取数据。这时就需要通过向Tomcat发送http请求消息头,来获取资源。如下:

  public static void main(String[] args)throws Exception 
	{   
		//建立Socket对象,创建Socket服务
		Socket s = new Socket("192.168.1.113",8080);
		//获取输出流
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
        //发送请求消息头
		out.println("GET /myweb/2.html HTTP/1.1");//请求获取指定资源路径下的资源
		out.println("Accept: */*");//可以接收所有数据类型
		out.println("Accept-Language: zh-cn");//接收语言
		out.println("Host: 192.168.1.113:8080");//明确IP地址和端口
		out.println("Connection: Keep-Alive");//连接状态
        //请求消息头必须和消息体有空行。
		out.println();
		out.println();
        //获取输入流
		BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream()));

		String line = null;
          
		while((line=bufr.readLine())!=null)
		{
			System.out.println(line);
		}
        //关闭资源
		s.close();

      }
}

       要注意的请求消息头和请求消息之间一定要用空行隔开。接下来我们再结合图形化界面,创建一个简易的浏览器,因为要获取输入的指定IP地址、主机名、端口等,这时就要用到URL对象,URL代表一个统一资源定位符,内部带有协议的封装了Socket对象,通过其特有方法获取I要连接的服务器端,通过openConnection方法与服务器端建立连接。下面就通过URL对象,实现浏览器的创建和功能实现。如下:

class  IEDemo
{
	//定义组件引用
        private Frame f;
	private TextField tf;
	private Button but;
	private TextArea ta;
	
	private Dialog d;
	private Label lab;
	private Button okBut;


	IEDemo()
	{
		init();
	}
	public void init()
	{
		//窗体的创建、基本设置
                f = new Frame("my window");
		f.setBounds(300,100,600,500);
		f.setLayout(new FlowLayout());

		tf = new TextField(60);

		but = new Button("转到");

		ta = new TextArea(25,70);


		d = new Dialog(f,"提示信息-self",true);
		d.setBounds(400,200,240,150);
		d.setLayout(new FlowLayout());
		lab = new Label();
		okBut = new Button("确定");

		d.add(lab);
		d.add(okBut);



		f.add(tf);
		f.add(but);
		f.add(ta);

                //加载事件
		myEvent();
		f.setVisible(true);
	}
	private void  myEvent()
	{
                
		okBut.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				d.setVisible(false);
			}
		});
		d.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				d.setVisible(false);
			}
		});
                //文本框添加键盘监听
		tf.addKeyListener(new KeyAdapter()
		{
			public void keyPressed(KeyEvent e)
			{
				try
				{
				if(e.getKeyCode()==KeyEvent.VK_ENTER)
					showDir();
				}
				catch (Exception ex)
				{
					throw new RuntimeException("操作失败");
                                }
			
			}
		});

                //按钮添加活动监听
		but.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				try
				{
					showDir();
				}
				catch (Exception ex)
				{
				  throw new RuntimeException("操作失败");
				}
				
				
			}
		});
                  
		f.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);	
			}
		});
	}
        //定义连接客户端的方法
	private void showDir()throws Exception
	{

		ta.setText("");
	         
                String urlPath = tf.getText();
		
		//建立URL对象
                URL url = new URL(urlPath);

		//实现连接
                URLConnection conn = url.openConnection();
		//获取输入流
		InputStream in = conn.getInputStream();

		byte[] buf = new byte[1024];

		int len = in.read(buf);
                //将数据写入文本区
		ta.setText(new String(buf,0,len));

	}

	public static void main(String[] args) 
	{
		new IEDemo();
	}
}
    注意一般输入网址,是不带端口号的,此时通过URL的getPort()获取。若通过网址返回的port为-1,则分配一个默认的80端口

小知识点:

        1.当Socket对象中构造函数为空时,可以通过connect方法连接服务器。

        2.ServerSocket(int port ,int backlog ),backlog表示队列的最大长度,即最多有几个客户端。通过这个构造函数,对客户端数量进行限制。

       3.我们上网时输入的都是网址,而不是ip地址,是怎么连接到指定主机的呢?这时就需要域名解析(DNS),将主机名变成IP地址。我们访问一个网址的过程是这样的:我们在进行访问时,会现在本机的hosts文件中寻找对应映射,有的话就直接返回。没有,就到DNS中去找对应的映射,找打后返回对应的IP地址,再通过IP地址找到对应的服务器。


-------------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值