Java中Socket应用——TCP和UDP编程

1 通过Socket实现TCP编程

TCP协议是面向连接、可靠的、有序的,以字节流的方式发送数据。
基于TCP协议实现网络通信的类:

  • 客户端的Socket类
  • 服务器端的ServerSocket类

1.1 Socket通信模型

Soket通信模型图示

1.2 Socket通信实现步骤

  1. 分别在服务器端和客户端创建ServerSocket和Socket(这也是网络通信的基础)
  2. 打开连接到Socket的输入/输出流
  3. 按照协议对Socket进行读/写操作
  4. 关闭:关闭输入输出流、关闭Socket

1.3 ServerSocket和Socket常用方法介绍

  • ServerSocket构造方法:
    • ServerSocket(int port):创建绑定到特定端口的服务器套接字
  • ServerSocket其他方法:
    • Socket accept():侦听并接受到此套接字的连接,调用该方法时会阻塞当前的侦听,等待客户端的连接,客户端连接成功后会创建返回一个Socket实例用来与当前的客户端进行通信重点内容
    • void close():关闭此套接字
    • getInetAddress:返回此服务器套接字的本地地址
    • getLocalPort():返回此套接字在其上侦听的端口

  • Socket构造方法:
    • Socket(InetAddress address , int port):创建一个流套接字并将其连接到指定的IP地址和端口号
    • Socket(String host , int port):创建一个流套接字并将其连接到指定主机上的指定端口号
  • Socket其他方法
    • getInputStream() 返回此套接字的输入流
    • getOutputStream()返回此套接字的输出流
    • shutdownInput()关闭当前Socket的输入流
    • shutdownOutput()关闭当前Socket的输出流

备注:
1. 源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器和服务。

1.4 编程实现TCP的Socket通信

1.4.1 服务器端的通信步骤

  • 建立连接

    • 创建ServerSocket对象,绑定监听端口
    • 通过accept()方法监听客户端请求,等待客户端的连接
    ServerSocket serversocket = new ServerSocket(8888);
    Socket socket = serversocket.accept();
  • 进行通信

    • 连接建立后,通过输入流读取客户端发送的请求信息

      InputStream is = socket.getInputStream();
      InputStreamReader isr = new InputStreamReader(is);
      BufferedReader br =new BufferedReader(isr);
      String info = null;
      while((info = br.readLine()) != null){
           System.out.println("来自客户端的信息:"+info);
      }
    • 通过输出流向客户端发送相应信息

      OutputStream os = serversocket.getOutputStream();
      PrintWriter pw = new PrintWriter(os);
      pw.wrtier("登录成功,欢迎使用");
      pw.flush();
  • 关闭相关资源

    • 读取完客户端信息后紧接着要关闭输入流

      socket.shutdownInput();
    • 给客户端发送完信息后紧接着要关闭输出流

      socket.shutdownOuput();
    • 最后关闭其他资源,务必在所有操作结束后:

      br.close();
      isr.close();
      is.close();
      pw.close();
      os.close();
      socket.close();
      serversocket.close();

1.4.2 客户端的通信步骤

  • 建立连接

    • 创建Socket对象,指定要连接的服务器的地址和端口号
    Socket socket = new Socket("localhost",8888);//"localhost"表示本机名
  • 进行通信

    • 连接建立后,通过输出流向服务器端发送请求信息

      OutputStream os = socket.getOutputStream();
      //将输出流包装为打印流
      PrintWrtier pw = new PrintWriter(os);
      //OutputStreamWriter osw = new OutputStreamWriter(os);
      //BufferedWriter bw = new BufferedWriter(osw);
      //向服务器端发送信息
      pw.write("username : admin ; key : 12300");
      pw.flush();
    • 通过输入流获取服务器端响应的信息

      InputStream is = socket.getInputStream();
      BufferedReader br = new BufferedReader(new InputStreamReader(is));
      String info = null;
      while((info = br.readLine()) != null){
          System.out.println("来自服务器端的信息:"+info);
      }
  • 关闭相关资源

    • 给服务器端发送完信息后紧接着关闭输出流

      socket.shutdownOutput();
    • 接收完服务器端发送的信息后紧接着要关闭输入流

      socket.shutdownInput();
    • 最后关闭其他资源,务必在所有操作结束后:

      pw.close();
      os.close();
      br.close();
      is.close();

备注:
1. 以上在一个主机上进行测试;
2. 必须先启动服务端后启动客户端。
3. 对于同一个socket,如果关闭了输出(输入)流,这与该流关联的socket也会被关闭,所以一般不用关闭流,直接关闭socket即可
4. 使用TCP通信时更多的是以对象的形式,可以用ObjectOutputStream和ObjectInputStream

1.4.3 使用多线程实现多客户端的通信

  1. 服务器端创建ServerSocket,循环调用accept()等待客户端连接
  2. 客户端创建一个socket并请求和服务器端连接
  3. 服务器端接受客户端请求,创建socket与该客户端建立专线连接
  4. 建立连接的两个socket在一个单独的线程上对话
  5. 服务器端继续等待新的连接

    • 服务器端线程处理类代码:

      public class ServerThread extends Thread {
      /**
      *和本线程相关的Socket  
      */
      private Socket socket = null;
      public ServerThread(Socket socket){
          this.socket = socket;
      }
      /**
       * 执行线程操作,响应客户端的请求
       */
      public void run(){
      
          InputStream is = null;
          InputStreamReader isr = null;
          BufferedReader br = null;
      
          OutputStream os = null;
          PrintWriter pw = null;
          try {
              //1. 获取输入流,并读取客户端信息
              is = socket.getInputStream();
              isr = new InputStreamReader(is);
              br = new BufferedReader(isr);
              String info = null;
              while((info = br.readLine())!=null){
                  System.out.println("来自客户端的信息:"+info);
              }
              //1.1 关闭输入流
              socket.shutdownInput();
              //2. 获取输出流,响应客户端        
              os = socket.getOutputStream();
              pw = new PrintWriter(os);
              pw.write("登录成功,欢迎使用");
              pw.flush();
              //2.1 关闭输出流
              socket.shutdownOutput();
              //获取客户端的IP地址
              InetAddress address = socket.getInetAddress();
              System.out.println("客户端的IP地址:"+address.getHostAddress());
      
          } catch (IOException e) {
              e.printStackTrace();
          }finally
          {
              //3.关闭资源
              try {
                  br.close();
                  isr.close();
                  is.close();
      
                  os.close();
                  pw.close();
      
                  socket.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
    • 服务器端代码:

      public class ServerwithTread {
      public static void main(String[] args) {
          try {
              //1.创建一个服务器端Socket,指定绑定的端口,并监听此端口
              ServerSocket serversocket = new ServerSocket(8888);
              //2. 调用accept()方法开始监听,等待客户端的连接
              System.out.println( "**服务器即将启动,等待客户端的连接**" );
              int count = 0;
              while(true){
                  Socket socket = serversocket.accept();
                  System.out.println("\n**第"+(++count)+"个客户端已连接**\n");
                  ServerThread serverthread = new ServerThread(socket);
                  serverthread.start();
              }
          } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
        }
      }
    • 客户端代码:

      public class Client {
      
      static Scanner scanner = new Scanner(System.in);
      
      public static void main(String[] args) {
      
          try {
              //1. 创建socket对象
              Socket socket = new Socket("localhost",8888);
              //2. 获取输出流,向服务器发送请求
              OutputStream os = socket.getOutputStream();
      
              PrintWriter pw = new PrintWriter(os);
      
              StringBuffer sb = new StringBuffer();
              System.out.println("请输入用户名:");sb.append("username:");
              String name = scanner.next();sb.append(name);
              System.out.println("请输入密码:");sb.append(";key:");
              String key = scanner.next();sb.append(key);
              pw.write(sb.toString());
              pw.flush();
      //          OutputStreamWriter osw = new OutputStreamWriter(os);
      //          BufferedWriter bw = new BufferedWriter(osw);
      //          bw.write("username : admin ; key : 12300");
      //          bw.flush();
              //2.1 关闭socket输出流
              socket.shutdownOutput();
              //3. 获取socket输入流,得到服务器端的响应信息
              InputStream is = socket.getInputStream();
              BufferedReader br = new BufferedReader(new InputStreamReader(is));
              String info = null;
              while((info = br.readLine()) != null){
                  System.out.println("来自服务器的信息:"+info);
              }
              //3.1 关闭输入流
              socket.shutdownInput();
               //4. 关闭资源
              pw.close();
      //          bw.close();
      //          osw.close();
              os.close();
              br.close();
              is.close();
              socket.close();
          } catch (UnknownHostException e) {
              e.printStackTrace();
          } catch (IOException e) {
              e.printStackTrace();
          } 
        }
      }
      

2 通过Socket实现UDP编程

  • UDP协议(用户数据报协议)是无连接、不可靠、无序的,但速度相对较快
  • UDP协议是以数据报为数据传输的载体,进行数据传输时,首先需要将要传输的数据定义成数据报(Datagram),在数据中指明数据所要达到的Socket(主机地址和端口号),然后再将数据报发送出去
  • 相关类:DatagramPacket(表示数据报包)和DatagramSocket(进行端到端通信的类,实现UDP通信)

2.1数据报Datagram

  • 构造方法:

    • DatagramPacket(byte[] buf, int length, InetAddress address, int port)
      构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号

    • DatagramPacket(byte[] buf, int length)
      构造 DatagramPacket,用来接收长度为 length 的数据包

2.2 DatagramSocket

表示用来发送和接收数据报包的套接字

  • 构造方法
    • DatagramSocket()
      创建数据报套接字,将其绑定到本地主机上任何可用的端口
    • DatagramSocket(int port)
      创建数据报套接字并将其绑定到本地主机上的指定端口
    • DatagramSocket(int port, InetAddress laddr)
      创建数据报套接字,将其绑定到指定的本地地址
  • 其他方法
    • close()
      关闭此数据报套接字
    • getInetAddress()
      返回此套接字连接的地址
    • getPort()
      返回此套接字的端口
    • send(DatagramPacket p)
      从此套接字发送数据报包
    • receive(DatagramPacket p)
      从此套接字接收数据报包

2.3 服务器端实现步骤

2.3.1 服务器端接收客户端信息

  1. 创建DatagramSocket,指定端口号

    DatagramSocket soket = new DatagramSocket(7788);
  2. 创建DatagramPacket

    byte[] data = new byte[1024];
    DatagramPacket packet = new DatagramPacket(data,data.length);
  3. 接收客户端发送的数据信息

    socket.receive(packet);

    当调用receive(packet)方法时,在接收到数据报之前会一直处于阻塞状态

  4. 读取数据,数据已经被保存在了数据报的字节数组中

    String info = new String(packet.getData(),0,packet.getlength());
    System.out.println("来自客户端的信息:"+info);

2.3.2 服务器端响应客户端

  1. 定义客户端的地址、端口号、数据(getAddress()返回某台机器的 IP 地址,调用该方法的数据报将要发往该机器或者是从该机器接收到的;getPort()返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的)。

    InetAddress address = packet.getAddress();
    int port = packet.getPort();
    byte[] datar = "欢迎使用!".toBytes();
  2. 创建数据报包,包含响应的数据信息

    DatagramPacket packet2 = new DatagramPacket(datar,datar.lenght,address,port);
  3. 发送数据报包

    socket.send(packet2);

2.4 客户端实现步骤

2.4.1 客户端向服务器端发送信息

  1. 定义发送的信息,比如要发送的服务器的地址、端口号和要发送的数据

    InetAddress address = InetAddress.getByName("localhost");
    int port = 7788byte[] data = "username:admin";key:12300".getBytes();
  2. 创建DatagramPacket,包含将要发送的信息

    DatagramPacket packet = new DatagramPacket(data,data.length(),address,port);
  3. 创建DatagramSocket对象,实现数据的发送

    DatagramSocket socket = new DatagramSocket();
  4. 发送数据

    socket.send(packet);

2.4.2 接收服务器的响应数据

  1. 创建数据报,用于接收服务器响应数据

    byte[] data2 = new byte[1024];
    DatagramPacket packet2 = new DatagramPacket(data2,data2.length);
  2. 接收服务器响应的数据

    socket.receive(packet2);
  3. 读取数据

    String info = new String(packet2.getData(),packet2.getLength());

备注:
1. 最后注意关闭资源:socket.colse();

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值