47 tcp网络程序

news2024/12/22 14:56:42

网路聊天

API详解

下面用到的API,都在sys/socket.h中

socket ():
在这里插入图片描述

  • socket() 打开一个网络通讯端口,如果成功的话,就像open() 一样返回一个文件描述符
  • 应用程序可以像读文件一样用read/write在网络上收发数据
  • 如果调用出错返回-1
  • 对于IPv4,family参数指定为AF_INET
  • 对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议
  • protocol参数是传输层协议,填0默认匹配

bind ()
在这里插入图片描述

  • 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后,就可以向服务器发起连接,服务器需要调用bind绑定一个固定的网络地址和端口号
  • bind() 成功返回0,失败返回-1
  • 作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听nyaddr所描述的地址和端口号
  • struct sockaddr* 是一个通用指针类型,myaddr参数实际上可以接收各种协议 的sockaddr结构体,而他们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度

myaddr初始化:
1.整个结构体清零
2.设置地址类型为AF_INET
3.网络地址为INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个ip地址
4.端口号为SERV_PORT,可以定义为9999

listen ()
在这里插入图片描述

  • listen() 声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接等待状态,如果接收到更多的连接请求就忽略,这里设置不会太大(一般是5)
  • listen() 成功返回0,失败返回-1

accept ()

在这里插入图片描述

  • 三次握手完成后,服务器调用accept接收连接
  • 如果服务器调用accept时还没有客户端的 连接请求,就阻塞等待直到有客户端连接上来
  • addr是一个传出参数,accept返回时传出客户端的地址和端口号
  • 如果给addr传递NULL,表示不关心客户端的地址
  • addrlen参数是一个传入传出参数(value-result arument),传入的是调用者提供的,缓冲区addr的长度以避免缓冲区溢出问题,传出的是客户端地质结构体的实际长度(有可能没有占满调用者提供的缓冲区)

connect ()
在这里插入图片描述

  • 客户端需要调用connect连接服务器
  • connct和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址
  • 成功返回0,出错返回-1

服务端

成员变量三个,分别是listen套接字,ip和端口号,服务端的ip和端口号必须固定
在这里插入图片描述
构造函数传入ip和地址初始化成员变量
在这里插入图片描述
init函数初始化套接字信息,绑定到设备上,和前面的udp一样
因为tcp是面向字节流的,一般比较被动,处于一种等待连接来的状态,所以要设置等待连接,listen状态:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

run函数开始接收消息,创建和套接字的连接,返回新的套接字描述符,用来后面通讯

为什么会有两个套接字
这个就像饭店拉客的例子,饭店门口有一个吆喝顾客进去吃饭的服务员,当接受这个建议后,进到店里,会重新分配一个服务员提供后续服务,点餐等,吆喝的服务员又会继续在门口等待下一个顾客

listen套接字是拉客的服务员,确保服务端需要通信,后续的发送和接收由accept创建的新套接字完成

在这里插入图片描述
service函数发送和接收消息,传入套接字,ip和port用来显示用户。tcp字节流读写可以直接用read和write函数
在这里插入图片描述

测试
开启服务端,服务端使用telnet命令,使用本机环回地址连接

telnet [ip] [端口]
在这里插入图片描述

接着输入^]就可以输入消息
在这里插入图片描述

#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "log.hpp"

string defaultip = "0.0.0.0";
uint16_t defaultport = 8000;
const int back = 2;  //连接请求队列个数,一般2-4

Log log;

enum
{
    SOCK_ERR = 1,
    BIND_ERR,
    LIStEN_ERR
};

class server
{
public:
    server(const string ip = defaultip, const uint16_t port = defaultport)
    {
        _ip = defaultip;
        _port = defaultport;
    }

    void init()
    {
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            log.logmessage(fatal, "socket create error");
            exit(SOCK_ERR);
        }
        log.logmessage(info, "socket create success:%d", _listensock);

        sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        inet_aton(_ip.c_str(), &local.sin_addr);
        local.sin_port = htons(_port);

        //绑定
        if (bind(_listensock, (const struct sockaddr*)&local, sizeof(local)) < 0)
        {
            log.logmessage(fatal, "bind error");
            exit(BIND_ERR);
        }
        log.logmessage(fatal, "bind success");

        //tcp面向连接,服务器一般被动,处于一种等待连接到来的状态
        // 设置listen状态
        if (listen(_listensock, back) < 0)
        {
            log.logmessage(fatal, "listen error");
            exit(LIStEN_ERR);
        }
    }

    void service(int fd, const string ip, const uint16_t port)
    {
        cout << "welcome " << ip << endl;
        char message[1024];
        while (true)
        {    
            ssize_t n = read(fd, message, sizeof(message) - 1);
            if (n > 0)
            {
                message[n] = 0;
                //数据处理
                string echo_string;
                echo_string = "[" + ip + ":" + to_string(port) + "]: " + message;
                cout << echo_string << endl;

                write(fd, echo_string.c_str(), echo_string.size());
            }
            
        }

    }

    void run()
    {
        while (true)
        {
            //获取链接
            sockaddr_in sock;
            socklen_t len;
            int sockfd = accept(_listensock, (sockaddr *)&sock, &len);
            if (sockfd < 0)
            {
                log.logmessage(warning, "accept fail");
                continue;
            }

            char buff[32];
            string clientip = inet_ntop(AF_INET, &sock.sin_addr, buff, len);
            uint16_t clientport = ntohs(sock.sin_port);

            service(sockfd, clientip, clientport);

            close(sockfd);
        }
    }

    ~server()
    {
        close(_listensock);
    }

private:
    int _listensock;
    string _ip;
    uint16_t _port;
};

客户端

初始化addr协议内容
在这里插入图片描述套接字
在这里插入图片描述

conncect函数建立连接
在这里插入图片描述

读写消息
在这里插入图片描述
在这里插入图片描述

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <fcntl.h>

using namespace std;

// string path = "/dev/pts/5";
// struct thread_data
// {
//     int _sockfd;
//     struct sockaddr_in _server;
//     string _ip;
// };

// void *send_message(void * temp)
// {
//     thread_data *td = (struct thread_data*)temp;
//     string message;
//     cout << "welcome " << td->_ip << endl;
//     while (true)
//     {
//         cout << "please enter: ";
//         getline(cin, message);
//         sendto(td->_sockfd, message.c_str(), message.size(), 0, (const sockaddr *)&td->_server, sizeof(td->_server));
//     }
    
// }

// void* recv_message(void* temp)
// {
//     // int fd = open(path.c_str(), O_WRONLY);
//     // if (fd < 0)
//     // {
//     //     perror("open");
//     // }

//     // dup2(fd, 2);

//     thread_data *td = (struct thread_data *)temp;
//     char buff[1024];
//     sockaddr_in rec;
//     socklen_t len = sizeof(rec);
//     while (true)
//     {
//         ssize_t s = recvfrom(td->_sockfd, buff, sizeof(buff) - 1, 0, (sockaddr *)&rec, &len);
//         if (s > 0)
//         {
//             buff[s] = 0;
//             cerr << buff << endl;
//         }
//     }

//     //close(fd);
// }

int main()
{
    //thread_data td;
    //ip和port可以通过命令行参数传入
    uint16_t port = 8000;
    string ip = "ip";
    struct sockaddr_in server;
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(ip.c_str());
    server.sin_port = htons(port);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        cout << "socket error" << endl;
    }

    //建立连接
    if (connect(sockfd, (const struct sockaddr*)&server, sizeof(server)) < 0)
    {
        cout << "connect error" << endl;
    }
    cout << "connect success" << endl;
    // 通信
    string message;
    char buff[1024];
    while (true)
    {
        cout << "please enter:";
        getline(cin, message);
        write(sockfd, message.c_str(), message.size());

        ssize_t n = read(sockfd, buff, sizeof(buff) - 1);
        if (n > 0)
        {
            buff[n] = 0;
            cout << buff << endl;
        }
    }

    // client 需要绑定,但不显示绑定,由os自由选择
    //  pthread_t tid_send;
    //  pthread_t tid_recv;
    //  pthread_create(&tid_send, nullptr, send_message, &td);
    //  pthread_create(&tid_recv, nullptr, recv_message, &td);

    // pthread_join(tid_send, nullptr);
    // pthread_join(tid_recv, nullptr);

    close(sockfd);
}

多进程版

上面的程序是单进程的,主进程会跟随sevice程序直到结束,只能提供一个客户链接。需要可以多个用户同时连接的
在service函数的时候fork子进程,让子进程去执行功能,双方关闭不需要的文件,子进程关闭listen套接字,父进程关闭sockfd套接字,互不影响。但父进程会卡在回收子进程的wait函数里,可以用非阻塞的等待,保存打开过的套接字轮询。还可以在fork的子进程里再一次fork,fork成功后子进程退出,这样代替执行功能的就是孙子进程,孙子进程会被os领养,结束后自动释放,同时设置进程退出信号为忽视,就不需要等待子进程

void run()
    {
        //忽视子进程
        signal(SIGCHLD, SIG_IGN);
        while (true)
        {
            //获取链接
            sockaddr_in sock;
            socklen_t len;
            int sockfd = accept(_listensock, (sockaddr *)&sock, &len);
            if (sockfd < 0)
            {
                log.logmessage(warning, "accept fail");
                continue;
            }

            char buff[32];
            string clientip = inet_ntop(AF_INET, &sock.sin_addr, buff, len);
            uint16_t clientport = ntohs(sock.sin_port);

            pid_t pid = fork();
            if (pid == 0)
            {
                // 子进程,孙子进程执行,子进程直接回收
                if (fork() > 0)
                    exit(0); // 孙子进程,system领养
                service(sockfd, clientip, clientport);
                close(_listensock);
                exit(0);
            }
            // 父进程
            pid_t ret = waitpid(pid, nullptr, 0);
            close(sockfd);
        }
    }

可以从函数开始就for循环fork,每一个子进程都会从listen套接字中获取标识符,执行service。只不过这种竞争性的需要给accept加锁

多线程版

创建进程的成本比较大,所以可以适用多线程版本

线程调用的函数来提供service功能,需要传入文件符,ip和port,所以创建一个结构体作为参数。线程执行的routine函数在类内需是静态的,主线程等待线程退出也会阻塞,所以将线程分类,退出信号忽视。静态函数无法调用非静态函数,所以传入的参数内带一个server类变量,向前声明server类,用this指针初始化,用来调用service函数

struct thread_data
{
    int _fd;
    string _ip;
    uint16_t _port;
    server* ser;
};

static void* routine(void* tmp)
    {
        //线程分离
        pthread_detach(pthread_self());
        //可以service函数提到类外
        //添加一个类成员
        thread_data *td = static_cast<thread_data*>(tmp);
        td->ser->service(td->_fd, td->_ip, td->_port);

        delete td;
        return nullptr;
    }
            pthread_t tid;
            thread_data* td = new thread_data;
            td->_fd = sockfd;
            td->_ip = clientip;
            td->_port = clientport;
            td->ser = this;
            pthread_create(&tid, nullptr, routine, td);

线程池版

多线程版本,如果有一个客户就要建立一个线程,当高峰的时候,可能会有很多个线程同时开启,资源占用大

加入之前写好的线程池,当接收到一个会话后,派给线程池,线程池会自动分配线程执行任务
在这里插入图片描述
修改tash任务类,成员保存文件符,ip和port
在这里插入图片描述
读取内容处理后写回去,关闭文件。常服务不在线程池里跑,执行一次后结束会话
在这里插入图片描述

ThreadPool

#pragma once
#include <vector>
#include <queue>
#include <pthread.h>
#include <string>
#include <unistd.h>

//换为封装的线程
struct ThreadInfo
{
    pthread_t _tid;
    std::string _name;
};
template <class T>
class pool
{
    static const int defaultnum = 5;

public:
   

    std::string getname(pthread_t tid)
    {
        for (auto ch : _thread)
        {
            if (ch._tid == tid)
            {
                return ch._name;
            }
        }
        return "None";
    }
    static void* HandlerTask(void* args)
    {
        pool<T> *tp = static_cast<pool<T> *>(args);
        std::string name = tp->getname(pthread_self());
        while (true)
        {
            pthread_mutex_lock(&(tp->_mutex));
            while (tp->_que.empty())
            {
                pthread_cond_wait(&(tp->_cond), &(tp->_mutex));
            }
            T t = tp->_que.front();
            tp->_que.pop();
            pthread_mutex_unlock(&tp->_mutex);
            t.run();
            //printf("%s finsih task:%s\n", name.c_str(), t.getresult().c_str());
            //sleep(1);
        }
        }
    void start()
    {
        for (int i = 0; i < _thread.size(); i++)
        {
            _thread[i]._name = "thread" + std::to_string(i);
            pthread_create(&_thread[i]._tid, nullptr, HandlerTask, this);
        }
    }

    void push(const T& x)
    {
        pthread_mutex_lock(&_mutex);
        _que.push(x);
        pthread_cond_signal(&_cond);
        pthread_mutex_unlock(&_mutex);
    }
    
    static pool<T>* GetInstance()
    {
        //套一层判断,只有第一次需要上锁
        if (_pl == nullptr)
        {
            pthread_mutex_lock(&_lock);
            if (_pl == nullptr)
            {
                //printf("first create\n");
                _pl = new pool<T>;
            }
            pthread_mutex_unlock(&_lock);
        }

        return _pl;
    }

private:
//构造私有化
    pool(int num = defaultnum)
        : _thread(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    pool(const pool<T> &) = delete;
    const pool<T> &operator=(const pool<T>&) = delete;
    ~pool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
    std::vector<ThreadInfo> _thread;
    std::queue<T> _que;

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static pthread_mutex_t _lock;
    static pool<T> *_pl;
};

//类外初始化
template <class T>
pool<T>* pool<T>::_pl = nullptr;
template <class T>
pthread_mutex_t pool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;

task

#pragma once
#include <stdio.h>
#include <string>
#include "log.hpp"
#include "init.hpp"

// enum
// {
//     DIVZERO = 1,
//     UNKNOW
// };

extern Log log;
Dict dict;
struct task
{
public:
   task(int fd, string ip, uint16_t port)
   {
       _fd = fd;
       _ip = ip;
       _port = port;
   }

   void run()
   {
    //常服务在线程池跑不合理,循环
       cout << "welcome " << _ip << endl;
       char message[1024];

       ssize_t n = read(_fd, message, sizeof(message) - 1);
       if (n > 0)
       {
           message[n] = 0;
           // 数据处理
           string echo_string;
            echo_string = "[" + _ip + ":" + to_string(_port) + "]: " + message;
            cout << echo_string << endl;

           write(_fd, echo_string.c_str(), echo_string.size());
       }
       else if (n == 0)
       {
           log.logmessage(info, "quit server");
       }
       else
       {
           log.logmessage(warning, "read error");
       }

       close(_fd);
   }

private:
    int _fd;
    string _ip;
    uint16_t _port;
};

server

#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
//#include <netinet/in.h>
#include <wait.h>
#include <signal.h>
#include "ThreadPool.hpp"
#include "task.hpp"
#include "log.hpp"

string defaultip = "0.0.0.0";
uint16_t defaultport = 8000;
const int back = 2;  //连接请求队列个数,一般2-4

Log log;

enum
{
    SOCK_ERR = 1,
    BIND_ERR,
    LIStEN_ERR
};

class server;
struct thread_data
{
    int _fd;
    string _ip;
    uint16_t _port;
    server* ser;
};

class server
{
public:
    server(const string ip = defaultip, const uint16_t port = defaultport)
    {
        _ip = defaultip;
        _port = defaultport;
    }

    void init()
    {
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            log.logmessage(fatal, "socket create error");
            exit(SOCK_ERR);
        }
        log.logmessage(info, "socket create success:%d", _listensock);

        sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        inet_aton(_ip.c_str(), &local.sin_addr);
        local.sin_port = htons(_port);

        //绑定
        if (bind(_listensock, (const struct sockaddr*)&local, sizeof(local)) < 0)
        {
            log.logmessage(fatal, "bind error:%s",strerror(errno));
            exit(BIND_ERR);
        }
        log.logmessage(info, "bind success");

        //tcp面向连接,服务器一般被动,处于一种等待连接到来的状态
        // 设置listen状态
        if (listen(_listensock, back) < 0)
        {
            log.logmessage(fatal, "listen error");
            exit(LIStEN_ERR);
        }
    }

    // void service(int fd, const string ip, const uint16_t port)
    // {
    //     cout << "welcome " << ip << endl;
    //     char message[1024];
    //     while (true)
    //     {    
    //         ssize_t n = read(fd, message, sizeof(message) - 1);
    //         if (n > 0)
    //         {
    //             message[n] = 0;
    //             //数据处理
    //             string echo_string;
    //             echo_string = "[" + ip + ":" + to_string(port) + "]: " + message;
    //             cout << echo_string << endl;

    //             write(fd, echo_string.c_str(), echo_string.size());
    //         }
            //读到0和其他情况
    //     }

    // }

    // static void* routine(void* tmp)
    // {
    //     //线程分离
    //     pthread_detach(pthread_self());
    //     //可以service函数提到类外
    //     //添加一个类成员
    //     thread_data *td = static_cast<thread_data*>(tmp);
    //     td->ser->service(td->_fd, td->_ip, td->_port);

    //     delete td;
    //     return nullptr;
    // }

    void run()
    {
        pool<task>::GetInstance()->start();
        // 忽视子进程
        //signal(SIGCHLD, SIG_IGN);
        while (true)
        {
            //获取链接
            struct sockaddr_in sock;
            socklen_t len = sizeof(sock);
            int sockfd = accept(_listensock, (sockaddr *)&sock, &len);
            if (sockfd < 0)
            {
                log.logmessage(warning, "accept fail");
                continue;
            }

            char buff[32];
            string clientip = inet_ntop(AF_INET, &sock.sin_addr, buff, len);
            uint16_t clientport = ntohs(sock.sin_port);
            log.logmessage(info, "get a new link,sockfd:%d", sockfd);
            // 线程池
            task t(sockfd, clientip, clientport);
            pool<task>::GetInstance()->push(t);
            //多线程
            // pthread_t tid;
            // thread_data* td = new thread_data;
            // td->_fd = sockfd;
            // td->_ip = clientip;
            // td->_port = clientport;
            // td->ser = this;

            // pthread_create(&tid, nullptr, routine, td);
            // 多进程
            // pid_t pid = fork();
            // if (pid == 0)
            // {
            //     // 子进程,孙子进程执行,子进程直接回收
            //     close(_listensock);
            //     if (fork() > 0)
            //         exit(0); // 孙子进程,system领养
            //     service(sockfd, clientip, clientport);
            //     close(sockfd);
            //     exit(0);
            // }
            // // 父进程
            // pid_t ret = waitpid(pid, nullptr, 0);
            // close(sockfd);
        }
    }

    ~server()
    {
        close(_listensock);
    }

private:
    int _listensock;
    string _ip;
    uint16_t _port;
};

英汉互译

准备一个词典文件,用:分割单词和汉语
在这里插入图片描述

init类加载词典和提供翻译功能
类成员用unorderedmap,string格式的单词查找存储的string类型汉语值
初始化功能打开文件,分割单词和翻译,加载到数据结构内
在这里插入图片描述

翻译功能遍历map查找翻译

在这里插入图片描述

task文件开始时初始化词典,收到数据调用翻译功能,将结果写会给客户

Dict dict;
echo_string = dict.translate(message);

init.hpp

#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include <unordered_map>

using namespace std;
const string filename = "./dict.txt";

static void split(string line, string* key, string* value)
{
    auto s = line.find(":");
    if (s != string::npos)
    {
        *key = line.substr(0, s);
        *value = line.substr(s + 1);
    }
}

class Dict
{
public:
    Dict()
    {
        cout << "开始打开" << endl;
        ifstream dic(filename);
        if (!dic.is_open())
        {
            cout << "file open error" << endl;
            exit(1);
        }
        string line;
        while (getline(dic, line))
        {
            string patr1;
            string part2;
            split(line, &patr1, &part2);
            _dict.insert({patr1, part2});
        }

        dic.close();
    }

    string translate(string word)
    {
        for (auto ch : _dict)
        {
            if (ch.first == word)
            {
                return ch.second;
            }
        }

        return "";
    }
    ~Dict()
    {

    }
private:
    unordered_map<string, string> _dict;
};

在这里插入图片描述

错误处理

当客户端发送请求,服务端正在往回写时,客户端的文件已经关闭,往一个不存在的文件写入内容就会发生错误,为了避免这种情况,刚开始的时候先关闭SIGPIP信号
在这里插入图片描述

write函数也有返回值,返回成功写入的字符个数,可以通过返回值判断写入情况
在这里插入图片描述

掉线重连

如果客户端在网络不好等其他情况掉线了,首先需要知道出错,也就是读或者写出错误,需要重新连接,模仿一个重连的情况

重连设置一定次数和时间间隔,用bool变量控制是否需要重连,读取成功后关闭文件重连

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

客户端发送数据的时候,服务端关闭,这时候客户端掉线重连了两次,重启服务端后连接成功

上面的情况有时候可能因为一个端口绑定后无法立即再次绑定,需要调用setsockopt函数复用之前的端口和ip启动

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1676964.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

读人工智能时代与人类未来笔记03_演变

1. 演变 1.1. 每个社会都找到了属于自己的一套适应世界的方法 1.1.1. 适应的核心&#xff0c;是有关人类心智与现实之间关系的概念 1.1.2. 人类认识周围环境的能力 1.1.2.1. 这种能力通过知识获得&#xff0c;同时也受到知识…

研发管理-选择研发管理系统-研发管理系统哪个好

选择研发管理系统-研发管理系统哪个好 选择研发管理系统时&#xff0c;并没有一个绝对的“最好”的系统&#xff0c;因为每个企业的需求和情况都是独特的。然而&#xff0c;我可以向您介绍一些在市场上广受欢迎且功能强大的研发管理系统&#xff0c;供您参考&#xff1a; 1、彩…

OpenAI 推出 GPT-4o:实现多模态 AI 交互

一、前言 OpenAI 推出了其最新的 AI 模型——GPT-4o&#xff0c;此次发布的并非 GPT-4.5 或 GPT-5&#xff0c;而是一款全新的“全模态模型(Omnimodel)”。这是一个将文本、语音和视觉能力集成到单一无缝 AI 体验中的突破性发展。 GPT-4o 于 2024 年 5 月 14 日发布&#xff0…

生产消费者模型-环形队列与信号量

文章目录 前言一、怎样的环形队列&#xff1f;二、什么是信号量三、使用步骤信号量的接口函数1. sem_init2.sem_destroy3.sem_wait4.sem_post 环形队列的设计测试用例 前言 之前我们使用互斥锁和条件变量实现过一个生产者消费者模型&#xff0c;那么那个生产消费者模型具有一个…

Github20K星开源团队协作工具:Zulip

Zulip&#xff1a;让团队协作的每一次交流&#xff0c;都精准高效。- 精选真开源&#xff0c;释放新价值。 概览 随着远程工作的兴起和团队协作的需求不断增加&#xff0c;群组聊天软件成为了日常工作中不可或缺的一部分。Zulip 是github上一个开源的团队协作工具&#xff0c;…

【问题实操】银河高级服务器操作系统实例分享,网卡drop问题分析

1.服务器环境以及配置 系统环境 物理机/虚拟机/云/容器 物理机 网络环境 外网/私有网络/无网络 私有网络 硬件环境 机型 华鲲振宇 TG225B1 处理器 kunpeng 920 内存 1024GB 主板型号 TG225B1 HZKY 整机类型/架构 aarch64 固件版本 6.57 软件环境 具体操作系…

谷歌Gemini时代来了!加固搜索护城河、赋能全家桶,Gemini 1.5 Pro升级至200万token

3 月中旬&#xff0c;谷歌宣布 Google I/O 定档北京时间 5 月 15 日凌晨 1 点。而当大会开幕时间临近&#xff0c;本应是讨论度最高的时候&#xff0c;「宿敌」OpenAI 却半路杀出&#xff0c;抢先一天&#xff0c;仅耗时 27 分钟就发布了颠覆性巨作 GPT-4o&#xff0c;将新一轮…

HTTP代理可以应用在那些领域呢

HTTP代理是IP代理领域中一个重要组成部分&#xff0c;它基于HTTP协议传输&#xff0c;使用海外服务器帮助用户绕开访问限制&#xff0c;浏览查看海外资讯信息。 HTTP代理可以应用在哪些领域呢&#xff1f; 1.保护使用者隐私 当今越来越数据被上传到网络云端上&#xff0c;用户…

Gone框架介绍17 - 创建一个可运行在生产环境的Web项目

gone是可以高效开发Web服务的Golang依赖注入框架 github地址&#xff1a;https://github.com/gone-io/gone 文档原地址&#xff1a;https://goner.fun/zh/guide/auto-gen-priest.html 请帮忙在github上点个 ⭐️吧&#xff0c;这对我很重要 &#xff1b;万分感谢&#xff01;&a…

【爬虫之scrapy框架——尚硅谷(学习笔记two)--爬取电影天堂(基本步骤)】

爬虫之scrapy框架--爬取电影天堂——解释多页爬取函数编写逻辑 &#xff08;1&#xff09;爬虫文件创建&#xff08;2&#xff09;检查网址是否正确&#xff08;3&#xff09;检查反爬&#xff08;3.1&#xff09; 简写输出语句&#xff0c;检查是否反爬&#xff08;3.2&#x…

Nginx生产环境最佳实践之配置灰度环境

你好呀&#xff0c;我是赵兴晨&#xff0c;文科程序员。 下面的内容可以说是干货满满建议先收藏再慢慢细品。 今天&#xff0c;我想与大家深入探讨一个我们日常工作中不可或缺的话题——灰度环境。你是否在工作中使用过灰度环境&#xff1f;如果是&#xff0c;你的使用体验如…

flutter开发实战-compute将工作交由isolate处理

flutter开发实战-compute将工作交由isolate处理 最近查看flutter文档时候&#xff0c;看到了compute可以将工作交由isolate处理。通过 Flutter 提供的 compute() 方法将解析和转换的工作移交到一个后台 isolate 中。这个 compute() 函数可以在后台 isolate 中运行复杂的函数并…

Leetcode 第 129 场双周赛题解

Leetcode 第 129 场双周赛题解 Leetcode 第 129 场双周赛题解题目1&#xff1a;3127. 构造相同颜色的正方形思路代码复杂度分析 题目2&#xff1a;3128. 直角三角形思路代码复杂度分析 题目3&#xff1a;3129. 找出所有稳定的二进制数组 I思路代码复杂度分析 题目4&#xff1a;…

JavaScript异步编程——11-异常处理方案【万字长文,感谢支持】

异常处理方案 在JS开发中&#xff0c;处理异常包括两步&#xff1a;先抛出异常&#xff0c;然后捕获异常。 为什么要做异常处理 异常处理非常重要&#xff0c;至少有以下几个原因&#xff1a; 防止程序报错甚至停止运行&#xff1a;当代码执行过程中发生错误或异常时&#x…

数字人解决方案——ID-Animator可保持角色一致生成视频动画

一、引 言 个性化或自定义生成在图像和视频生成领域是一个不断发展的研究方向&#xff0c;尤其是在创建与特定身份或风格一致的内容方面。您提到的挑战和解决方案为这一领域提供了有价值的见解&#xff1a; 训练成本高&#xff1a;这是一个普遍问题&#xff0c;因为个性化生成…

手机触控面板中应用的电容式触摸芯片

手机触控屏&#xff08;Touch panel&#xff09;又称为触控面板&#xff0c;是个可接收触头等输入讯号的感应式液晶显示装置&#xff0c;当接触了屏幕上的图形按钮时&#xff0c;屏幕上的触觉反馈系统可根据预先编程的程式驱动各种连结装置&#xff0c;可用以取代机械式的按钮面…

【AI】DeepStream(03):deepstream_test1_app

1、简介 deepstream-test1:演示各种 DeepStream 插件构建 GStreamer 管道。从文件中获取视频、解码、批处理,然后进行对象检测,最后在屏幕上渲染框。 源码路径:/opt/nvidia/deepstream/deepstream/sources/apps/sample_apps/deepstream-test1 先看下效果 2、编译 1)…

Redis-分片集群存储及读取数据详解

文章目录 Redis分片集群是什么&#xff1f;Redis分片集群的存储及读取数据&#xff1f; 更多相关内容可查看 Redis分片集群是什么&#xff1f; Redis分片集群是一种分布式部署方式&#xff0c;通过将数据分散存储在多个Redis节点上&#xff0c;从而提高了系统的性能、扩展性和…

【微记录】linux内核态日志如何持续观测?以及dmesg如何显示年月日时间戳?(dmesg -w ; -T)

文章目录 持续观测方法1方法2 dmes显示时间戳 持续观测 方法1 dmesg -w参考&#xff1a;https://man7.org/linux/man-pages/man1/dmesg.1.html 方法2 tail -f /var/log/kern.logdmes显示时间戳 dmesg -T #按照人类可读性高的时间戳 比如2024-05-15 01:20:16实操&#xff1…

快速学习SpringAi

Spring AI是AI工程师的一个应用框架&#xff0c;它提供了一个友好的API和开发AI应用的抽象&#xff0c;旨在简化AI应用的开发工序&#xff0c;例如开发一款基于ChatGPT的对话应用程序。通过使用Spring Ai使我们更简单直接使用chatgpt 1.创建项目 jdk17 引入依赖 2.依赖配置 …