关闭

黑马程序员—网络编程

105人阅读 评论(0) 收藏 举报

java基础学习笔记——网络编程

网络编程,在java中亦可以称作Socket编程,此时,看到Socket这个英文单词的时候,动动手,在有道词典中便可以很轻松的获得它的含义:Socket n.  插座;窝,穴;牙槽;套接字。vt. 给…配插座。在本文中和java的官方手册中便采取套接字的含义。

提到网络,我们最熟悉的就是TCP/IP协议的四层次结构和其中的一些协议了。这里我们只要简单的了解一下就行,毕竟在此环境下我们是学习的是java语言的基本知识。我们需要简单知道的有在java编程中常见的一些网络结构、浏览器和服务器的工作原理、TCP/IP协议中一些经常用的协议、当然最好我们还得知道一下DNS解析的原理。因此本文的架构便显得很清晰了。

一、网络结构:

1.C/S(client/server)结构:

在此中结构中,程序猿最关心的是我们在开发的时候都应该做什么样的工作。顾名思义,当我们看到c/s时,我们便很清楚的知道,改结构的软件,客户端和服务端都是需要编写的。这也是它的缺点,当然这样的结构也有它自己的好处,我们想想,当一个网络结构的任务分别由客户端和服务器端完成时,服务器端开始就乐了,因为客户端承担了一部分的工作,服务器端的任务量就减轻了,服务器端就可以留出一些资源干别的事情了。

2.B/S(browser/server)结构:

与c/s结构的网络组成来比,b/s这种结构的软件,我们只负责服务器端的开发。因为用户可以用常见的浏览器就可以轻松的取代客户端的功能。所以开发成本相对低,维护更为简单。这是它的优点,同时这种结构的网络组成的缺点是所有运算都要在服务端完成,因此对一些大规模的需求应用来说,服务器的性能就要有所要求。

二、域名解析原理:

首先我们要知道什么是DNS,DNS就是域名服务器(Domain Name Server)的缩写。顾名思义,这个东东是来搞定域名解析的,譬如,当你在浏览器中输入"www.baidu.com"的时候,你是不是会很纳闷,这一步到底在底层做了些什么?

其实,在你输入"www.baidu.com"的时候,DNS就开始为你工作了,因为你要想访问一台机器上的一个正在运行的程序的时候,首先得拿到这台机器的IP地址啊,这就相当于你要找一个人,首先得知道他们家的门牌号,其次,在找她在他们家的哪个屋子里边居住,而找屋子的这一过程反映在电脑方面就是域名解析服务器干的事情了。同时浏览器找主机的过程中就是在服务器中查找对应的应用程序的端口号的过程。只有当我们拿到一个IP号码,一个PORT时,我们才可以在“茫茫脑海”中找到你所要的那台电脑,例如百度的服务器,在通过PORT找到你要请求的应用程序。

因此,这就是你输入"www.baidu.com"时,所做的一些简单的工作,而域名服务器就是将人们好记的"www.baidu.com"转换成IP地址的过程。可端口号是干什么的呢?

在此,用户的主机如果可以上网的话,你可以在浏览器的地址里输入"www.baidu.com:80",然后回车,发现浏览到了百度的搜索网站,因为你的这一请求应用的是http协议(超文本传输协议(Hyper Text Transport Protocol)
),它的默认端口号码就是80,你明确也可以,不明确也可以。

      至此,You know ...?


三、浏览器和服务器交互原理:

接上一话题:当你找到了一台主机和主机上的对应此端口号的应用程序之后,为什么浏览器会给你展示你想要的资料和东东呢????

这就是属于浏览器和服务器之间的秘密对话了,浏览器和服务器到底说了些什么悄悄话呢?只有开发者知道,对于我这个小菜鸟来说也得简单了解一下。

首先,以小学语文的角度来说,这次事件的主动发起者则是用户输入的一串地址了!这串地址到底干了些什么?见下面:

GET / HTTP/1.1 //请求行请求方式  请求的资源路径   http协议版本。
Accept:
image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel,application/vnd.ms-powerpoint, application/msword, */*  
Accept-Language: zh-cn,zu;q=0.5
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.2)
Host: 127.0.0.1:8080
Connection: Keep-Alive

以上就是我在eclipse中简单测试中读取到的浏览器发给服务器的请求。用过Tomcat的伙伴都可以看出,我的服务端是用Tomcat来模拟的,因为我用的是8080端口。

当服务器接受到请求之后,服务器就是被动请求者,它会反馈给浏览器什么信息呢?继续做实验测试,我们可以看下面的代码:      

HTTP/1.1 200 OK应答行//:http的协议版本   应答状态码   应答状态描述信息
Server: Apache-Coyote/1.1
ETag: W/"199-1323480176984"
Last-Modified: Sat, 10 Dec 2011 01:22:56 GMT
Content-Type: text/html
Content-Length: 199
Date: Fri, 20 Aug 201407:51:39 GMT
Connection: close

在此,我们只需简单了解一下上述字眼的含义即可,黑色部分是解释了很重要部分的含义,至于其它部分有兴趣的同学可以自行 度娘 or Google。上述测试只是截取了请求头和应答头部分,至于请求体和应答体部分too long,应此未贴上。

四、TCP/IP传输层中的两种常见协议

1.TCP

TCP(Transmission Control Protocol),In Chinese,就是传输控制协议的简称。特点就是:面向连接,安全,可靠,三次握手。在Java中的对象体现就是一个客户端Socket和一个服务端ServerSocket。具体细节参考计算机网络相关书籍。

2.UDP

UDP(User Datagram Protocol),In Chinese,就是用户数据报协议的简称。在Java中的对象体现就是客户端和服务端共同使用的收发点:DatapragramSocket和数据包DatapragramPacket。特点:面向无连接,不可靠。

五、java网络编程对象学习

首先,因为java是一种面向对象的编程语言,因此,要学习java中的网络编程,那么我们的主要任务就是了解API文档中的相关对象。这些对象则在java.net包中,因此,在你的程序中如果用到此类对象,就记得要导入此包中的相关对象哦。当然,我们使用的高级编程工具eclipse工具则只需要一个快捷键即可导入所有你需要的包了。ctrl+shift+o即可。当然,在平时的学习中,我们应该留意一下,因为当你遇到记事本文档时,试问,如果你没有平时的积累,怎么导入包?动手查吗?显然,就显得很不专业了,特别是一些面试公司,故意给你出难题,让你在linux系统下的vi编辑中编写一个简单的聊天程序,试问,你如果不知道所用对象在API中的哪个包底下,你怎么写出程序,这也是高级编辑器给我们带来的坏习惯。所以,.....没有所以了,继续。。。。

1.InetAdress对象:

       

  1. public class InetAddress extends Object implements Serializable  

此类的继承关系如上所示。首先,我们看一下在官方的api中是怎么对此类进行描述的:此类表示互联网协议 (IP) 地址。Oh,I know.原来此类是对我们平时所见的IP地址封装成的一个对象。因此,不必再看描述了。然后瞧一眼它的构造方法都有哪些?

在官方文档中瞅了一眼,顿时傻眼了,此哥们竟然没有为我们提供构造方法,意味着我们不能自己new对象喽!我们该怎么办?没关系,继续看此类给我们提供的一些方法,此时我们发现里边有好多的静态方法:



ok,相信,看到此处,我们应该找到了解决没有构造函数的问题了。好了,我们来一个小案例:

  1. //获取本地主机ip地址对象。  
  2. InetAddress ip = InetAddress.getLocalHost();                  
  3. System.out.println(ip.getHostAddress());  
  4.   
  5. //获取其他主机的ip地址对象。  
  6. InetAddress ip_other = InetAddress.getByName("www.baidu.com");  
  7. System.out.println(ip_other.getHostAddress());  
  8. System.out.println(ip_other.getHostName());  


运行上述程序,在控制台上则会打印出:

  1. 202.201.12.11  
  2. attitude_pc  
  3. 119.75.217.56  
  4. www.baidu.com  

其中,“202.201.12.11”是我的主机的IP地址,“attitude_pc”是本地机器的名称。“119.75.217.56”是百度的IP地址(You can try it.)。

可以看出,此对象是不是很牛掰,我在第二段测试代码中输入了"www.baidu.com",它竟然给我搞来了ip地址。同时我们也得注意此类的有些方法会抛出UnknownHostException异常,因为你不能瞎传参数啊。

至此,此对象介绍完毕,因为方法太多,不能一一介绍,想要了解的读者请自行看java官方的API文档。

2.与UDP协议有关的API对象:

(1)DatagramSocket

  1. public class DatagramSocket extends Object  
此类的继承关系如上。官方的API是这样描述的:此类表示用来发送和接收数据报包的套接字。数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。

由此可知,我们如果想要使用UDP协议进行通信的话,就得首先建立所谓的数据报套接字,及所谓的端点。通俗点讲,使用此类我们可以建立起收发点。

构造函数摘要

       

从构造函数摘要中我们可以看出:此类在可以创建对象时,可以采用不同的构造方法,可以在创建的时候直接绑定目的主机的相关信息,也可以不指定,在以后的对象使用中调用此类的方法再制定。

此类的相关方法请参考相关的API文档。

(2)DatagramPacket

  1. public final class DatagramPacket extends Object  
此类的继承关系见类描述信息。官方介绍时:此类表示数据报包。数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。因此我们可以看出,当我们利用DatagramSocket建立了数据发送的端点的时候,就可以采用DatagramPacket将要发送的数据打包,并且明确目的主机的相关信息,使数据在端点之间可以传送。
构造函数摘要

       利用途中的构造函数我们可以创建出不同数据包的发送方式。

(3)案例演示一

好了,光说不练假把式,我们现在就来利用以上对象创建属于我们自己的聊天程序。
发送端代码:
  1. import java.io.IOException;  
  2. import java.net.DatagramPacket;  
  3. import java.net.DatagramSocket;  
  4. import java.net.InetAddress;  
  5.   
  6. public class UDPClient {  
  7.   
  8.     public static void main(String[] args) throws IOException {  
  9.         /*1.利用DatagramSocket建立我们自己的发送点; 
  10.          * 2.利用DatagramPacket打包我们要发送的数据,并且明确发送对方的相关地址; 3.发送我们的数据; 
  11.          * 4.建立监听器,监听来自服务器端的反馈消息。 5.关闭Socket。 
  12.          */  
  13.         System.out.println("发送端启动!!");  
  14.   
  15.         // 建立发送点。  
  16.         DatagramSocket ds = new DatagramSocket();  
  17.   
  18.         // 打包要发送的数据:  
  19.         String content = "Hello,it's the testing content!";  
  20.         DatagramPacket dp = new DatagramPacket(content.getBytes(),  
  21.                 content.getBytes().length, InetAddress.getByName("127.0.0.1"),  
  22.                 10001);  
  23.   
  24.         // 使用发送端将打包好的数据进行发送,目的主机IP和PORT已经指定好了。  
  25.         ds.send(dp);  
  26.   
  27.         // 关闭发送端。  
  28.         ds.close();  
  29.   
  30.     }  
  31. }  



接收端代码:
  1. import java.io.IOException;  
  2. import java.net.DatagramPacket;  
  3. import java.net.DatagramSocket;  
  4.   
  5. public class UDPServer {  
  6.     public static void main(String[] args) throws IOException {  
  7.   
  8.         /* 思路: 
  9.          * 1.利用DatagramSocket建立接受端点,并且明确自己的PORT。2.建立数据包,准备接受数据。3.接受数据。4.解析数据。 
  10.          * 5.关闭端点。 
  11.          */  
  12.   
  13.         System.out.println("接收端启动!!");  
  14.         // 建立udp socket服务。  
  15.         DatagramSocket ds = new DatagramSocket(10001);  
  16.   
  17.         // 创建数据包。  
  18.         byte[] buf = new byte[1024];  
  19.         DatagramPacket dp = new DatagramPacket(buf, buf.length);  
  20.   
  21.         // 接受数据。  
  22.         ds.receive(dp);  
  23.   
  24.         // 解析数据。  
  25.         String content = new String(dp.getData(), 0, dp.getLength());  
  26.         String ip = dp.getAddress().getHostAddress();  
  27.         int port = dp.getPort();  
  28.   
  29.         // 将数据进行简单的打印  
  30.         System.out.println("From:"+ip + ":" + port + ":" + content);  
  31.   
  32.         // 关闭服务。  
  33.         ds.close();  
  34.     }  
  35. }  

Ok,聊天程序已经完成。当然,我们也可以利用多线程技术进行完善代码,减少了我们在测试代码时候程序开启两次的麻烦,这里不再赘述。

(4)案例演示二

有些哥们可能看过上述程序之后会说,你这是聊天程序吗?我怎么不能输入自己想输入的内容呢?Ok,上边的代码就当热热身,Now,我们推出上述代码的完善版本:我们可以在一端用键盘敲入自己想要输入内容,然后当按下回车键之后,让服务端接收到我们的消息,并且显示在控制台上,并且此程序只需要一个main函数控制即可,这就需要多线程技术的支持喽!!!!Ok,让我们看看此程序怎么能够实现?????在此奉上代码:

主函数代码:
  1. import java.io.IOException;  
  2. import java.net.DatagramSocket;  
  3.   
  4. public class ChatControl {  
  5.     public static void main(String[] args) throws IOException {  
  6.         //建立发送端的Socket服务:  
  7.         DatagramSocket sen_ds = new DatagramSocket();  
  8.         //建立接收端的Socket服务,并暴漏出自己的借端口。  
  9.         DatagramSocket rec_ds = new DatagramSocket(10006);  
  10.           
  11.         //开启两个线程:  
  12.         new Thread(new UDPSend(sen_ds)).start();  
  13.         new Thread(new UDPReceive(rec_ds)).start();  
  14.           
  15.     }  
  16. }  

发送端代码:
  1. import java.io.BufferedReader;  
  2. import java.io.InputStreamReader;  
  3. import java.net.DatagramPacket;  
  4. import java.net.DatagramSocket;  
  5. import java.net.InetAddress;  
  6.   
  7. public class UDPSend implements Runnable {  
  8.   
  9.     // 初始化构造函数,使对象能够在创建的时候接受Socket服务对象。  
  10.     private DatagramSocket ds;  
  11.     public UDPSend(DatagramSocket ds) {  
  12.         this.ds = ds;  
  13.     }  
  14.   
  15.     @Override  
  16.     public void run() {  
  17.         try {  
  18.             // 绑定键盘录入  
  19.             BufferedReader bufr = new BufferedReader(new InputStreamReader(  
  20.                     System.in));  
  21.             // 存储键盘录入  
  22.             String line = null;  
  23.             while ((line = bufr.readLine()) != null) {  
  24.                 // 将键盘录入打包,进行发送。  
  25.                 DatagramPacket dp = new DatagramPacket(line.getBytes(),  
  26.                         line.getBytes().length,  
  27.                         InetAddress.getByName("127.0.0.1"), 10006);  
  28.                 ds.send(dp);  
  29.                 //如果键盘录入的是"bye",那么录入程序结束。  
  30.                 if ("bye".equals(line)) {  
  31.                     break;  
  32.                 }  
  33.             }  
  34.   
  35.         } catch (Exception e) {  
  36.             System.out.println("消息发送失败!!");  
  37.         }  
  38.     }  
  39. }  

接收端代码:
  1. import java.io.IOException;  
  2. import java.net.DatagramPacket;  
  3. import java.net.DatagramSocket;  
  4.   
  5. public class UDPReceive implements Runnable {  
  6.   
  7.     // 初始化构造函数,使对象能够在创建的时候接受Socket服务对象。  
  8.     private DatagramSocket ds;  
  9.   
  10.     public UDPReceive(DatagramSocket ds) {  
  11.         this.ds = ds;  
  12.     }  
  13.   
  14.     @Override  
  15.     public void run() {  
  16.         while (true) {  
  17.             try {  
  18.                 // 建立接收数据的缓冲区:  
  19.                 byte[] buf = new byte[1024];  
  20.                 // 将数据读入缓冲区:  
  21.                 DatagramPacket dp = new DatagramPacket(buf, buf.length);  
  22.                 ds.receive(dp);  
  23.                 // 对接收的数据报进行解析:  
  24.                 String content = new String(dp.getData(), 0, dp.getLength());  
  25.                 int port = dp.getPort();  
  26.                 String ip = dp.getAddress().getHostAddress();  
  27.                 // 打印数据:  
  28.                 System.out.println("From:" + ip + ":" + port + ":" + content);  
  29.                 // 判断接收到的数据,从而判断自己的状态是否应该结束。  
  30.                 if (content.equals("bye")) {  
  31.                     System.out.println(ip + "line off!");//break;                 
  32.                 }  
  33.   
  34.             } catch (Exception e) {  
  35.                 // 对异常的简单处理!  
  36.                 System.out.println("接收失败!!");  
  37.             }  
  38.         }  
  39.     }  
  40.   
  41. }  
OK,程序书写完毕 !You can try it in your own IDE(Integrated Development Environment)...

2.与TCP协议有关的API对象

(1)Socket对象

public class Socket extends Object

此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。 

构造方法摘要:

在TCP创建客户端的过程中,建议此客户端一创建就应该明确目的主机。Socket客户端一旦建立,就在底层建立了Socket流,包括输入流和输出流,也就是数据传输的通道。
获取流的方法是:
public InputStream getInputStream() throws IOException
public OutputStream getOutputStream()throws IOException

(2)建立客户端的代码演示

  1. <span style="font-family: Arial, Helvetica, sans-serif;">//注意:下列代码要配合服务器端代码使用。</span>  
  1. <span style="font-family: Arial, Helvetica, sans-serif;">package demo.tcp;</span>  
  1. import java.io.IOException;  
  2. import java.io.OutputStream;  
  3. import java.net.Socket;  
  4. import java.net.UnknownHostException;  
  5.   
  6. public class TCPClientDemo {  
  7.         /* 
  8.          * TCP客户端的建立: 
  9.          */  
  10.     public void main(String[] args) throws UnknownHostException, IOException {  
  11.         //建立客户端的Socket服务。  
  12.         Socket s = new Socket("127.0.0.1",10005);  
  13.         //获取输出流。  
  14.         OutputStream os = s.getOutputStream();  
  15.         //写数据  
  16.         os.write("来自tcp客户端的数据!".getBytes());  
  17.         //关闭资源  
  18.         s.close();  
  19.     }  
  20. }  

(3)ServerSocket对象

public class ServerSocket extends Object
此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。

构造方法摘要:

   

要创建一个TCP服务端,就要使用到ServerSocket对象。因为服务端要为客户端提供相应的服务,因此,在建立服务端的时候,就应该为服务端指定相应的端口号。

(4)ServerSocket相关方法

public Socket accept() throws IOException

侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。

即,此方法是用来监听哪一个客户端连接到了服务器本身,当有客户端连接到服务端时,服务端就会调用此方法,来获取Socket对象,从而拿到与客户端关联的相关信息。

(5)服务端建立代码演示

 

  1. package demo.tcp;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.net.ServerSocket;  
  6. import java.net.Socket;  
  7.   
  8. public class TCPServerDemo {  
  9.     /* 
  10.      * TCP服务端建立。 
  11.      */  
  12.     public static void main(String[] args) throws IOException {  
  13.         //建立服务端服务,利用ServerSocket对像,并设置端口号。  
  14.         ServerSocket ss = new ServerSocket(10005);  
  15.         //监听,并且获取连接到其本身的Socket对象。  
  16.         Socket s = ss.accept();  
  17.         //获取输入流,接受客户端发送来的信息。  
  18.         InputStream is = s.getInputStream();  
  19.         //自定义缓冲区,接收数据。  
  20.         byte[] buf = new byte[1024];  
  21.         //读取数据。  
  22.         int len = is.read(buf);  
  23.         String content = new String(buf,0,len);  
  24.           
  25.         System.out.println("来自客户端的消息是:"+content);  
  26.           
  27.         //关闭客户端  
  28.         s.close();  
  29.     }  
  30.   
  31. }  

     
0
0

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