首页 > 编程语言 >Java--网络编程基础

Java--网络编程基础

时间:2022-08-20 16:12:34浏览次数:84  
标签:java Socket -- 编程 import Java 连接 服务端 socket

网络编程

网络编程指的是编写跨多个设备(计算机)执行的程序,设备使用网络彼此连接

J2SE API的java.net包中包含一组类和接口,它们提供低级别的通信详细信息,开发者可编写专注于解决手头问题的程序。java.net包提供对两种常见网络协议的支持

  • TCP - TCP代表传输控制协议,它允许两个应用程序之间的可靠通信。TCP通常用于Internet协议,称为TCP/IP

  • UDP - UDP代表用户数据报协议,这是一种无连接协议,允许在应用程序之间传输数据包


Socket 编程

简介

Socket(套接字)是使用TCP提供通信机制在两台计算机之间进行通信。客户端程序在通信的末尾创建一个套接字,并尝试将套接字连接到服务器。建立连接后,服务器会在通信结束时创建一个套接字对象。客户端和服务器现在可以通过写入和读取套接字进行通信

image-20220505213330285

java.net.Socket类表示一个套接字,java.net.ServerSocket类为服务器程序提供了一种监听客户端并与它们建立连接的机制。使用Socket(套接字)在两台计算机之间建立TCP连接时,会发生以下步骤

  • 服务器实例化ServerSocket对象,表示要在哪个端口号上进行通信

  • 服务器调用ServerSocket类的accept()方法。此方法等待,直到客户端连接到给定端口上的服务器

  • 在服务器等待之后,客户端实例化Socket对象,指定要连接的服务器名称(IP地址)和端口号

  • Socket类的构造函数尝试将客户端连接到指定的服务器和端口号。如果建立了通信,则客户端现在具有能够与服务器通信的Socket对象

  • 在服务器端,accept()方法返回对连接到客户端套接字的服务器上的新套接字的引用

建立连接后,可以使用I/O流进行通信。每个套接字都有一个OutputStream和一个InputStream。客户端的OutputStream连接到服务器的InputStream,客户端的InputStream连接到服务器的OutputStream

TCP是双端通信协议,因此可以同时跨两个流发送数据

image-20220505215024784

Socket通信入门案例

搭建简单的Socket通信入门案例,通过一次性的客户端发送和服务端接收数据,掌握并了解

  • Socket服务端和客户端的基本编程

  • 传输编码统一指定,防止乱码

服务端监听一个端口,等待连接的到来

 import java.io.IOException;
 import java.io.InputStream;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
 ​
 /**
  * @author 杨文成
  * @date 2022/05/05 21:52
  * @describe    服务端监听一个端口,等待连接的到来
  */
 public class SocketServer {
 ​
     public static void main(String[] args) throws IOException {
         //服务端监听指定端口
         ServerSocket serverSocket = new ServerSocket(8888);
 ​
         //服务端阻塞等待连接
         System.out.println("服务端阻塞等待连接端口8888……");
         Socket socket = serverSocket.accept();
 ​
         // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
         InputStream inputStream = socket.getInputStream();
         byte[] bytes = new byte[1024];
         int len;
         StringBuilder builder = new StringBuilder();
         while ((len = inputStream.read(bytes)) != -1) {
             //指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
             builder.append(new String(bytes,0,len, StandardCharsets.UTF_8));
         }
 ​
         System.out.println("服务端接收到客户端的发来的信息:"+builder.toString());
 ​
         //依次关闭资源
         inputStream.close();
         socket.close();
         serverSocket.close();
     }
 }

客户端指定ip(域名亦可)和端口,连接到指定的服务端,进行数据传输

 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
 ​
 /**
  * @author 杨文成
  * @date 2022/05/05 21:59
  * @describe 客户端指定ip和端口,连接到指定的服务端,进行数据传输
  */
 public class SocketClient {
 ​
     public static void main(String[] args) throws IOException {
 ​
         //客户端指定ip(域名亦可)和端口,与服务端建立连接
         Socket socket = new Socket("127.0.0.1", 8888);
 ​
         //获取输出流
         OutputStream outputStream = socket.getOutputStream();
 ​
         //输出数据
         outputStream.write("hello server".getBytes(StandardCharsets.UTF_8));
 ​
         //依次关闭资源
         outputStream.close();
         socket.close();
     }
 }

测试时需要先启动服务端再启动客户端

入门案例优化

双向通信

服务端添加返回响应数据的操作

 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
 ​
 /**
  * @author 杨文成
  * @date 2022/05/05 21:52
  * @describe    服务端监听一个端口,等待连接的到来
  */
 public class SocketServer {
 ​
     public static void main(String[] args) throws IOException {
         //服务端监听指定端口
         ServerSocket serverSocket = new ServerSocket(8888);
 ​
         //服务端阻塞等待连接
         System.out.println("服务端阻塞等待连接端口8888……");
         Socket socket = serverSocket.accept();
 ​
         // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
         InputStream inputStream = socket.getInputStream();
         byte[] bytes = new byte[1024];
         int len;
         StringBuilder builder = new StringBuilder();
         while ((len = inputStream.read(bytes)) != -1) {
             //指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
             builder.append(new String(bytes,0,len, StandardCharsets.UTF_8));
         }
 ​
         System.out.println("server get message from client: "+builder.toString());
 ​
         //服务端返回响应的数据
         OutputStream outputStream = socket.getOutputStream();
         outputStream.write("hello client,i get the message".getBytes(StandardCharsets.UTF_8));
 ​
         //依次关闭资源
         outputStream.close();
         inputStream.close();
         socket.close();
         serverSocket.close();
     }
 }

客户端添加接收服务端返回数据的操作

 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
 ​
 /**
  * @author 杨文成
  * @date 2022/05/05 21:59
  * @describe 客户端指定ip和端口,连接到指定的服务端,进行数据传输
  */
 public class SocketClient {
 ​
     public static void main(String[] args) throws IOException {
 ​
         //客户端指定ip(域名亦可)和端口,与服务端建立连接
         Socket socket = new Socket("127.0.0.1", 8888);
 ​
         //获取输出流
         OutputStream outputStream = socket.getOutputStream();
 ​
         //输出数据
         outputStream.write("hello server".getBytes(StandardCharsets.UTF_8));
 ​
         //通过shutdownOutput告诉服务端,客户端已发送完数据,后续只能接受数据
         socket.shutdownOutput();
 ​
         //接收服务端返回的数据
         InputStream inputStream = socket.getInputStream();
         byte[] bytes = new byte[1024];
         int len;
         StringBuilder builder = new StringBuilder();
         while ((len = inputStream.read(bytes)) != -1) {
             //指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
             builder.append(new String(bytes,0,len,StandardCharsets.UTF_8));
         }
 ​
         System.out.println("client get message from server: " + builder.toString());
 ​
 ​
         //依次关闭资源
         inputStream.close();
         outputStream.close();
         socket.close();
     }
 }

服务端并发处理

在上面的例子中,服务端仅仅只是接受了一个Socket请求,并处理了它,然后就结束了,但是在实际开发中,一个Socket服务往往需要服务大量的Socket请求

循环接受请求并处理 -- 不建议

缺点:循环处理多个Socket请求,不过当一个请求的处理比较耗时的时候,后面的请求将被阻塞

 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
 ​
 /**
  * @author 杨文成
  * @date 2022/05/05 21:52
  * @describe    服务端监听一个端口,等待连接的到来
  */
 public class SocketServer {
 ​
     public static void main(String[] args) throws IOException {
         //服务端监听指定端口
         ServerSocket serverSocket = new ServerSocket(8888);
 ​
         //服务端阻塞等待连接
         System.out.println("服务端阻塞等待连接端口8888……");
 ​
         while (true) {
             Socket socket = serverSocket.accept();
             // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
             InputStream inputStream = socket.getInputStream();
             byte[] bytes = new byte[1024];
             int len;
             StringBuilder builder = new StringBuilder();
             while ((len = inputStream.read(bytes)) != -1) {
                 //指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
                 builder.append(new String(bytes,0,len, StandardCharsets.UTF_8));
             }
 ​
             System.out.println("server get message from client: "+builder.toString());
 ​
             //服务端返回响应的数据
             OutputStream outputStream = socket.getOutputStream();
             outputStream.write("hello client,i get the message".getBytes(StandardCharsets.UTF_8));
 ​
             //依次关闭资源
             outputStream.close();
             inputStream.close();
             socket.close();
         }
     }
 }
线程池接收请求并处理 -- 推荐

用多线程的方式来处理Socket,即每有一个Socket请求的时候,就创建一个线程来处理它。不过在实际生产中,创建的线程会交给线程池来处理,为了

  • 线程复用,创建线程耗时,回收线程慢

  • 防止短时间内高并发,指定线程池大小,超过数量将等待,方式短时间创建大量线程导致资源耗尽,服务挂掉

 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 ​
 /**
  * @author 杨文成
  * @date 2022/05/05 21:52
  * @describe 服务端监听一个端口,等待连接的到来
  */
 public class SocketServer {
 ​
     public static void main(String[] args) throws IOException {
         //服务端监听指定端口
         ServerSocket serverSocket = new ServerSocket(8888);
         //设置一个读取的超时时间,当超过指定的时间后,还没有读到数据,就假定这个连接无用
         //然后抛异常,捕获异常后关闭连接就可以了
         serverSocket.setSoTimeout(10000);
 ​
         //服务端阻塞等待连接
         System.out.println("服务端阻塞等待连接端口8888……");
 ​
         //如果使用多线程,那就需要线程池,防止并发过高时创建过多线程耗尽资源
         ExecutorService threadPool = Executors.newFixedThreadPool(100);
 ​
 ​
         while (true) {
             Socket socket = serverSocket.accept();
 ​
             Runnable runnable = () -> {
 ​
                 InputStream inputStream = null;
                 OutputStream outputStream = null;
 ​
                 try {
                     // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
                     inputStream = socket.getInputStream();
                     byte[] bytes = new byte[1024];
                     int len;
                     StringBuilder builder = new StringBuilder();
                     while ((len = inputStream.read(bytes)) != -1) {
                         //指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
                         builder.append(new String(bytes, 0, len, StandardCharsets.UTF_8));
                     }
 ​
                     System.out.println(String.format("server get message from client: %s,服务处理线程: %s",builder.toString(),Thread.currentThread().getName()));
 ​
                     //服务端返回响应的数据
                     outputStream = socket.getOutputStream();
                     outputStream.write("hello client,i get the message".getBytes(StandardCharsets.UTF_8));
                 } catch (Exception e) {
                     e.printStackTrace();
                 } finally {
                     //依次关闭资源
                     release(inputStream,outputStream,socket);
                 }
             };
 ​
             threadPool.execute(runnable);
         }
     }
 ​
     /**
      * 释放资源
      * @param inputStream
      * @param outputStream
      * @param socket
      */
     public static void release(InputStream inputStream,OutputStream outputStream,Socket socket){
 ​
         try {
             if (inputStream != null) {
                 outputStream.close();
             }
         } catch (IOException e) {
             e.printStackTrace();
         }
 ​
         try {
             if (outputStream != null) {
                 outputStream.close();
             }
         } catch (IOException e) {
             e.printStackTrace();
         }
 ​
         try {
             if (socket != null) {
                 socket.close();
             }
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 }

标签:java,Socket,--,编程,import,Java,连接,服务端,socket
From: https://www.cnblogs.com/52-IT-y/p/16607938.html

相关文章

  • netbeans for php常用快捷键
     https://www.cnblogs.com/onephp/p/5378158.html文本编辑复制当前行到上一行/下一行ctl+shf+up移动当前行到上一行/下一行shf+alt+left/right/down/up删除当前行ct......
  • html页面提交两次才能跳转页面的原因?
    遇到的现象:提交后数据库内容增加了,但页面没有跳转,在网址栏最后多了一个"?"类似于:http://127.0.0.1:8080/test.html?第二次提交才会跳转页面 原因:在form表单中,所......
  • 爱吃瓜?那就来用python采集每天热点,发送到自己的邮箱,绝不错过
    前言嗨喽,大家好呀~这里是爱看美女的茜茜呐本篇文章内容主要为如何用代码,把你想要的内容,以邮件的形式发送出去内容可以自己完善,还可以设置一个定时发送,或者开机启动自动......
  • 深度学习 之 模型部署【3】-ONNX 入门
    ONNX简介开放神经网络交换,OpenNeuralNetworkExchange,是一套表示网络模型的开放格式,由微软和FaceBook在2017年推出;通过几年的快速发展,大有一统整个AI模型(ml、dl)的......
  • 动态规划之——01背包问题
    动态规划(DynamicProgramming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。 ——引用于百度百科我们先从一个例子看下动态规划的思想。有一个最大负重为8千克的背......
  • MySQL中的时间问题(二)
    MySQL中存储的时间,主要分为datetime类型和int类型。一般来说规范的存法是存int型,特别是像过期时间、最近更新等需要排序、比较大小的时间,更应该存为int型。但最近的项目数......
  • 2022 HDU多校1
    String(扩展KMP、差分)Problem给定一个字符串\(S\),定义\(F_S\)表示满足如下条件的正整数\(x\)的数量\(1\lex\le|S|\)\(S[1,\cdots,x]=S[|S|-x+1,|S|]\)\(S[1,\cdot......
  • MybatisPlus核心功能——实现CRUD增删改查操作 (包含条件构造器)
    CRUD官方文档:https://baomidou.com/(建议多看看官方文档,每种功能里面都有讲解)【本文章使用的mybatisplus版本为3.5.2】条件构造器一般都是用service层的方法,因为比ma......
  • 二叉树 查找第k大的数
    改造方法需在节点N中记录以节点N为根的子树的节点数numOfNodes,根节点记录整颗树的节点数目,则若根节点的左子树的numOfNodes刚好为k-1,那这个根节点的值即为目标值。注意......
  • v-for循环
    v-for="xin请求数据所在的数组"x为声明的变量名,存储请求数据的元素x相当于data.data 获取某个值时用x.name相当于data.data.name          ......