springBoot整合harbor

2023-09-22 14:42:18
<docker.version>3.2.13</docker.version>
<fastjson.version>1.2.75</fastjson.version>

<dependencies>
        <dependency>
            <groupId>com.github.docker-java</groupId>
            <artifactId>docker-java-core</artifactId>
            <version>${docker.version}</version>
        </dependency>
        <dependency>
            <groupId>com.github.docker-java</groupId>
            <artifactId>docker-java-transport-httpclient5</artifactId>
            <version>${docker.version}</version>
        </dependency>
        <dependency>
            <groupId>com.github.docker-java</groupId>
            <artifactId>docker-java</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.glassfish.jersey.connectors</groupId>
                    <artifactId>jersey-apache-connector</artifactId>
                </exclusion>
            </exclusions>
            <version>${docker.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
    </dependencies>
import com.alibaba.fastjson.JSON;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.BuildImageCmd;
import com.github.dockerjava.api.command.BuildImageResultCallback;
import com.github.dockerjava.api.command.LoadImageCmd;
import com.github.dockerjava.api.command.TagImageCmd;
import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import com.sunyard.staging.starter.harbor.properties.HarborProperties;
import com.sunyard.staging.starter.harbor.utils.AesCbcUtil;
import com.sunyard.staging.starter.harbor.utils.UnCompress;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class HarborApiClient {
    @Autowired
    private HarborProperties harborProperties;

    /**
     * 构建DocekrClient实例
     *
     * @return
     */
    public DockerClient getDocekrClient() throws Exception {
        DefaultDockerClientConfig dockerClientConfig = null;
        if (harborProperties.isDockertlsverify()) {
            dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder()
                    .withDockerHost(harborProperties.getDockerhost())
                    .withDockerTlsVerify(true)
                    .withDockerCertPath(harborProperties.getDockerCertPath())
                    .withRegistryUsername(harborProperties.getRegistryusername())
                    .withRegistryPassword(AesCbcUtil.Decrypt(harborProperties.getRegistrypassword()))
                    .withRegistryUrl(harborProperties.getRegistryurl())
                    .build();
        } else {
            dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder()
                    .withDockerHost(harborProperties.getDockerhost())
                    .withDockerTlsVerify(false)
                    .withDockerCertPath(harborProperties.getDockerCertPath())
                    .withRegistryUsername(harborProperties.getRegistryusername())
                    .withRegistryPassword(AesCbcUtil.Decrypt(harborProperties.getRegistrypassword()))
                    .withRegistryUrl(harborProperties.getRegistryurl())
                    .build();
        }
        DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
                .dockerHost(dockerClientConfig.getDockerHost())
                .sslConfig(dockerClientConfig.getSSLConfig())
                .build();
        return DockerClientImpl.getInstance(dockerClientConfig, httpClient);
    }


    /**
     * 构建DocekrClient实例
     *
     * @param dockerIpPort tcp://172.1.0.154:2375
     * @param dockerCertPath docker安全证书配置路径
     * @param dockerTlsVerify docker是否需要TLS认证
     * @return
     * @throws Exception
     */
    public DockerClient getDockerClient(String dockerIpPort, String dockerCertPath, boolean dockerTlsVerify) throws Exception {
        DefaultDockerClientConfig dockerClientConfig = null;
        if (dockerTlsVerify) {
            dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder()
                    .withDockerHost(StringUtils.isNotBlank(dockerIpPort) ? dockerIpPort : harborProperties.getDockerhost())
                    .withDockerTlsVerify(true)
                    .withDockerCertPath(StringUtils.isNotBlank(dockerCertPath) ? dockerCertPath : harborProperties.getDockerCertPath())
                    .withRegistryUsername(harborProperties.getRegistryusername())
                    .withRegistryPassword(AesCbcUtil.Decrypt(harborProperties.getRegistrypassword()))
                    .withRegistryUrl(harborProperties.getRegistryurl())
                    .build();
        } else {
            dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder()
                    .withDockerHost(StringUtils.isNotBlank(dockerIpPort) ? dockerIpPort : harborProperties.getDockerhost())
                    .withDockerTlsVerify(false)
                    .withDockerCertPath(StringUtils.isNotBlank(dockerCertPath) ? dockerCertPath : harborProperties.getDockerCertPath())
                    .withRegistryUsername(harborProperties.getRegistryusername())
                    .withRegistryPassword(AesCbcUtil.Decrypt(harborProperties.getRegistrypassword()))
                    .withRegistryUrl(harborProperties.getRegistryurl())
                    .build();
        }
        DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
                .dockerHost(dockerClientConfig.getDockerHost())
                .sslConfig(dockerClientConfig.getSSLConfig())
                .build();
        return DockerClientImpl.getInstance(dockerClientConfig, httpClient);
    }

    /**
     * 获取docker基础信息
     *
     * @param dockerClient
     * @return
     */
    public static String getDockerInfo(DockerClient dockerClient) {
        Info info = dockerClient.infoCmd().exec();
        return JSON.toJSONString(info);
    }

    /**
     * 给镜像打标签
     *
     * @param dockerClient
     * @param imageIdOrFullName
     * @param respository
     * @param tag
     */
    public static void tagImage(DockerClient dockerClient, String imageIdOrFullName, String respository, String tag) {
        TagImageCmd tagImageCmd = dockerClient.tagImageCmd(imageIdOrFullName, respository, tag);
        tagImageCmd.exec();
    }

    /**
     * load镜像
     *
     * @param dockerClient
     * @param inputStream
     */
    public static void loadImage(DockerClient dockerClient, InputStream inputStream) {
        LoadImageCmd loadImageCmd = dockerClient.loadImageCmd(inputStream);
        loadImageCmd.exec();
    }

/**
     * Description 从harbor中拉取镜像到指定服务端的docker环境
     *
     * @date 2023/9/21 15:24
     * @param: dockerIpPort  "tcp://172.1.1.74:2375"
     * @param: dockerCertPath 安全证书路径
     * @param: dockerTlsVerify是否使用ssl
     * @param: imagePathInHarbor 镜像再harbor的路径 去除docker pull后的路径值
     * @return: boolean
     */
    private boolean harborImagePull(String dockerIpPort, String dockerCertPath, boolean dockerTlsVerify, String imagePathInHarbor) {
        try {
            DockerClient dockerClient = harborApiClient.getDockerClient(dockerIpPort, dockerCertPath, dockerTlsVerify);
            PullImageResultCallback exec = new PullImageResultCallback() {
                @Override
                public void onNext(PullResponseItem item) {
                    //用于进行镜像拉取的回调数据显示,item会显示镜像拉取的layer每层的状态
                }
            };
            // harbor服务器连接认证配置
            AuthConfig authConfig = new AuthConfig()
                    .withRegistryAddress(harborProperties.getRegistryurl() + ":" + harborProperties.getPort())
                    .withUsername(harborProperties.getRegistryusername())
                    .withPassword(AesCbcUtil.Decrypt(harborProperties.getRegistrypassword()));
            // 直接通过docker pull命令拉取时如果docker中未配置harbor密钥信息时不指定端口80,否则docker 中执行命令时将会失败
            dockerClient.pullImageCmd(imagePathInHarbor).withAuthConfig(authConfig).exec(exec)
                    .awaitCompletion(30, TimeUnit.MINUTES);
            exec.close();
            return true;
        } catch (Exception e) {
            logger.error("harbor镜像拉取失败:", e);
            return false;
        }
    }

    /**
     * 推送镜像
     *
     * @param dockerClient
     * @param imageName
     * @return
     * @throws InterruptedException
     */
    public static Boolean pushImage(DockerClient dockerClient, String imageName) throws InterruptedException {
        final Boolean[] result = {true};
        ResultCallback.Adapter<PushResponseItem> callBack = new ResultCallback.Adapter<PushResponseItem>() {
            @Override
            public void onNext(PushResponseItem pushResponseItem) {
                if (pushResponseItem != null) {
                    ResponseItem.ErrorDetail errorDetail = pushResponseItem.getErrorDetail();
                    if (errorDetail != null) {
                        result[0] = false;
                        log.error(errorDetail.getMessage(), errorDetail);
                    }
                }
                super.onNext(pushResponseItem);
            }
        };
        dockerClient.pushImageCmd(imageName).exec(callBack).awaitCompletion();
        return result[0];
    }

    /**
     * 上传镜像
     *
     * @param file      镜像文件
     * @param imageName
     * @throws Exception
     */
    public void uploadImage(DockerClient dockerClient, MultipartFile file, String imageName) throws Exception {
        InputStream inputStream = null;
        //获取加载镜像的名称
        String uploadImageName = "";
        try {
            inputStream = file.getInputStream();
            String password = AesCbcUtil.Decrypt(harborProperties.getRegistrypassword());
            //Harbor登录信息
            AuthConfig autoConfig =
                    new AuthConfig().withRegistryAddress(harborProperties.getRegistryurl()).
                            withUsername(harborProperties.getRegistryusername()).withPassword(password);
            //加载镜像
            dockerClient.loadImageCmd(inputStream).exec();
            String imageFile = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."));
            String imageId = imageFile.substring(imageFile.lastIndexOf("_") + 1);
            List<Image> list = dockerClient.listImagesCmd().exec();
            for (Image image : list) {
                // 压缩包可能包含多个对象,只导入指定镜像
                if (image.getId().contains(imageId)) {
                    uploadImageName = image.getRepoTags()[0];
                    break;
                }
            }
            //镜像打tag
            dockerClient.tagImageCmd(uploadImageName, imageName, imageName.split(":")[2]).exec();
            //push至镜像仓库
            dockerClient.pushImageCmd(imageName).withAuthConfig(autoConfig).start().awaitCompletion(30, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("上传镜像出现异常", e);
        } finally {
            //push成功后,删除本地加载的镜像
            dockerClient.removeImageCmd(imageName).exec();
            dockerClient.removeImageCmd(uploadImageName).exec();
            //关闭文件流,这个应该使用try-catch 的try中
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    /**
     * 从镜像的tar文件中获取镜像名称
     *
     * @param imagePath
     * @return
     */
    public static String getImageName(String imagePath) {
        try {
            return UnCompress.getImageName(imagePath);
        } catch (Exception e) {
            log.error("从镜像的tar文件中获取镜像名称出现异常!", e);
            return null;
        }
    }

    /**
     * 通过dockerFile构建镜像
     *
     * @param dockerClient
     * @param dockerFile
     * @return
     */
    public static String buildImage(DockerClient dockerClient, File dockerFile) {
        BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(dockerFile);
        BuildImageResultCallback buildImageResultCallback = new BuildImageResultCallback() {
            @Override
            public void onNext(BuildResponseItem item) {
                log.info("{}", item);
                super.onNext(item);
            }
        };
        return buildImageCmd.exec(buildImageResultCallback).awaitImageId();
    }

    /**
     * 获取镜像列表
     *
     * @param dockerClient
     * @return
     */
    public static List<Image> imageList(DockerClient dockerClient) {
        List<Image> imageList = dockerClient.listImagesCmd().withShowAll(true).exec();
        return imageList;
    }
}
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Setter
@Getter
@Configuration
@ConfigurationProperties(prefix = "spring.harbor")
public class HarborProperties {

    // docker服务端IP地址
    public String dockerhost = "tcp://127.0.0.1:2375";
    // docker安全证书配置路径
    public String dockerCertPath = "";
    // docker是否需要TLS认证
    public boolean dockertlsverify = false;
    // Harbor仓库的IP
    public String registryurl = "127.0.0.1:80";
    // Harbor仓库的登录用户名
    public String registryusername = "sungrid";
    // Harbor仓库的登录密码 当前密码进行了加密处理
    public String registrypassword = "h8Wj3V01WWbBotV72XFJ38Js2nUD3Jj6Mc9K";
    // docker 镜像部署服务器地址
    public String dockerservicemachine = "172.1.0.154:22";

    public String dockerservicemachineusername = "root";

    public String dockerservicemachinepassword = "Xydxnj220406!";

    public int port = 80;

}


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

@Slf4j
public class UnCompress {
    private static final String BASE_DIR = "";
    // 符号"/"用来作为目录标识判断符
    private static final String PATH = File.separator;
    private static final int BUFFER = 1024;
    private static final String EXT = ".tar";

    /**
     * 归档
     * @param srcPath
     * @param destPath
     * @throws Exception
     */
    public static void archive(String srcPath, String destPath) throws Exception {
        File srcFile = new File(srcPath);
        archive(srcFile, destPath);
    }

    /**
     * 归档
     * @param srcFile 源路径
     * @param destFile 目标路径
     * @throws Exception
     */
    public static void archive(File srcFile, File destFile) throws Exception {
        TarArchiveOutputStream taos = new TarArchiveOutputStream(new FileOutputStream(destFile));
        archive(srcFile, taos, BASE_DIR);
        taos.flush();
        taos.close();
    }

    /**
     * 归档
     * @param srcFile
     * @throws Exception
     */
    public static void archive(File srcFile) throws Exception {
        String name = srcFile.getName();
        String basePath = srcFile.getParent();
        String destPath = basePath+File.separator + name + EXT;
        archive(srcFile, destPath);
    }

    /**
     * 归档文件
     * @param srcFile
     * @param destPath
     * @throws Exception
     */
    public static void archive(File srcFile, String destPath) throws Exception {
        archive(srcFile, new File(destPath));
    }

    /**
     * 归档
     * @param srcPath
     * @throws Exception
     */
    public static void archive(String srcPath) throws Exception {
        File srcFile = new File(srcPath);
        archive(srcFile);
    }

    /**
     * 归档
     * @param srcFile 源路径
     * @param taos TarArchiveOutputStream
     * @param basePath 归档包内相对路径
     * @throws Exception
     */
    private static void archive(File srcFile, TarArchiveOutputStream taos, String basePath) throws Exception {
        if (srcFile.isDirectory()) {
            archiveDir(srcFile, taos, basePath);
        } else {
            archiveFile(srcFile, taos, basePath);
        }
    }

    /**
     * 目录归档
     * @param dir
     * @param taos TarArchiveOutputStream
     * @param basePath
     * @throws Exception
     */
    private static void archiveDir(File dir, TarArchiveOutputStream taos, String basePath) throws Exception {
        File[] files = dir.listFiles();
        if (files.length < 1) {
            TarArchiveEntry entry = new TarArchiveEntry(basePath + dir.getName() + PATH);
            taos.putArchiveEntry(entry);
            taos.closeArchiveEntry();
        }
        for (File file : files) {
            // 递归归档
            archive(file, taos, basePath + dir.getName() + PATH);
        }
    }

    /**
     * 归档内文件名定义
     * <pre>
     * 如果有多级目录,那么这里就需要给出包含目录的文件名
     * 如果用WinRAR打开归档包,中文名将显示为乱码
     * </pre>
     */
    private static void archiveFile(File file, TarArchiveOutputStream taos, String dir) throws Exception {
        TarArchiveEntry entry = new TarArchiveEntry(dir + file.getName());
        //如果打包不需要文件夹,就用 new TarArchiveEntry(file.getName())
        entry.setSize(file.length());
        taos.putArchiveEntry(entry);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        int count;
        byte data[] = new byte[BUFFER];
        while ((count = bis.read(data, 0, BUFFER)) != -1) {
            taos.write(data, 0, count);
        }
        bis.close();
        taos.closeArchiveEntry();
    }

    /**
     * 解归档
     * @param srcFile
     * @throws Exception
     */
    public static void dearchive(File srcFile) throws Exception {
        String basePath = srcFile.getParent();
        dearchive(srcFile, basePath);
    }

    /**
     * 解归档
     * @param srcFile
     * @param destFile
     * @throws Exception
     */
    public static void dearchive(File srcFile, File destFile) throws Exception {
        TarArchiveInputStream tais = new TarArchiveInputStream(new FileInputStream(srcFile));
        dearchive(destFile, tais);
        tais.close();
    }

    /**
     * 解归档
     * @param srcFile
     * @param destPath
     * @throws Exception
     */
    public static void dearchive(File srcFile, String destPath) throws Exception {
        dearchive(srcFile, new File(destPath));
    }

    /**
     * 文件 解归档
     * @param destFile 目标文件
     * @param tais ZipInputStream
     * @throws Exception
     */
    private static void dearchive(File destFile, TarArchiveInputStream tais) throws Exception {
        TarArchiveEntry entry = null;
        while ((entry = tais.getNextTarEntry()) != null) {
            // 文件
            String dir = destFile.getPath() + File.separator + entry.getName();
            File dirFile = new File(dir);
            // 文件检查
            fileProber(dirFile);
            if (entry.isDirectory()) {
                dirFile.mkdirs();
            } else {
                dearchiveFile(dirFile, tais);
            }
        }
    }

    /**
     * 文件 解归档
     * @param srcPath 源文件路径
     * @throws Exception
     */
    public static void dearchive(String srcPath) throws Exception {
        File srcFile = new File(srcPath);
        dearchive(srcFile);
    }

    /**
     * 文件 解归档
     * @param srcPath 源文件路径
     * @param destPath 目标文件路径
     * @throws Exception
     */
    public static void dearchive(String srcPath, String destPath) throws Exception {
        File srcFile = new File(srcPath);
        dearchive(srcFile, destPath);
    }

    /**
     * 文件解归档
     * @param destFile 目标文件
     * @param tais TarArchiveInputStream
     * @throws Exception
     */
    private static void dearchiveFile(File destFile, TarArchiveInputStream tais) throws Exception {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
        int count;
        byte data[] = new byte[BUFFER];
        while ((count = tais.read(data, 0, BUFFER)) != -1) {
            bos.write(data, 0, count);
        }
        bos.close();
    }

    /**
     * 文件探针
     * <pre>
     * 当父目录不存在时,创建目录!
     * </pre>
     * @param dirFile
     */
    private static void fileProber(File dirFile) {
        File parentFile = dirFile.getParentFile();
        if (!parentFile.exists()) {
            // 递归寻找上级目录
            fileProber(parentFile);
            parentFile.mkdir();
        }
    }

    public static String getImageName(String tempFilePath) throws Exception{
        String dirPath = tempFilePath.substring(0, tempFilePath.lastIndexOf("."));
        File dirFile = new File(dirPath);
        if (!dirFile.exists()) {
            dirFile.mkdirs();
        }
        UnCompress.dearchive(tempFilePath, dirPath);
        if (!dirPath.endsWith(File.separator)) {
            dirPath += File.separator;
        }
        //目标文件
        String destFilePath = dirPath + "manifest.json";
        File destFile = new File(destFilePath);
        if (!destFile.exists()) {
            return null;
        }
        StringBuilder sb = new StringBuilder("");
        InputStream io = new FileInputStream(new File(destFilePath));
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = io.read(bytes)) > 0) {
            sb.append(new String(bytes, 0, len));
        }
        String content = sb.toString();
        //只取第一个配置项
        List<JSONObject> jsonList = (List<JSONObject>) JSONArray.parse(content);
        System.out.println("jsonList = " + jsonList);
        return ((List<String>)jsonList.get(0).get("RepoTags")).get(0);
    }
}
更多推荐

Docker 原理

基础技术试验进程linuxnamespaceMount(mnt)namespace可以在不影响宿主机文件系统的基础上挂载或者取消挂载文件系统了;PID(ProcessID)在一个pidnamespace中,第一个进程的pid为1,所有在此namespace中的其他进程都是此进程的子进程,操作系统级别的其他进程不需要在此

kruskal重构树

Kruskal重构树第二次学了。大致思路就是在最小生成树加边的时候,把每条边也设一个点,和他连着的两个点连边。由于最小生成树的贪心,感觉很像哈夫曼树,有性质是经过的边的长度(已经转化为点权)越向上越大/越小,取决于生成树的排序。那就可以通过倍增找不经过长度超过x的边所能走到的所有点,实际上是一直往上跳的那个子树。for

红海云签约科百特,“膜界”小巨人人力资源数字化转型全面提速

杭州科百特过滤器材有限公司(以下简称“科百特”)是全球优秀的创新过滤企业,专注于纳米膜材料的开发和应用,致力于为全球集成电路制造和生物制药产业提供创新的过滤纯化解决方案。近日,科百特与广州红海云计算股份有限公司达成战略合作。红海云作为一家技术驱动的专业型厂商,基于深厚的人力资源管理专业积累和前沿的数字技术,成功打造了众

Linux CentOS7系统运行级别

运行级别就是Linux操作系统当前正在运行的功能级别。在早期系统设置中,共设有七个运行级别,编号从0到6。系统可以引导到任何给定的运行级别。每个运行级别指定不同的系统配置,并允许访问不同的进程组合。默认情况下,Linux会引导至运行级别3或运行级别5。启动时一次只执行一个运行级别,它不会一个接一个地执行。在SysVin

Buffer Pool

一.BufferPool的含义BufferPool:缓冲池,简称BP,其作用是用来缓存表数据与索引数据,减少磁盘IO操作,提升效率。当Mysql执行查询的sql语句的时候,会先去缓存当中看是否有对应的数据,如果有则直接读内存,如果没有则去磁盘里拿,然后把数据放到BufferPool里拿,来提高查询速度。BufferPo

Unity减少发布打包文件的体积——获取精灵图片的信息限制它的大小

一、起因一个工程,打包成webGL且压缩成zip文件后,接近400M,后来把大的精灵图片设置最大尺寸,降低大小后,再次发布,zip文件缩减到250M二、如何一键获得工程里面的精灵图片信息三、获取精灵图片信息1、查找项目中的所有精灵图片//查找工程文件中的所有精灵图片string[]guids=AssetDatabase

2023年中秋·国庆节放假通知

放假期间如有业务、技术及其他相关需求,欢迎新老客户前来咨询!放假期间如给您带来不便敬请谅解!注意事项1、放假离开公司前须全面清扫所属办公区域的卫生,收拾好桌面、保管好个人办公物品;2、关闭个人电脑等办公设备的电源,切断用电设备的插座电源开关,锁好门窗防雨,做好防火防盗工作;3、节日期间重点安全部门,如仓库,要加强对仓库

【计算机网络】网络编程接口 Socket API 解读(5)

Socket是网络协议栈暴露给编程人员的API,相比复杂的计算机网络协议,API对关键操作和配置数据进行了抽象,简化了程序编程。本文讲述的socket内容源自Linuxman。本文主要对各API进行详细介绍,从而更好的理解socket编程。connectconnect()遵循POSIX.1-20081.库标准c库,li

IP归属地应用的几个主要特点

作为一款优秀的IP地址定位工具,主题IP归属地的应用无疑是最好的选择之一。该应用可以将您需要查询的IP地址快速定位到所在的具体物理位置,并提供详细的地址和地图信息。接下来,让我们一起来看一看IP归属地应用的几个主要特点:1.快速查询IP地址归属地主题IP归属地应用的查询速度非常快,您只需要输入需要查询的IP地址点击“查

微软宣布推广数字助理 Copilot;GPT 应用开发和思考

🦉AI新闻🚀微软宣布推广基于生成式人工智能的数字助理Copilot摘要:微软宣布将基于生成式人工智能的数字助理Copilot推广到更多软件产品中。新的AI助理MicrosoftCopilot将在Windows中无缝可用,包括Windows11桌面、Microsoft365、Outlook、Edge浏览器和必应。它能

【Linux网络编程】序列化与反序列化

我们网络收发数据实际上只能接收到字符串,但是在现实生活中描述一个客观物体都是以很多属性来描述的,所以在网络中结构体类型的数据更常见,那我们如何发送结构体数据呢?这里就涉及到协议的概念了。我们想象一个场景,在特种兵执行任务时,他们有特定的战术手语,这样他们就能根据手语进行相应的战术配合了。所以协议也是一样,客户端和服务器

热文推荐