黑马程序员——java基础——网络编程

来源:互联网 发布:ue软件 编辑:程序博客网 时间:2024/05/01 19:33

一,网络模型

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

这个网络模型将网络划分为七成:

(1) 物理层:主要定义物理设备标准,如网线的接口类型,光纤的接口类型,各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1,0,也就是我们常说的数模转换和模数转换)。这一层的数据叫做比特。

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

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

(4)传输层:定义了一些传输数据的协议与端口。主要将从下层的数据进行分段和传输,到达目的地后再进行重组。常把这一层的数据叫做段。

(5)会话层:通过传输层建立数据传输的通路。主要在你的系统之间发起会话或者接受会话请求。

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

(7)应用层:主要是一些终端的应用,比如说FTP(各种文件下载),WEB(ie浏览器),QQ之类的。


B:TCP/IP参考模型

上面已经介绍了OSI参考模型 但是它把网络划分成了七层,比较繁琐,所以后来出现了在OSI基础上发展而来的TCP/IP参考模型。该模型只用了四层来表示来表示网络,分别是:主机至网际层    网际层,传输层, 应用层。

OSI参考模型和TCP/IP参考模型层与层之间的对应关系


看上面的图片可以看出 传输层和网际层比较独立 ,所以这两层是整个模型中非常重要的两层 后面的课程也是主要以他两为主。

二,网络通讯的三要素

A:IP地址

IP地址是网络中设备的标识,存在于网络参考模型中的网际层。由于IP地址是由数字或者数字和字母组成的 所以不方便记忆,可以用主机名表示IP地址。本地回环地址 127.0.0.1  主机名:localhost。

Java中用一个类来描述IP地址 就是InetAddress ,该类用两个子类Inet4Address(IPV4)和Inet6Address(IPV6)。该类中有封装解析IP地址的方法。

//获取本地主机的IP地址对象InetAddress localIp=InetAddress.getLocalHost();//输出IP地址System.out.println(localIp.getHostAddress());//输出主机名System.out.println(localIp.getHostName());
该类的getByName(String host)这个方法返回的是一个IP地址对象   这个参数host是主机名
<span style="white-space:pre"></span>InetAddress inetAddress=InetAddress.getByName("www.baidu.com");        System.out.println(inetAddress.getHostAddress());        System.out.println(inetAddress.getHostName());
输出结果:

111.13.100.91
www.baidu.com


getAllByName(String host)  在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。

    InetAddress[] inetAddress=InetAddress.getAllByName( "www.baidu.com");     System.out.println(Arrays.toString(inetAddress));
输出结果:

[www.baidu.com/111.13.100.92, www.baidu.com/111.13.100.91]

B:端口号

用来标识进程的逻辑地址,是不同进程的标识。有效的端口是0—65535 ,通常0—1024端口会是系统使用或保留的端口。

C:传输协议,定义了通信的规则常见的传输协议有TCP协议 , UDP协议。

(1)UDP协议 叫做数据报文协议

UDP传输协议的特点

a, 将数据及源和目的都封装在数据包中,不需要建立连接。

b, 每个数据包的大小都限制在64k内,这并不意味值UDP传输不能传送大量的数据,只是每个数据包的大小是有限制的要是数据量大的话,可以存放在多个数据包中。

c, 因无连接 ,所以是不可靠的协议。

d, 因为不需要建立连接,所以传输速度快。

(2)TCP协议 叫做传输控制协议

TCP传输协议的特点

a,需要建立连接,形成传输数据的通道。

b,在形成的通道中可以进行大量数据的传输。

c,通过三次握手建立连接是可靠的协议。

d,因为要求必须要建立连接,所以效率会低。


对讲机的数据传输的方式就是UDP  一个人拿着对讲机喊话,但是另外一个对讲机没有开,那么喊话的内容封装的数据包就直接丢掉 ,QQ的聊天的传输方式也是UDP。   TCP 和UDP 最大的区别就是 可靠  比如在传输数据的时候 对面 不在了,这是传输就会立即停止 不再传输,不会出现数据丢包的情况    打电话就是 TCP协议,UDP同样可以传输大数据只是 只是 每个数据包的大小有限


三, Socket 

socket就是没网络通信提供服务的一种机制。通讯的两端都存在socket,网络通信实际上就是socket之间的通讯,数据在两个socket之间用ioin进行数据传输。

前面讲的两种通讯协议  都需要socket(网络端点)   不同通讯协议有不同的socket  

A:UDP传输


DatagramSocket类就是UDP传输对应的Socket端点对象的类,DatagramPacket 就是UDP传输的数据报包的对象对应的类

DatagramPacket 的构造函数中的参数凡是有InetAddress 类型的参数的构造函数  就表示该数据报包 是被发送的数据包(数据报包 两种  一种里面封装的是被发送的数据,另外一种里面封装的是 接受到的数据)

演示这两个类的使用(发送数据报包)

创建UDP传输的发送端

思路:

1,建立UDP传输的socket服务

2, 将发送的数据封装到数据包中

3, 通过UDP的socket服务将数据包发送出去 就是调用send()方法

4,关系socket服务 因为利用socket服务在通讯的两端进行数据传输或调用到系统底层的资源 。


        //创建UDPsocket服务        DatagramSocket ds= new DatagramSocket();                 //要发送的数据        String str= "UDP,发送的数据" ;        byte[] buf=str.getBytes();        //创建要发发送的数据包对象        DatagramPacket dp= new DatagramPacket(buf, buf.length,InetAddress. getByName("127.0.0.1"), 10000);         //发送数据        ds.send(dp);         //关闭资源        ds.close();

运行上面代码是看不到任何的效果的因为 没有一个UDP传输的接收端 在接收上面发送端发送的数据,这就是UDP传输丢包的情况,还有创建上面的socket服务的时候并没有特意指定一个端口 ,但是它会绑定到系统中的任意一个可用的端口。



创建UDP传输的接收端

思路

1, 创建UDP传输的socket服务,因为是接收端所以必须明确一个端口,以方便发送端发送数据。

2, 创建数据包,用来存储接收到的数据,方便用数据包对象的方法解析这些数据。

3, 用socket服务的receive方法接收数据放到数据包中。

4, 通过数据包对象的方法解析这些数据。

5, 关闭资源。

            //创建接收端socket服务,这里在创建DatagramSocket对象的时候要指定 接受数据端口 若果不这定端口就会绑定任意一个端口           DatagramSocket ds= new DatagramSocket(10000);                       //创建一个字节数组 用来封装接受到的数据            byte[] buf= new byte[1024];                       //创建接收数据的数据包对象           DatagramPacket dp= new DatagramPacket(buf,buf. length);                       //调用socket的接收数据的方法这是一个阻塞式的方法           ds.receive(dp);                       //输出接收到的数据  dp.getLength()是获取数据包中的真正的数据的个数           String str= new String(buf,0,dp.getLength());           System. out.println(str);           //关闭服务           ds.close();

注意:上面的receive方法是一个阻塞式方法,如果接收不到发送端发送过来的数据就会阻塞。还有数据包中的getPort() 和getAddress() 返回的都是发送端信息 并不是返回的在发送端程序中将接收端的Ip和端口封装在数据包的数据 ,在UDP传输的时候,要先运行接收端的程序,不然是接收不到数据的。


由于DataGramSosket既能发送数据也能接受数据 所以 我们可以实现在一个程序中是实现 自己发送数据 自己接受数据  (注意,必须要在创建udp socket对象的时候明确端口 )

         //创建UDPsocket服务         DatagramSocket ds= new DatagramSocket(10000);                           //要发送的数据         String str= "UDPf,发送的数据" ;         byte[] buf=str.getBytes();         //创建要发发送的数据包对象         DatagramPacket dp= new DatagramPacket(buf, buf.length,InetAddress. getByName("127.0.0.1"), 10000);         //发送数据         ds.send(dp);         byte[] arr=new byte[1024];         //用于接受的数据包         DatagramPacket re= new DatagramPacket(arr,arr. length);         ds.receive(re);         System. out.println( new String(re.getData(),0,re.getLength()));         //关闭资源         ds.close();


B:TCP传输

TCP传输的原理和UDP差不多只是涉及的对象不太一样 UDP的Socket 既可以发送数据也可以接受数据,而TCP 是需要建立链接的,它的数据传输是在两个端点之间的通道中进行的(这个通道其实就是流 网络流,也叫Socket流),所以它对应的Socket类有两个,一个是客户端的套接字,另外一个是服务器的套接字。(套接字就是Socket)

用下面的图片简单阐述TCP传输



TCP传输客户端:

思路:

1, 创建TCP传输客户端的socket 服务 其实就是socket对象,建议创建该对象的时候就要明确要访问的主机,和指定端口。

2, 通过socket对象的getInputStream和getOutputStream方法获取输出输入输出流。

3, 通过输出流发送数据或者通过输入流读取数据。

4, 关闭资源。

     //创建客户端的socket服务     Socket s=new Socket("localhost",10000);          //获取输出流     OutputStream out=s.getOutputStream();          //发送数据     out.write("TCP传输来了".getBytes());          //关闭资源     s.close();


TCP传输服务端:

思路:


1, 创建TCP传输服务端的socket 服务 其实就是serverSocket对象并指定端口。

2, 获取连接到服务端的客户端对象 通过accept方法。

3, 通过socket对象的getInputStream和getOutputStream方法获取输出输入输出流。

4, 通过输出流发送数据或者通过输入流读取数据。

5, 关闭资源。

    //创建socket服务      ServerSocket ss =new ServerSocket(10000);          //获取连接到服务端的客户端的socket对象     Socket s=ss.accept();          //获取连接过来客户端的IP地址     String ip=s.getInetAddress().getHostAddress();          //获取输入流     InputStream in=s.getInputStream();          byte[] arr=new byte[1024];          //读取数据 并打印     int len=0;     while((len=in.read(arr))!=-1)     {     System.out.println(ip+new String(arr,0,len));     }          //关闭资源     s.close();     ss.close();

服务端是没有流的 我们只有先拿到客户端的socket对象 然后获取流  一个服务端可以连接到多个客户端一般来说服务端的资源是不用关闭的,就上面的代码来言ss.close()可以不用写,s.close()要写 因为 服务端 会有很多的客户端连进来,服务端在处理完了工作之后必须要在服务端 调用获取的客户端socket对象的close方法将客户端和服务端的链接断开,不然会占用服务端的资源,本来我们可以在客户端关闭的 但是客户端的关闭资源不可控 有的客户端就没有关闭资源.服务端的accept方法是阻塞的,要是没有客户端连接过来的话这个方法就是一直阻塞。所以我们应该先运行服务器端的程序,要是先运行客户端的程序的话,会抛出异常。客户端在用输出流往服务器端写入数据,那么对应的 服务器端就是再用 输入流读取数据


客户端和服务器通讯 客户端给服务器发送数据 ,服务器接收到数据之后给客户端一个反馈

                   //创建TCPsocket客户端对象 这要指定服务器的IP 和端口           Socket s=new Socket(InetAddress.getLocalHost(),10000);                                  //获取输出流对象           OutputStream out=s.getOutputStream();           String str= "TCP通讯,数据发送了" ;            byte[] buf=str.getBytes();            //将数据发送给服务器           out.write(buf);                       //获取一个输入流对象           InputStream in=s.getInputStream();            //从服务器接收数据            byte[] newbuf= new byte[1024];            int ch=in.read(newbuf);           System. out.println( new String(newbuf,0,ch));                       //关闭资源           s.close();


            //创建服务器端socket对象 这里必须对外要暴露一个端口           ServerSocket ss= new ServerSocket(10000);            //获取链接到该服务器的客户端socket对象   这个方法是阻塞式 的方法           Socket s=ss.accept();            //获取读取流对象           InputStream in=s.getInputStream();            //nwe一个字节数组 用来接受从客户端发送过来的数据            byte[] buf= new byte[1024];            //用输入流读取客户端发送过来的数据            int ch=in.read(buf);           System. out.println( new String(buf,0,ch));                       //获取一个输出流           OutputStream out=s.getOutputStream();            //服务器接收到数据之后 要给客户端 一个反馈           out.write( "数据已经收到了" .getBytes());                       //关闭资源           s.close();           ss.close(); //服务器一般不用关的

现在需求 在控制台上输入数据,发送给服务器,服务器接收到数据将数据输出,如果接收到字母数据将数据转换成大写的返回给客户端。

客户端:

//创建客户端的socketSocket s=new Socket("localhost",10000);//录入用户输入BufferedReader bufReader=new BufferedReader(new InputStreamReader(System.in));String temp=null;//获取输出流PrintWriter p=new PrintWriter(s.getOutputStream(),true);//获取输入流BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));byte[] arr=new byte[1024];while((temp=bufReader.readLine())!=null){if("over".equals(temp))break;//向服务器端发送数据p.println(temp);//从服务器端读取数据String str=bufIn.readLine();System.out.println(str);}//关闭资源s.close();

服务端:

//创建服务端的socketServerSocket ss=new ServerSocket(10000);//获取连接到服务端的客户端Socket s=ss.accept();//获取输入流BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));//获取输出流PrintWriter p=new PrintWriter(s.getOutputStream(),true);String temp=null;while((temp=bufIn.readLine())!=null){System.out.println(temp);//返回数据给客户端p.println(temp.toUpperCase());}//关闭资源s.close();ss.close();


用多线程实现一个文件的上传(一个服务器可以同时连接到多个客户端)


public class UploadClient {     public static void main(String[] args) throws IOException {            // TODO Auto-generated method stub            //创建TCP客户端对象           Socket s= new Socket(InetAddress.getLocalHost(),50002);            //创建数据读取流 从硬盘里面读取数据           FileInputStream fileIn= new FileInputStream( "c://【捌零无损音乐】王冰洋-飞舞.WAV" );            //获取socket输出流将从硬盘里面读取的数据发送到服务器端           OutputStream out=s.getOutputStream();            byte[] buf= new byte[1024];            int len=0;            while((len=fileIn.read(buf))!=-1)           {                out.write(buf,0,len);           }            //告诉服务器端 文件传输已经结束           s.shutdownOutput();                       //创建socket输入读取从服务器端的返回信息           InputStream in =s.getInputStream();            byte[] buf1= new byte[1024];            int len1=in.read(buf1);           String str= new String(buf1,0,len1);           System. out.println(str);                       //关闭资源           fileIn.close();           s.close();     }}public class UploadServer {     public static void main(String[] args) throws IOException {            // TODO Auto-generated method stub            //创建服务器端的socket对象           ServerSocket ss=new ServerSocket(50002);            //获取连接到服务器的客户端            while( true)           {           Socket s=ss.accept();            new Thread( new SocketTask(s)).start();                      }                }}public class SocketTask implements Runnable {     private Socket s= null;     public SocketTask(Socket s) {            super();            this. s = s;     }          @Override     public void run() {            // TODO Auto-generated method stub            //定义一个计数器 如果文件存在的话  最终的文件的名称上加1 带上小括号            int count=0;            //创建一个输出流将送客户端读取到的数据写入到硬盘里面           OutputStream bufOut= null;            //获取连接服务器端的客户端的IP           InetAddress IP= s.getInetAddress();            try{                 //获取数据读取流 从客户端读取数据                InputStream in= s.getInputStream();                 byte[] buf= new byte[1024];                 //new File对象就是就是服务器端最最终写入硬盘里面的文件件                File dir= new File( "c://dir");                 if(!dir.exists())                     dir.mkdirs();                File file= new File(dir,IP+ ".wav");                 //如果文件已经存在于服务器端 就让文件名带上编号                                 while(file.exists())                {                     file= new File(dir,IP+"("+(++count)+")" +".wav" );                }                bufOut= new FileOutputStream(file);                 int len=0;                 while((len=in.read(buf))!=-1)                {                     bufOut.write(buf,0,len);                }                           //创建一个输出流 给客户端反馈信息                OutputStream out= s.getOutputStream();                out.write( "上传完成".getBytes());                           }            catch(IOException e)           {}            finally           {                 if( this. s!= null)                      try{                      this. s.close();                     }                      catch(IOException e)                     {                                                }                                 if(bufOut!= null)                 try{                     bufOut.close();                }                 catch(IOException e)                {                                     }           }     }}




0 0
原创粉丝点击