网络编程(TCP通信、Socket)入门详解(一)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 网络编程(TCP通信、Socket)入门详解(一)

入门知识

软件结构

  • C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
  • B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。

两种架构各有优势,但是无论哪种架构,都离不开网络的支持。

网络编程:就是在一定的协议下,实现两台计算机的通信的程序。

网络通信的大致流程为:一个数据包经由应用程序产生,进入到协议栈中进行各种报文头的包装,然后操作系统调用网卡驱动程序指挥硬件,把数据发送到对端主机。

整个过程的大体的图示如下:

0df3d7ca7bcb0a46393e9a7404ef132d6a60aff1.png

协议栈其实是位于操作系统中的一些协议的堆叠,这些协议包括 TCP、UDP、ARP、ICMP、IP等。

通常某个协议的设计都是为了解决特定问题的,比如:

  • TCP 的设计就负责安全可靠的传输数据
  • UDP 设计就是报文小,传输效率高
  • ARP 的设计是能够通过 IP 地址查询物理(Mac)地址
  • ICMP 的设计目的是返回错误报文给主机
  • IP 设计的目的是为了实现大规模主机的互联互通


网络通信常见协议:UDP|TCP

UDP:面向无连接的协议,通信的双方不用建立连接,可以直接发送数据

  • 好处:效率高,耗资小
  • 弊端:不安全,容易丢失数据


TCP:面向连接协议,客户端和服务器端必须经过3次握手建立逻辑连接,才能通信

  • 好处:安全
  • 弊端:效率低

三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。

  1. 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。// 服务器你死了吗?
  2. 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。// 我活着 啊!!
  3. 第三次握手,客户端再次向服务器端发送确认信息,确认连接。// 我知道了!!


TCP/IP 协议

TCP/IP协议参考模型

TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中

  • 应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等
  • 传输层:TCP,UDP
  • 网络层:IP,ICMP,OSPF,EIGRP,IGMP
  • 数据链路层:SLIP,CSLIP,PPP,MTU

每一抽象层建立在低一层提供的服务上,并且为高一层提供服务。

v2-6da4cb5ef8a02ea7eeca8fb132bd0776_720w.jpg


IP地址

IP地址:就相当于计算机的身份号(唯一)

ip地址的作用:具有唯一性,在网络中可以通过ip地址找到另外一台计算机

ip地址分类:

  • ipv4:ip地址由4个字节组成,一个字节8位(比特位 1,0)

    二进制:11001101.11001100.11000001.11001111

    十进制:192.168.0.106

    每个字节的范围:0-255(2^8),ip地址第一位不能为0

    ip地址的数量:42亿(2^32=4294967296个)

    问题:随着计算机的增多,ip地址面临枯竭(全球IPv4地址在2011年2月分配完毕)不够用,就出了ipv6地址

  • ipv6:ip地址由16个字节组成,一个字节8位(比特位 1,0)

    ip地址的数量:2^128=3.4028236692093846346337460743177e+38

    号称可以为地球上每一粒沙子编写一个ip地址

    为了表示方便使用十六进制:fe80::a8a6:b83c:8b8b:2685%17

一些常用dos命令:dos窗口 win+r ==> cmd ==> dos窗口

1.查看电脑的IP信息
    命令:ipconfig
    --------------------------------------------------
       Windows IP 配置
       连接特定的 DNS 后缀 . . . . . . . :
       本地链接 IPv6 地址. . . . . . . . : fe80::a8a6:b83c:8b8b:2685%17
       IPv4 地址 . . . . . . . . . . . . : 192.168.0.106
       子网掩码  . . . . . . . . . . . . : 255.255.255.0
       默认网关. . . . . . . . . . . . . : 192.168.0.1
    --------------------------------------------------

2.测试你的电脑和指定ip地址的电脑是否可以连通
    命令:ping ip地址
    --------------------------------------------------
    C:\Users\Administrator>ping 192.168.0.222  没有ping通
    正在 Ping 192.168.0.222 具有 32 字节的数据:
    来自 192.168.0.106 的回复: 无法访问目标主机。
    来自 192.168.0.106 的回复: 无法访问目标主机。
    来自 192.168.0.106 的回复: 无法访问目标主机。
    来自 192.168.0.106 的回复: 无法访问目标主机。
    --------------------------------------------------
    C:\Users\Administrator>ping www.baidu.com
    正在 Ping www.a.shifen.com [61.135.169.121] 具有 32 字节的数据:
    来自 61.135.169.121 的回复: 字节=32 时间=6ms TTL=56
    来自 61.135.169.121 的回复: 字节=32 时间=4ms TTL=56
    来自 61.135.169.121 的回复: 字节=32 时间=4ms TTL=56
    来自 61.135.169.121 的回复: 字节=32 时间=4ms TTL=56
    --------------------------------------------------

3.ping本机的ip地址(你自己电脑的ip地址) 
    命令:ping 127.0.0.1    或    ping localhost 


端口号

端口号是一个逻辑端口,无法直接看到,使用一些软件可以看到(电脑管家,360.…)

当打开网络软件(联网使用)时,操作系统就会为这个网络软件分配一个随机的端口号或者网络软件在打开的时候和操作系统要指定的端口号

端口号是由两个字节组成,表示的范围:2^16=0-65535 之间


1024之前的端口号,不能使用,已经被操作系统分配给一些已知的网络软件。

注意:各个网络软件的端口号是不能重复

常用的端口号:

  • 80端口:网络端口
  • 数据库:mysql:3306, oracle:1521
  • Tomcat服务:8080


保证数据能准确无误发送到对方计算机的某一个软件上,使用 ip地址:端口号

测试端口号是否连通:telnet ip地址:端口号


InetAddress类:获取IP地址

java.net.InetAddress:描述计算机的ip地址

此类表示互联网协议 (IP) 地址。

可以使用InetAddress类中的方法获取到计算机的ip地址

创建对象的方式:静态方法

static InetAddress getLocalHost()             // 返回本地主机(你自己电脑的ip地址对象)。
static InetAddress getByName(String host)     // 在给定主机名的情况下确定主机的 IP 地址。
/* 参数:
     String host:可以传递主机名称、ip地址、域名
*/

​ 非静态的方法:

String getHostAddress()     // 返回 IP 地址字符串(以文本表现形式)。
String getHostName()         // 获取此 IP 地址的主机名。


Socket:套接字

应用程序比如浏览器、电子邮件、文件传输服务器等产生的数据,会通过传输层协议进行传输。而应用程序是不会和传输层直接建立联系的,而是有一个能够连接应用层和传输层之间的套件,这个套件就是 Socket

v2-57afdc329c984ad909d911bcddd6d0c7_720w.jpg


阻塞/非阻塞、同步/异步

  • 阻塞:等待结果,什么事都不能做
  • 非阻塞:可以做别的事情
  • 同步:主动获取结果
  • 异步:等待通知结果


  • BIO:Block(阻塞的) IO 【同步、阻塞】
  • NIO:Non-Block(非阻塞的)(同步)IO 【同步、非阻塞】——JDK1.4开始
  • AIO:Asynchronous(异步-非阻塞)IO 【异步、非阻塞】 ——JDK1.7开始


TCP/IP 通信

TCP 通信的客户端:Socket

作用:主动和服务器经过3次握手建立连接通路,给服务器发送数据,读取服务器回写的数据

表示客户端的类:java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。

套接字:封装了IP地址和端口号的网络单位

构造方法:

public Socket(InetAddress address, int port)    // 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
public Socket(String host, int port)             // 创建一个流套接字并将其连接到指定主机上的指定端口号。
/* 参数:
       InetAddress address | String host:传递服务器的ip地址
       int port:服务器的端口号
*/

​ 成员方法:

OutputStream getOutputStream()    // 返回此套接字的输出流。
InputStream getInputStream()     // 返回此套接字的输入流。

void shutdownOutput()             // 禁用此套接字的输出流。
                                // 对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列

注意:

  1. 创建客户端Socket对象的时候,客户端会根据服务器的ip地址和端口号和服务器经过三次握手连接连接通路

    • 服务器已经启动了,服务器的ip地址和端口号填写正确:握手成功,创建好Socket对象
    • 服务器没有启动,服务器的ip地址和端口号填写错误:握手失败,会抛出连接异常

      ConnectException: Connection refused: connect

  2. 客户端和服务器之间进行数据传输,不能使用自己创建的流对象(只能和本地硬盘之间进行读写)。

    使用Socket对象中提供的网络流对象


TCP 通信的服务端:ServerSocket

作用:接收客户端的请求和客户端经过3次握手建立连接通路;读取客户端发送的数据,给客户端回写(发送)数据

表示服务器的类:java.net.ServerSocket;此类实现服务器套接字。

构造方法:

public ServerSocket(int port) // 创建绑定到特定端口的服务器套接字。

成员方法:

Socket accept()     // 侦听并接受到此套接字的连接。
/* 使用accpet方法,会一直监听客户端的请求
        有客户端请求服务器,accept方法就会获取到请求的客户端Socket对象
        没有客户端请求服务器,accept方法会进入到阻塞状态,一直等待
*/

注意:

​ 服务器启动的时候,抛出了以下的异常:说明服务器使用的端口号已经被占用了,需要更换端口号

​ java.net.BindException: Address already in use: JVM_Bind


文件上传案例

文件上传的客户端

读取本地文件,上传到服务器中,读取服务器回写的"上传成功!"

文件上传就是文件的复制:

​ 数据源:c:\1.jpg

​ 目的地:服务器中

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class Demo01TCPClient {
    public static void main(String[] args) throws IOException {
        //1.创建本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("c:\\1.jpg");
        
        //2.创建客户端Socket对象,构造方法绑定服务器的ip地址和端口号
        Socket socket = new Socket("127.0.0.1", 9999);
        
        //3.使用客户端Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //4.使用本地字节输入流FileInputStream对象中的方法read,读取要上传的而文件
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = fis.read(bytes)) != -1){
            //5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器中
            os.write(bytes, 0, len);
        }
        // 上传结束
        socket.shutdownOutput();
        
        //6.使用客户端Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //7.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的"上传成功!"
        while ((len = is.read(bytes)) != -1){
            System.out.println(new String(bytes, 0, len));
        }
        
        //8.释放资源(FileInputStream对象, Socket)
        fis.close();
        socket.close();
    }
}


文件上传的服务器端(多线程)

读取客户端上传的文件,把文件保存到服务器的硬盘上,给客户端回写"上传成功!"

文件上传就是文件的复制:

​ 数据源: 客户端上传的文件 1.jpg

​ 目的地: 服务器的硬盘中 d:\upload\1.jpg

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Demo02TCPServer {
    public static void main(String[] args) throws IOException {
        //1.判断d盘有没有upload文件夹,没有则创建
        File file = new File("d:\\upload");
        if(!file.exists()){
            file.mkdir();
        }
        //2.创建服务器ServerSocket对象,和系统要指定的端口号9999
        ServerSocket server = new ServerSocket(9999);
        
        // 一直循环监听客户端的请求(轮询)
        while(true){
             //3.使用服务器ServerSocket对象中的方法accpet,监听并获取请求的客户端Socket对象
            Socket socket = server.accept();
            
            // 开启一个新的线程完成这个客户端的文件上传
            new Thread(()->{
                try {
                    //4.使用客户端Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象
                    InputStream is = socket.getInputStream();
                    
                    /*
                        自定义一个文件的名称,防止名称的重复,覆盖之前的文件
                        规则:不重复 ==> 自己写 ==> 域名 + 毫秒值 + 随机数
                     */
                    String fileName = "cormorant" + System.currentTimeMillis() 
                        + new Random().nextInt(9999999) + ".jpg";
                    
                    //5.创建本地字节输出流FileOutputStream对象,绑定要输出的目的地
                    //FileOutputStream fos = new FileOutputStream(file + "\\1.jpg");  //d:\\upload\\1.jpg
                    FileOutputStream fos = new FileOutputStream(file + File.separator + fileName);
                    
                    //6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
                    byte[] bytes = new byte[1024];
                    int len = 0;
                    while ((len = is.read(bytes)) != -1){
                        //7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件,写到服务器的硬盘中保存
                        fos.write(bytes, 0, len);
                    }
                    
                    //8.使用客户端Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
                    //9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功!"
                    socket.getOutputStream().write("上传成功".getBytes());
            
                    //10.释放资源(fos, Socket, ServerScoket)
                    fos.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        
        //让服务器一直启动,不在关闭了
        //server.close();
    }
}


文件上传的阻塞问题

1594623812215.png

/*
    解决:上传完图片之后,给服务器写一个结束标记,告之服务器文件已经上传完毕,无需在等待了
    Socket对象中的方法
        void shutdownOutput() 禁用此套接字的输出流。
        对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列
 */
socket.shutdownOutput();


Socket 通信

概述

Linux 内核协议簇中有几十种通讯协议,AF-INET就是常见 TCP/IP 的通讯方式,AF-UNIX 是用于本机线程间通讯一种IPC机制,从用户角度看,所采用通讯模式相差不大,但就原理上看,相差较大。


AF_INET 域 socket 通信过程

典型的TCP/IP四层模型的通信过程:

20210708142718915.png

发送方、接收方依赖 IP:Port 来标识,即将本地的 socket 绑定到对应的 IP 端口上,发送数据时,指定对方的 IP 端口,经过Internet,可以根据此 IP 端口最终找到接收方;接收数据时,可以从数据包中获取到发送方的IP端口。

发送方通过系统调用 send() 将原始数据发送到操作系统内核缓冲区中。内核缓冲区从上到下依次经过TCP层、IP层、链路层的编码,分别添加对应的头部信息,经过网卡将一个数据包发送到网络中。经过网络路由到接收方的网卡。网卡通过系统中断将数据包通知到接收方的操作系统,再沿着发送方编码的反方向进行解码,即依次经过链路层、IP层、TCP层去除头部、检查校验等,最终将原始数据上报到接收方进程。


AF_UNIX 域 socket 通信过程

典型的本地 IPC,类似于管道,依赖路径名标识发送方和接收方。即发送数据时,指定接收方绑定的路径名,操作系统根据该路径名可以直接找到对应的接收方,并将原始数据直接拷贝到接收方的内核缓冲区中,并上报给接收方进程进行处理。同样的接收方可以从收到的数据包中获取到发送方的路径名,并通过此路径名向其发送数据。

20210708142816236.png


异同及应用场景

相同点

  • 操作系统提供的接口 socket(),bind(),connect(),accept(),send(),recv(),以及用来对其进行多路复用事件检测的 select(),poll(),epoll() 都是完全相同的。收发数据的过程中,上层应用感知不到底层的差别。


不同点

  • 建立 socket 传递的地址域,及bind()的地址结构稍有区别:

    • socket() 分别传递不同的域 AF_INET 和 AF_UNIX
    • bind() 的地址结构分别为sockaddr_in(制定IP端口)和 sockaddr_un(指定路径名)
  • AF_INET 需经过多个协议层的编解码,消耗系统cpu,并且数据传输需要经过网卡,受到网卡带宽的限制。AF_UNIX 数据到达内核缓冲区后,由内核根据指定路径名找到接收方socket对应的内核缓冲区,直接将数据拷贝过去,不经过协议层编解码,节省系统 cpu,并且不经过网卡,因此不受网卡带宽的限制。
  • AF_UNIX 的传输速率远远大于 AF_INET
  • AF_INET 不仅可以用作本机的跨进程通信,同样的可以用于不同机器之间的通信,其就是为了在不同机器之间进行网络互联传递数据而生。而 AF_UNIX 则只能用于本机内进程之间的通信。


应用场景

  • AF_UNIX 由于其对系统 cpu 的较少消耗,不受限于网卡带宽,及高效的传递速率,本机通信则首选 AF_UNIX 域
  • AF_INET 多用于跨机器之间的通信


AFUNIX Server Socket 通信

Java AFUNIXServerSocket类

参考:https://vimsky.com/examples/detail/java-class-org.newsclub.net.unix.AFUNIXServerSocket.html

依赖:

        <dependency>
            <groupId>com.kohlschutter.junixsocket</groupId>
            <artifactId>junixsocket-core</artifactId>
            <version>2.3.2</version>
        </dependency>
        <dependency>
            <groupId>com.kohlschutter.junixsocket</groupId>
            <artifactId>junixsocket-common</artifactId>
            <version>2.3.2</version>
        </dependency>


使用示例1:run

import org.newsclub.net.unix.AFUNIXServerSocket; 

@AllArgsConstructor
public class SocketJob implements Runnable{
    private String path;
    
    public void run() throws IOException {
        File socketFile = new File(path);
        socketFile.deleteOnExit();

        final ExecutorService executorService = Executors.newCachedThreadPool();

        try (AFUNIXServerSocket server = AFUNIXServerSocket.newInstance()) {
            // 绑定路径
            server.bind(new AFUNIXSocketAddress(socketFile));
            System.out.println("server: " + server);

            while (!Thread.interrupted()) {
                System.out.println("Waiting for connection...");
                executorService.execute(new ClientConnection(this, server.accept()));
            }
        } finally {
            executorService.shutdown();
        }
    }
}

使用示例2:main

import org.newsclub.net.unix.AFUNIXServerSocket; 

public static void main(String[] args) throws IOException {
    final File socketFile =
            new File(new File(System.getProperty("java.io.tmpdir")), "junixsocket-test.sock");

    try (AFUNIXServerSocket server = AFUNIXServerSocket.newInstance()) {
        server.bind(new AFUNIXSocketAddress(socketFile));
        System.out.println("server: " + server);

        while (!Thread.interrupted()) {
            System.out.println("Waiting for connection...");
            try (Socket sock = server.accept()) {
                System.out.println("Connected: " + sock);

                try (InputStream is = sock.getInputStream(); 
                     OutputStream os = sock.getOutputStream()) {
                    byte[] buf = new byte[128];
                    int read = is.read(buf);
                    System.out.println("Client's response: " + new String(buf, 0, read));

                    System.out.println("Saying hello to client " + os);
                    os.write("Hello, dear Client".getBytes());
                    os.flush();
                }
            }
        }
    }
}


socket 通讯消息接收

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;

/**
 * AF_UNIX 域 socket 通讯消息接收
 */
@AllArgsConstructor
@Slf4j
public class SocketMsgReceiveBO implements Runnable {

    /**
     * AF_UNIX 域 docker socket 客户端
     */
    private Socket socketClient;

    @Override
    public void run() {
        log.info("start socket client receive msg");

        if (socketClient == null || socketClient.isClosed()){
            log.error("socket client is unavailable");
            return;
        }

        StringBuffer acceptMsgBuffer = new StringBuffer();
        try (InputStream is = socketClient.getInputStream()) {
            byte[] buf = new byte[2048];
            int readLenth;
            // 退出当前循环依赖于socketClient端主动关闭链接,否则当前线程一致等待通讯
            while ((readLenth = is.read(buf)) != -1){
                acceptMsgBuffer.append(new String(buf, 0, readLenth, StandardCharsets.UTF_8));
                log.info("server accept msg:{}", acceptMsgBuffer);

                ArrayList<String> validJsonMsgList = new ArrayList<>();
                // 获取socket回调消息中的符合json格式的字符串
                String validJsonMsg = getValidJsonStrFromMsg(acceptMsgBuffer.toString());
                while (StringUtils.isNotBlank(validJsonMsg)){
                    // 添加到待解析消息集合中
                    validJsonMsgList.add(validJsonMsg);

                    /**
                     * 比较当前合法json格式的消息和原始消息的长度
                     * 若一致,则原始消息即为一个完整的符合json格式的消息,清空消息缓存Buffer
                     * 若不一致,则从原始消息的符合json格式片段的最后一个字符之后开始截取,等待拼接到下一次的消息
                     */
                    String originMsg = acceptMsgBuffer.toString();
                    if (validJsonMsg.length() == originMsg.length()){
                        acceptMsgBuffer = new StringBuffer();
                        break;
                    } else {
                        acceptMsgBuffer = new StringBuffer(originMsg.substring(validJsonMsg.length()));
                    }

                    log.info("all wait parse valid json msgs:{} and nextBuffer:{}", JSON.toJSONString(validJsonMsgList), acceptMsgBuffer);

                    // 解析合法的消息
                    validJsonMsgList.forEach(msg -> parseSocketMsg(msg));
                }

            }
        } catch (Exception e){
            log.error("failed to receive socket client msg", e);
        } finally {
            String msg = acceptMsgBuffer.toString();
            if (StringUtils.isNotBlank(msg) && isValidSocketMsg(msg)){
                log.info("finally parse last msg:{}", msg);
                parseSocketMsg(msg);
            }
            if (!socketClient.isClosed()){
                // 若socket客户端未自动关闭,则主动关闭
                try {
                    socketClient.close();
                    log.info("success to close socket client");
                } catch (IOException e){
                    log.error("failed to close socket client");
                }
            }
        }
    }

    /**
     * 获取socket回调消息中的合法json格式的字符串
     */
    private String getValidJsonStrFromMsg(String msg) {
        if (StringUtils.isBlank(msg)){
            return "";
        }
        /**
         * 为防止出现粘包现象,对该消息进行循环判断,一旦发现内部存在合法的json格式,即任务是有效的消息
         * 依赖每次对消息进行截取长度少1的方式,以保证当不能找到匹配的JSON也能退出循环
         */
        while (!isValidSocketMsg(msg) && StringUtils.isNoneBlank(msg)){
            // 从0开始截取到当前字符串的最后一个字符。substring(startIndex, endIndex)方法入参前闭后开区间
            msg = msg.substring(0, msg.length()-1);
            int lastLeftBraceIndex = msg.lastIndexOf("}");
            if (lastLeftBraceIndex < 0){
                // 当字符串中没有"}"字符时,退出循环
                break;
            }
            // 从0截取到字符串的最后一个"}"字符
            msg = msg.substring(0, lastLeftBraceIndex + 1);
        }
        if (isValidSocketMsg(msg)){
            return msg;
        }
        return "";
    }

    /**
     * 如果接收到的消息非空,且符合json格式,则认为是合法的消息,可以进行解析
     */
    private boolean isValidSocketMsg(String msg) {
        if(StringUtils.isBlank(msg)) return false;
        try {
            JSON.parse(msg);
            return true;
        } catch (Exception e){
            return false;
        }
    }

    /**
     * 解析符合json格式的消息
     */
    private void parseSocketMsg(String msgStr) {
        JSONObject msgJsonObj = JSON.parseObject(msgStr);
        // TODO 根据约定的通讯字段进行相关业务逻辑
    }
}
相关文章
|
18天前
|
机器学习/深度学习 人工智能 算法
深度学习入门:理解神经网络与反向传播算法
【9月更文挑战第20天】本文将深入浅出地介绍深度学习中的基石—神经网络,以及背后的魔法—反向传播算法。我们将通过直观的例子和简单的数学公式,带你领略这一技术的魅力。无论你是编程新手,还是有一定基础的开发者,这篇文章都将为你打开深度学习的大门,让你对神经网络的工作原理有一个清晰的认识。
|
21天前
|
开发者 Python
Python Socket编程:不只是基础,更有进阶秘籍,让你的网络应用飞起来!
在数字时代,网络应用成为连接世界的桥梁。Python凭借简洁的语法和丰富的库支持,成为开发高效网络应用的首选。本文通过实时聊天室案例,介绍Python Socket编程的基础与进阶技巧。基础篇涵盖服务器和客户端的建立与数据交换;进阶篇则探讨多线程与异步IO优化方案,助力提升应用性能。通过本案例,你将掌握Socket编程的核心技能,推动网络应用飞得更高、更远。
32 1
|
13天前
|
域名解析 网络协议 应用服务中间件
网络编程入门如此简单(四):一文搞懂localhost和127.0.0.1
本文将以网络编程入门者视角,言简意赅地为你请清楚localhost和127.0.0.1的关系及区别等。
18 2
网络编程入门如此简单(四):一文搞懂localhost和127.0.0.1
|
4天前
|
网络协议 Linux 网络性能优化
Linux基础-socket详解、TCP/UDP
综上所述,Linux下的Socket编程是网络通信的重要组成部分,通过灵活运用TCP和UDP协议,开发者能够构建出满足不同需求的网络应用程序。掌握这些基础知识,是进行更复杂网络编程任务的基石。
21 1
|
8天前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习中的卷积神经网络(CNN)入门与实践
【8月更文挑战第62天】本文以浅显易懂的方式介绍了深度学习领域中的核心技术之一——卷积神经网络(CNN)。文章通过生动的比喻和直观的图示,逐步揭示了CNN的工作原理和应用场景。同时,结合具体的代码示例,引导读者从零开始构建一个简单的CNN模型,实现对图像数据的分类任务。无论你是深度学习的初学者还是希望巩固理解的开发者,这篇文章都将为你打开一扇通往深度学习世界的大门。
|
19天前
|
机器学习/深度学习 人工智能 算法
深度学习中的卷积神经网络(CNN)入门与实践
【9月更文挑战第19天】在这篇文章中,我们将探索深度学习的一个重要分支——卷积神经网络(CNN)。从基础概念出发,逐步深入到CNN的工作原理和实际应用。文章旨在为初学者提供一个清晰的学习路径,并分享一些实用的编程技巧,帮助读者快速上手实践CNN项目。
|
17天前
|
网络协议 Python
网络世界的建筑师:Python Socket编程基础与进阶,构建你的网络帝国!
在数字宇宙中,网络如同复杂脉络连接每个角落,Python Socket编程则是开启这一世界的钥匙。本文将引导你从基础概念入手,逐步掌握Socket编程,并通过实战示例构建TCP/UDP服务器与客户端。你将学会使用Python的socket模块进行网络通信,了解TCP与UDP的区别,并运用多线程与异步IO提升服务器性能。跟随本文指引,成为网络世界的建筑师,构建自己的网络帝国。
26 2
|
18天前
|
网络协议 Python
告别网络编程迷雾!Python Socket编程基础与实战,让你秒变网络达人!
在网络编程的世界里,Socket编程是连接数据与服务的关键桥梁。对于初学者,这往往是最棘手的部分。本文将用Python带你轻松入门Socket编程,从创建TCP服务器与客户端的基础搭建,到处理并发连接的实战技巧,逐步揭开网络编程的神秘面纱。通过具体的代码示例,我们将掌握Socket的基本概念与操作,让你成为网络编程的高手。无论是简单的数据传输还是复杂的并发处理,Python都能助你一臂之力。希望这篇文章成为你网络编程旅程的良好开端。
37 3
|
17天前
|
网络协议 开发者 Python
网络编程小白秒变大咖!Python Socket基础与进阶教程,轻松上手无压力!
在网络技术飞速发展的今天,掌握网络编程已成为开发者的重要技能。本文以Python为工具,带你从Socket编程基础逐步深入至进阶领域。首先介绍Socket的概念及TCP/UDP协议,接着演示如何用Python创建、绑定、监听Socket,实现数据收发;最后通过构建简单的聊天服务器,巩固所学知识。让初学者也能迅速上手,成为网络编程高手。
51 1
|
24天前
|
机器学习/深度学习 人工智能 TensorFlow
深度学习入门:理解卷积神经网络(CNN)
【9月更文挑战第14天】本文旨在为初学者提供一个关于卷积神经网络(CNN)的直观理解,通过简单的语言和比喻来揭示这一深度学习模型如何识别图像。我们将一起探索CNN的基本组成,包括卷积层、激活函数、池化层和全连接层,并了解它们如何协同工作以实现图像分类任务。文章末尾将给出一个简单的代码示例,帮助读者更好地理解CNN的工作原理。
40 7