关于网络的笔记

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

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.*;

 

public class 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();

       }

      

      

      

    }

    public static void main(String[] args) {

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

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

    }

 

}

class Server implements 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

    public void 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.*;

public class TcpClient {

    private String  hostname="localhost";

    private int port = 7000;

    private Socket socket = 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 ;

    }

   

    private void 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();

           }

       }

    }

    public static void main(String[] args) {

       new TcpClient().connect();

 

    }

 

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值