关于网络的笔记

来源:互联网 发布:python 数据清洗 编辑:程序博客网 时间:2024/04/27 13:47

[黑马程序员]关于网络的笔记

Socket套接字,封装了底层数据传输的细节,程序通过它与服务器建立连接,实现通信。在客户/服务器通信模式中,客户端主动建立与服务器的连接(需要创建Socket),服务器收到连接请求后也创建与客户连接的Socket。Socket可以看做通信两端的收发器,客户端和服务器都通过它进行数据传输。

附注:

UDP协议:用户数据报协议,面向非连接,不可靠传输,传输速度快,适用于传输少量数据

TCP协议:传输控制协议,面向连接,可靠传输,传输速度慢,适用于传输大量数据

 

Java中有种套接字:

java.net.Socket、java.net.ServerSocket、java.net.DatagramSocket,前面两个建立在TCP协议上的,后面一个建立在UDP协议上的。

 

java.net.Socket、java.net.ServerSocket是建立在TCP协议的基础上,TCP以恶意是一种可靠的数据传输协议,如果数据传输中途丢失或者损坏,TCP会再次发送数据;如果数据到达到接受方时顺寻打乱,那么TCP协议在接收方会重新恢复数据的正确顺序。但是传输速率会降低。

 

java.net.DatagramSocket建立在UDP协议上,它的传输速率较高,但是UDP协议,但是不可靠,他无法保证数据一定到达接收方,也不能保证到达的数据是正确的数据。它适合传输少量数据。

使用UDP协议通信,客户机和服务器是对等的,都是使用DatagramSocket,它既可以接受数据,也可以传输数据。UDP是无连接的,所以不存在一一对应关系。每个DatagramSocket与本地地址(ip+端口)绑定。DatagramSocket可以把数据把数据发送给任意一个远程DatagramSocket,也可以接受任意一个远程的DatagramSocket的数据。接受和发送的数据封装在DatagramPacket中。

 

一、            DatagramPacket

 

它可以分两类,一个是用于发送,一个是用来接收。用于发送数据,构造方法要指定目的地址(DatagramPacket包含:将要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号),而接受方无需指定。

发送方

1、DatagramPacket(byte[] buf, int length, InetAddress address, int port)

2、DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)

3、DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)

4、DatagramPacket(byte[] buf, int length, SocketAddress address)

发送方将buf中的数据发送出去。

 

接受方:

DatagramPacket(byte[] buf, int length)

DatagramPacket(byte[] buf, int offset, int length)

接收方把数据放到buf数组中。

 

如发送方代码可以像这样(使用InetAddress):

InetAddress ip = InetAddress.getByName("localhost");//getByName(远程主机名字)得到远程主机是InetAddress

int port = 8000;//指定远程主机监听的端口号

byte[] data = "message".getBytes();//要发送的数据,byte数组的形式

DatagramPacket dp = new DatagramPacket(byte[] data, data.length,ip,port);

或者使用SocketAddress:

InetAddress ip = InetAddress.getByName("localhost");//getByName(远程主机名字)得到远程主机是InetAddress

int port = 8000;//指定远程主机监听的端口号

SocketAddress remoteAddr = new SocketAddress(ip,port);

byte[] data = "message".getBytes();//要发送的数据,byte数组的形式

DatagramPacket dp = new DatagramPacket(byte[] data, data.length,remoteAddr);

 

可以通过DatagramPacket得到远程主机的地址:

public int getPort()

public InetAddress getAddress()

public SocketAddress getSocketAddress()

 

也可以得到接收到的数据以及数据长度

byte[] getData()

int getLength(),它并不一定等于buf数组的长度。

 

二、DatagramSocket类:

构造方法

DatagramSocket:每个DatagramSocket与本地地址,构造方法

DatagramSocket()由操作系统分配一个可用端口

DatagramSocket(int port)绑定端口

DatagramSocket(int port,InetAdress laddr)绑定端口且指定地址(适合一个主机有多个地址的情况)

DatagramSocket(SocketAdress addr)绑定地址(适合一个主机有多个地址的情况)

 

DatagramSocket获得绑定的地址:

int getLocalPort()

InetAddress getLocalAddress();

SocketAdress getLocalSocketAdress();

 

注意:InetAddress通常为IP地址;SocketAddress通常为 IP 地址 + 端口号。获得远程的地址是通过DatagramPacket获得

public int getPort()

public InetAddress getAddress()

public SocketAddress getSocketAddress()

 

发送和接受数据

public void send(DatagramPacket dp)

发送dp中的数据。注意atagramPacket需要包含:将要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号。由于UDP传输不可靠,所以调用send()方法数据发送正确但是并没有到达接方,也不会抛出异常。

public void receive(DatagramPacket dp)

接收数据,存放到dp中。如果网络上没有数据,那么执行方法的线程会阻塞,直到收到数据。如果接收到的数据长度超过缓冲区,那么超出的数据就会丢失,所以dp的缓冲区应该足够大(即buf数组足够大)。

使用UDP协议的聊天程序的例子:

package org.wang;

 

import java.awt.*;

import java.io.*;

import java.net.*;

public class Mychat extends Frame{

   

    List list = new List(8);

    TextField tfIP = new TextField(15);

    TextField tfData = new TextField(25);

    Panel panel = new Panel();

   

   

    DatagramSocket ds ;

    public Mychat(){

       this.add(list);

       this.add(panel,"South");

      

       panel.setLayout(new BorderLayout());

       panel.add(tfIP,"West");

       panel.add(tfData,"East");

      

       try {

           ds = new DatagramSocket(3000);

       } catch (SocketException e1) {

          

           e1.printStackTrace();

       }

      

       new Thread(new Runnable(){

 

           @Override

           public void run() {

              byte[] buf = new byte[1024];

              DatagramPacket dp = new DatagramPacket(buf,buf.length);

              while(true){

                  try {

                     ds.receive(dp);

                  } catch (IOException e) {

                     if(!ds.isClosed())

                         e.printStackTrace();

                  }

                  //new String(dp.getData(),0,dp.getLength())

                  //dp.getData().toString()

                  list.add(new String(dp.getData(),0,dp.getLength())+"    FROM:"+

                         dp.getAddress().getHostAddress()+":"+

                         dp.getPort(),0);

              }

             

             

           }

          

       }).start();

             

       tfData.addActionListener(new ActionListener(){

 

           @Override

           public void actionPerformed(ActionEvent e) {

              //获得对方IP地址

              byte[] buf = tfData.getText().getBytes();

             

              try {

                  DatagramPacket dp = new DatagramPacket(buf, buf.length,

                         InetAddress.getByName(tfIP.getText()),3000);

                  ds.send(dp);//这两句话都有异常

              } catch (IOException e1) {

                  // TODO Auto-generated catch block

                  e1.printStackTrace();

              }

              //发送消息后,清空文本框

              tfData.setText("");

             

             

           }

          

       });

       addWindowListener(new WindowAdapter(){

            public void windowClosing(WindowEvent e) {

               ds.close();

               dispose();

               System.exit(0);

            }

           }

       );

    }

   

   

    public static void main(String[] args){

       Mychat mychat = new Mychat();

       mychat.setSize(400,580);

       mychat.setTitle("chat");

       mychat.setVisible(true);

       //mychat.setResizable(false);

      

    }

 

}

 

三、基于TCP协议的通信

 

客户端使用Socket与服务器通信,

Socket的构造方法较多,常用的有

Socket()通过系统默认类型的 SocketImpl 创建未连接套接字

Socket(String host, int port)

Socket(InetAddress address, int port)

其中host指定服务器的主机名,address指定服务器的地址、 port指定服务器的端口

 

客户端使用Socket请求与服务器建立连接,默认会一直等待下去,可以设置等待时间,void connect(SocketAddress endpoint, int timeout)

 这时使用第一个构造方法:

Socket socket = new Socket();

SocketAddress addr = new InetSocketAddress(String hostname, int port)

//InetSocketAddress是SocketAddress是子类

socket.connect(addr,100000);

//connect(SocketAddress endpoint, int timeout)

 

在通信结束后应该及时关闭socket,如果在通信的过程中,如果发送方没有关闭socket就终止了程序,接收方就会抛出异常。

 

服务器端使用ServerSocket,常用的构造方法有:

ServerSocket(int port)

ServerSocket(int port, int backlog)

其中port为服务器绑定的端口,backlog表示接受的客户请求长度。

 

获得ServerSocket对象之后,程序调用accept()方法,监听绑定的端口,等待客户连接,如果收到一个连接请求,就返回一Socket对象,此Socket对象与客户端的Socket对象没什么区别

Socket socket = new ServerSocket(port).accept();

 

Socket和ServerSocket都提供了获得本地地址和端口的方法。另外,Socket提供了getInputStream()和getOutputStream()方法,分别获得InputStream对象和OutputStream对象,通过它们进行数据传输。

 

InputStream和OutputStream传输效率不是很高,一般情况下,通过过滤流来装饰:

对于OutputStream的装饰:

OutputStream out = socket.getOutputStream();

PrintWriter pw = new PrintWriter(out,true);//true表示自动刷新

对于InputStream的装饰:

InputStream in = socket.getInputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(in));

 

 

服务器端(下面的例子的service2()方法可以接受多个客户连接,且互不干扰):

package org.wang;

 

import java.io.*;

import java.net.*;

 

publicclass TcpServer {

   ServerSocket server =null ;

   Socket socket =null ;

   void service(){//service()方法适用于单个客户程序连接

      try {

          server =new ServerSocket(1051);

          socket =server.accept();

          

          InputStream in = socket.getInputStream();

          OutputStream out = socket.getOutputStream();

          

          BufferedReader reader = new BufferedReader(new InputStreamReader(in));

          PrintWriter writer = new PrintWriter(out,true);

          writer.println("欢迎你");

          String msg ;

          while(( msg = reader.readLine()) !=null){

             writer.println("回显:"+msg);

             System.out.println(msg);

          }

      } catch (IOException e) {

          e.printStackTrace();

      }finally{

          try {

             if(socket!=null)socket.close();

          } catch (IOException e) {

             // TODO Auto-generated catch block

             e.printStackTrace();

          }

      }

      

   }

   void service2(){//service2()方法适用于多个客户程序连接

      try {

          server =new ServerSocket(7000);

          

          while(true){

             socket =server.accept();//不能方法while的上面

             new Thread(new Server(socket)).start();

          }

      } catch (IOException e) {

          

          e.printStackTrace();

      }

      

      

      

   }

   publicstaticvoid main(String[] args) {

      //new TcpServer().service();//service()只能接受单个客户

      new TcpServer().service2();//接受多个可会连接

   }

 

}

class Serverimplements Runnable{

   Socket socket =null;

   public Server(Socket socket){

      this.socket = socket;

   }

   BufferedReader getReader(Socket socket){

      InputStream in = null;

      try {

          in = socket.getInputStream();

      } catch (IOException e) {

          e.printStackTrace();

      }

      BufferedReader reader = new BufferedReader(new InputStreamReader(in));

      return reader ;

   }

   PrintWriter getWriter(Socket socket){

      OutputStream out = null;

      try {

          out = socket.getOutputStream();

      } catch (IOException e) {

          e.printStackTrace();

       }

      PrintWriter writer = new PrintWriter(out,true);

      return writer ;

   }

   

   @Override

   publicvoid run() {

      BufferedReader reader = getReader(socket);

      PrintWriter writer = getWriter(socket);

      writer.println("欢迎你");

      try {

          String msg = null;

          while(!((msg=reader.readLine()).equalsIgnoreCase("exit"))&&

                        (!msg.equalsIgnoreCase("quit"))){

             String reverseMsg = (new StringBuffer(msg).reverse()).toString();

             System.out.println(msg);

             writer.println(msg+"-->"+reverseMsg);

      

          }  

      } catch (IOException e) {

          e.printStackTrace();

      }finally{

          try {

             if(socket!=null)socket.close();

          } catch (IOException e) {

             e.printStackTrace();

          }

      }

   }

   

}

客户端:

package org.wang;

 

import java.io.*;

import java.net.*;

publicclass TcpClient {

   private String hostname="localhost";

   privateintport = 7000;

   private Socketsocket =null ;

   

   public TcpClient(){

      try {

          socket =new Socket(hostname,port);

      } catch (UnknownHostException e) {

          e.printStackTrace();

      } catch (IOException e) {

          // TODO Auto-generated catch block

          e.printStackTrace();

      }

   }

   BufferedReader getReader(Socket socket){

      InputStream in = null;

      try {

          in = socket.getInputStream();

      } catch (IOException e) {

          e.printStackTrace();

      }

      BufferedReader reader = new BufferedReader(new InputStreamReader(in));

      return reader ;

   }

   PrintWriter getWriter(Socket socket){

      OutputStream out = null;

      try {

          out = socket.getOutputStream();

      } catch (IOException e) {

          e.printStackTrace();

      }

      PrintWriter writer = new PrintWriter(out,true);

      return writer ;

   }

   

   privatevoid connect(){

      BufferedReader reader = getReader(socket);

      PrintWriter writer = getWriter(socket);

      BufferedReader sysReader = new BufferedReader(new InputStreamReader(System.in));

      String msg = null;

      

      try {

          System.out.println(reader.readLine());

          while((msg=sysReader.readLine())!=null){

             writer.println(msg);

             System.out.println(reader.readLine());

             if(msg.equalsIgnoreCase("quit")||msg.equalsIgnoreCase("exit")){

                 break;

             }

          }

      } catch (IOException e) {

          e.printStackTrace();

      }finally{

          try {

             if(socket!=null)socket.close();

          } catch (IOException e) {

             e.printStackTrace();

          }

      }

   }

   publicstaticvoid main(String[] args) {

      new TcpClient().connect();

 

   }

 

}