网络编程套接字,Linux下实现echo服务器和客户端

2023-10-31

目录

1、一些网络中的名词

1.1 IP地址

1.2 端口号port

1.3  "端口号" 和 "进程ID"

1.4 初始TCP协议

1.5 UDP协议

2、socket编程接口

2.1 socket 常见API

2.2 sockaddr结构

3、简单的网络程序

3.1 udp实现echo服务器和客户端

3.1.1 echo服务器实现

3.1.2 echo客户端实现

3.1.3 运行结果

3.2  tcp实现echo服务器和客户端

3.2.1 多进程的echo服务器

3.2.2 基于线程池tcp的echo服务器

 3.3 代码中的一些函数

3.3.1 地址转换函数

3.3.2 udp使用的的函数

3.3.3 tcp使用的函数

4、结语


1、一些网络中的名词

1.1 IP地址

        IP地址就和我们现实中的地址是一个概念,只不过一个在网络中定位,一个在现实中定位,

        在一台服务器往另一台服务器发送数据的时候,IP数据包的头部中,有两个IP地址,一个是源IP地址,另一个是目的IP地址,

1.2 端口号port

端口号(port)是传输层协议的内容.

        端口号是一个2字节16位的整数;

        端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;

        IP地址 + 端口号能够标识网络上的某一台主机的某一个进程;

        一个端口号只能被一个进程占用.

1.3  "端口号" 和 "进程ID"

        pid 表示唯一一个进程; 此处我们的端口号也是唯一表示一个进程。一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定。

1.4 初始TCP协议

传输层协议

有连接

可靠传输

面向字节流

1.5 UDP协议

传输层协议

无连接

不可靠传输

面向数据报

网络字节序

        在计算机的内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

        发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;

        接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;

        因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.

        TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.

        不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;

        如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

        为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

h表示host,n表示network,l表示32位长整数,s表示16位短整数。

例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。

如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;

如果主机是大端字节序,这些  函数不做转换,将参数原封不动地返回。

2、socket编程接口

2.1 socket 常见API

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器) 
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, 
          socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog); 
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, 
          socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, 
          socklen_t addrlen);

2.2 sockaddr结构

        socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同.

         IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.

        IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.

        socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;

sockaddr 结构

struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);	/* Common data: address family and length.  */
    char sa_data[14];		/* Address data.  */
  };

sockaddr_in 结构

struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;			/* Port number.  */
    struct in_addr sin_addr;		/* Internet address.  */

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
			   __SOCKADDR_COMMON_SIZE -
			   sizeof (in_port_t) -
			   sizeof (struct in_addr)];
  };

        虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址.

in_addr结构

struct in_addr
  {
    in_addr_t s_addr;
  };

in_addr用来表示一个IPv4的IP地址. 其实就是一个32位的整数;

3、简单的网络程序

3.1 udp实现echo服务器和客户端

3.1.1 echo服务器实现

//udp_server.hpp
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unordered_map>
#include <vector>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

class udpserver{
public:
    udpserver(std::string ip, int16_t port):_fd(-1), _ip(ip), _port(port),_users(0)
    {}

    ~udpserver(){
        if (_fd > 0) {
            close(_fd);
        }
    }

    void initServer() {
        _fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_fd < 0) {
            perror("注册socket失败");
            exit(2);
        }
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = inet_addr(_ip.c_str());

        if (bind(_fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
            perror("绑定失败!");
            exit(3);
        }
        //std::cout << "绑定成功!"<< std::endl;
    }

    void startServer(){

        //准备用来接收客户端发送的消息的缓冲区
        char buffer[1024];
        while (1) {
            
            //准备用来接收发送消息的客户端信息
            memset(buffer, '\0', sizeof(buffer));
            struct sockaddr_in peer;
            memset(&peer, 0, sizeof(peer));
            socklen_t len = sizeof(peer);
            //接收数据,以及接收发送数据的客户端信息
            //std::cout << "正在接收!" << std::endl;
            ssize_t recv_size = recvfrom(_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
            
            //打印客户端发送来的数据
            //std::cout << "接收成功!正在打印:" << std::endl;
            // if (recv_size > 0) {
            //     buffer[recv_size] = 0;
            //     std::string ip = inet_ntoa(peer.sin_addr);
            //     int16_t port = ntohs(peer.sin_port);
            //     std::cout << "[" << ip << ":" << port << "]:";
            //     std::cout << buffer << std::endl;
            // }
            //处理数据
            buffer[recv_size] = 0;
            std::string massage;
            massage += inet_ntoa(peer.sin_addr);
            massage += ":";
            massage += ntohs(peer.sin_port);

            //_users.insert(make_pair<std::string,struct sockaddr_in>(massage, peer);
            _users.insert({massage, peer});

            massage += "#";
            massage += buffer;
            //_users.insert(makepair(, peer);

            //回写数据
            //sendto(_fd, buffer, strlen(buffer), 0, (struct sockaddr*)&peer, len);
            for (auto &s : _users) {
                sendto(_fd, massage.c_str(), massage.size(),0 ,(struct sockaddr*)&(s.second), sizeof(s.second));
            }
        }
    }

private:
    int _fd;
    std::string _ip;
    int16_t _port;
    std::unordered_map<std::string,struct sockaddr_in> _users;
};
//udp_server.cpp
#include "udpserver.hpp"
#include <memory>

int main(int argc, char* args[]) {

    std::string ip;
    int16_t port = 0;
    if (argc == 3) {
        ip = args[1];
        port = atoi(args[2]);
    }
    else if (argc == 2) {
        ip = "0.0.0.0";
        port = atoi(args[1]);
    }
    else{
        perror("输入错误!");
        return 1;
    }

    std::unique_ptr<udpserver> server(new udpserver(ip,port));
    server->initServer();
    server->startServer();
    return 0;
}

3.1.2 echo客户端实现

//udp_client.cpp
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>
#include <cstdio>

#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>

struct sendData{
    int _sock;
    struct sockaddr_in *server;
};

void* sending(void *arg) {
    struct sendData* data = (struct sendData*)arg;
    int sock = data->_sock;
    struct sockaddr_in server = *(data->server);

    while (1) {
        std::string massage;
        std::cerr << "请输入内容:" ;
        std::getline(std::cin, massage);
        //发送数据
        sendto(sock, massage.c_str(), massage.size(), 0, (struct sockaddr*)&server, sizeof(server));
    }
}

void* receive(void *arg) {
    struct sendData* data = (struct sendData*)arg;
    int sock = data->_sock;
    char buffer[1024];
    while (1) {
        memset(buffer, '\0', sizeof(buffer));
        struct sockaddr_in from;
        socklen_t len = sizeof(from);
        ssize_t recv_size = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)&from, &len);
        if(recv_size < 1) {
            continue;
        }
        buffer[recv_size] = '\0';
        //printf("[%s:%u]#%s\n",inet_ntoa(from.sin_addr),ntohs(from.sin_port),buffer);
        std::cout << buffer << std::endl;
    }
}

//客户端,负责给服务端发送消息
int main(int argc, char* args[]) {

    if (argc != 3) {
        std::cerr << "请正确输入参数!" << std::endl;
        exit(1);
    }
    std::string ip = args[1];
    int16_t port = atoi(args[2]);

    //创建套接字
    int _sock = socket(AF_INET, SOCK_DGRAM, 0);
    //这里依然会绑定,但是不需要手动绑定,回自动绑定,在第一次send的时候自动绑定,  
    if (_sock < 0) {
        exit(2);
    }

    struct sockaddr_in server;
    server.sin_addr.s_addr = inet_addr(ip.c_str());
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    socklen_t len = sizeof(server);

    sendData data;
    data._sock = _sock;
    data.server = &server;

    //创建线程,让线程1负责发送,线程2负责接收
    pthread_t send,recv;
    pthread_create(&send,nullptr,sending,(void*)&data);
    pthread_create(&send,nullptr,receive,(void*)&data);

    pthread_join(send,nullptr);
    pthread_join(recv,nullptr);

    close(_sock);
    return 0;
}

3.1.3 运行结果

3.2  tcp实现echo服务器和客户端

3.2.1 多进程的echo服务器


#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <signal.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static void servise(int serviseSock, std::string userip, int16_t userport) {
    char buffer[1024];
    while (1) {
        memset(buffer, 0, sizeof(buffer));
        size_t s = read(serviseSock,buffer,sizeof(buffer));    

        if (s > 0) {
            buffer[s] = '\0';
            std::cout << userip.c_str() << ":" << userport << "#" << buffer << std::endl;
        }
        else if (s == 0) {
            //表示对方关闭了连接
            std::cerr << userip << ":" << userport << " shutdowm,me too!" << std::endl;
            break;
        }
        else {
            std::cerr << "read socket error," << errno << strerror(errno) << std::endl;
            break;
        }

        write(serviseSock, buffer, strlen(buffer));
    }
}


class tcpServer{
public:
    tcpServer(int16_t port, std::string ip = "")
    :_ip(ip),
    _port(port),
    _listenSock(-1)
    {}
    
    ~tcpServer(){
        if (_listenSock > 0) {
            close(_listenSock);
        }
    }

    void initServer(){
        //backlog不能太大也不能太小
        static int gbacklog = 20;
        //申请描述符
        _listenSock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenSock < 0) {
            std::cerr << "注册socket失败" << std::endl;
            exit(2);
        }
        //绑定端口号和IP地址
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        local.sin_port = htons(_port);

        if (bind(_listenSock, (struct sockaddr*)&local, sizeof(local)) < 0) {
            std::cerr << "绑定ip和端口号失败" << std::endl;
            exit(3);
        }

        //设置监听状态
        if (listen(_listenSock, gbacklog) < 0) {
            std::cerr << "设置监听失败" << std::endl;
            exit(4);
        }
    }

    void start() {

        //将子进程的信号改为忽略
        signal(SIGCHLD, SIG_IGN);
        
        while (1) {

            struct sockaddr_in user;
            socklen_t len = sizeof(user);
            int serviseSock = accept(_listenSock, (struct sockaddr*)&user, &len);
            std::string userip = inet_ntoa(user.sin_addr);
            int16_t userport = ntohs(user.sin_port);
            
            //servise(serviseSock,userip,userport);
            int pid = fork();
            if (pid == 0) {
                close(_listenSock);
                servise(serviseSock,userip,userport);
                close(serviseSock);
                exit(0);
            }
            close(serviseSock);
        }
    }

private:
    std::string _ip;
    int16_t _port;
    int _listenSock;
};

        但是我们都知道,在操作系统中,进程是资源分配的基本单位,如果使用多进程的方案的话,就非常的浪费资源,所以,相比之下,使用多线程的方式回更好,我们在实现一个基于线程池的实现方式。

3.2.2 基于线程池tcp的echo服务器

//自己实现的循环队列,当中使用的锁和信号都是自己封装的,这里就不放代码了
//ringqueue.hpp
#include <iostream>
#include <vector>
#include "sem.hpp"
#include "mutex.hpp"

template<class T>
class ringqueue {

public:
    ringqueue(int capacity = 10)
    :_ring_queue(capacity),
    _start(0),
    _tail(0),
    _space_sem(capacity),
    _data_sem(0),
    _mtx()
    {}

    void push(const T &in){

        _space_sem.p();
        _mtx.lock();
        _ring_queue[_start++] = in;
        _start %= _ring_queue.size();
        _data_sem.v();
        _mtx.unlock();
    }

    void pop(T & out){
        _data_sem.p();
        _mtx.lock();
        out = _ring_queue[_tail++];
        _tail %= _ring_queue.size();
        _space_sem.v();
        _mtx.unlock();
    }

    ~ringqueue()
    {
        
    }

private:
    std::vector<T> _ring_queue;
    int _start;
    int _tail;
    sem _space_sem;
    sem _data_sem;
    mutex _mtx;
};
//单例模式的线程池
//其中的线程也是自己进行封装的,不做代码展示
//thread_pool.hpp
#include "thread.hpp"
#include "ringQueue.hpp"
#include <ctime>
#include <unistd.h>

template <class T>
struct poolData
{
    Thread* _self;
    ringqueue<T>* _rq;
};

template <class T>
class Pool
{

public:
    static Pool<T>* getpool(int num = 10){
        if (nullptr == _pool) {
            pthread_mutex_lock(&mtx);
            if (nullptr == _pool) {
                _pool = new Pool<T>(num);
            }
            pthread_mutex_unlock(&mtx);
        }
        return _pool;
    }

private:
    Pool(int num) 
    :_consumer(num),
    _rq(10)
    {}
    Pool(const Pool& pool) = delete;
    Pool& operator=(const Pool& pool) = delete;
public:
    void strat()
    {
        poolData<T> condata[_consumer.size()];
        for (int i = 0; i < _consumer.size(); ++i) {
            _consumer[i] = new Thread(i);
            condata[i]._self = _consumer[i];
            condata[i]._rq = &_rq;
            _consumer[i]->create(consumer,&condata[i]);
        }
    }

    // 生产者
    void pushTask(T task)
    {
        _rq.push(task);
    }
    // 消费者
    static void *consumer(void *args)
    {
        poolData<T> *pd = (poolData<T>*)args;
        Thread *self = pd->_self;
        ringqueue<T> *rq = pd->_rq;
        std::cout << self->name() << " Successfully started!" << std::endl;
        while (true) {
            T t;
            rq->pop(t);
            (*t)();
            delete t;
        }
    }

    ~Pool()
    {
        for (int i = 0; i < _consumer.size(); ++i) {
            _consumer[i]->join();
            delete _consumer[i];
        }
        
    }

private:
    
    ringqueue<T> _rq;
    std::vector<Thread*> _consumer;
    static pthread_mutex_t mtx;
    static Pool<T>* _pool;
};

template<class T>
Pool<T>* Pool<T>::_pool = nullptr;

template<class T>
pthread_mutex_t Pool<T>::mtx = PTHREAD_MUTEX_INITIALIZER;
//tcp_server.hpp
#include "thread_pool.hpp"
#include "Task.hpp"

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <signal.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static void servise(int serviseSock, std::string & userip, int16_t userport) {
    char buffer[1024];
    while (1) {
        memset(buffer, 0, sizeof(buffer));
        size_t s = read(serviseSock,buffer,sizeof(buffer));    
        if (s > 0) {
            buffer[s] = '\0';
            std::cout << userip.c_str() << ":" << userport << "#" << buffer << std::endl;
        }
        else if (s == 0) {
            //表示对方关闭了连接
            std::cerr << userip << ":" << userport << " shutdowm,me too!" << std::endl;
            break;
        }
        else {
            std::cerr << "read socket error," << errno << strerror(errno) << std::endl;
            break;
        }

        write(serviseSock, buffer, strlen(buffer));
    }
    close(serviseSock);
}

class tcpServer{
public:
    tcpServer(int16_t port, std::string ip = "")
    :_ip(ip),
    _port(port),
    _listenSock(-1),
    _pool_ptr(Pool<Task*>::getpool())
    {}
    
    ~tcpServer(){
        if (_listenSock > 0) {
            close(_listenSock);
        }
    }

    void initServer(){
        //backlog不能太大也不能太小
        static int gbacklog = 20;
        //申请描述符
        _listenSock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenSock < 0) {
            std::cerr << "注册socket失败" << std::endl;
            exit(2);
        }
        //绑定端口号和IP地址
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        local.sin_port = htons(_port);

        if (bind(_listenSock, (struct sockaddr*)&local, sizeof(local)) < 0) {
            std::cerr << "绑定ip和端口号失败" << std::endl;
            exit(3);
        }
        //设置监听状态
        if (listen(_listenSock, gbacklog) < 0) {
            std::cerr << "设置监听失败" << std::endl;
            exit(4);
        }
    }

    void start() {
        _pool_ptr->strat();
        while (1) {
            struct sockaddr_in user;
            socklen_t len = sizeof(user);
            int serviseSock = accept(_listenSock, (struct sockaddr*)&user, &len);
            std::string userip = inet_ntoa(user.sin_addr);
            int16_t userport = ntohs(user.sin_port);
            Task *task = new Task(serviseSock, userip, userport, servise);
            _pool_ptr->pushTask(task);
        }
    }

private:
    std::string _ip;
    int16_t _port;
    int _listenSock;
    Pool<Task*>* _pool_ptr;
};
//服务器入口,
//tcp_server.cpp
#include "tcp_server.hpp"
#include <memory>

int main(int argc, char* args[]) {
    std::string ip;
    int16_t port;
    if (argc == 2) {
        ip = "";
        port = atoi(args[1]);
    }
    else if (argc == 3) {
        ip = args[1];
        port = atoi(args[2]);
    }
    else {
        std::cerr << "输入错误!" << std::endl;
        exit(1);
    }
    std::unique_ptr<tcpServer> server(new tcpServer(port,ip));
    server->initServer();
    server->start();

    return 0;
}

3.2.3 运行结果

 3.3 代码中的一些函数

3.3.1 地址转换函数

        本节基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位  的IP 地址但是我们通常用点分十进制的字符串表示IP 地址,以下函数可以在字符串表示  和in_addr表示之间转换;

字符串与in_addr的一些函数:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);

3.3.2 udp使用的的函数

发送函数sendto:

#include <sys/types.h>
#include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                const struct sockaddr *dest_addr, socklen_t addrlen);

接收函数recvfrom:

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                  struct sockaddr *src_addr, socklen_t *addrlen);

3.3.3 tcp使用的函数

发送函数

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

接收函数

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

4、结语

        本文中若有错误,请私信或评论指出,谢谢!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

网络编程套接字,Linux下实现echo服务器和客户端 的相关文章

  • Tomcat Intellij Idea:远程部署

    RackSpace 云服务器 Ubuntu 12 04 Intellij Idea 11 1 2 Windows 8 Tomcat 7 0 26 JDK 6 在 Intellij Idea 上 当我尝试在远程 Tomcat 7 服务器上运行
  • 批量删除文件名中包含 BASH 中特殊字符的子字符串

    我的目录中有一个文件列表 opencv calib3d so2410 so opencv contrib so2410 so opencv core so2410 so opencv features2d so2410 so opencv
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • SSH,运行进程然后忽略输出

    我有一个命令可以使用 SSH 并在 SSH 后运行脚本 该脚本运行一个二进制文件 脚本完成后 我可以输入任意键 本地终端将恢复到正常状态 但是 由于该进程仍在我通过 SSH 连接的计算机中运行 因此任何时候它都会登录到stdout我在本地终
  • docker 非 root 绑定安装权限,WITH --userns-remap

    all 尝试让绑定安装权限正常工作 我的目标是在容器中绑定安装卷 以便 a 容器不以 root 用户身份运行入口点 二 docker daemon 配置了 userns remap 这样容器 主机上没有 root c 我可以绑定挂载和读 写
  • 在 Linux 上以编程方式设置 DNS 名称服务器

    我希望能够通过我的 C C 程序为 Linux 上的 DNS 名称服务器添加 IP 地址 我在一个带有只读 etc resolv conf 的嵌入式平台上 这意味着我不能简单地将 nameserver xxx xxx xxx xxx 行添加
  • tcpdump 是否受 iptables 过滤影响?

    如果我的开发机器有iptables规则到FORWARD一些数据包 这些数据包是否被 tcpdump 捕获 我有这个问题 因为我知道存在其他链称为INPUT如果数据包路由到 它会过滤发往应用程序的数据包FORWARD链 它会到达吗tcpdum
  • linux-x64 二进制文件无法在 linuxmusl-x64 平台上使用错误

    我正在安装Sharp用于使用 package json 的 Nodejs 项目的 docker 映像上的映像压缩包 当我创建容器时 我收到有关 Sharp 包的以下错误 app node modules sharp lib libvips
  • 如何阻止ubuntu在使用apt安装或更新软件包时弹出“Daemons using outdatedlibraries”? [关闭]

    Closed 这个问题是与编程或软件开发无关 help closed questions 目前不接受答案 我最近新安装了 Ubuntu 22 04 LTS 我发现每次使用 apt 安装或更新软件包时 它都会询问我有关Which servic
  • 使用循环在 C 中管道传输两个或多个 shell 命令

    我正在尝试执行ls wc l通过 C 语言程序 而不是使用命令行 这是我当前的工作代码 int main int pfds 2 pipe pfds pid t pid fork if pid 0 The child process clos
  • 添加文件时运行 shell 命令

    我的 Linux 机器上有一个名为 images 的文件夹 该文件夹连接到一个网站 该网站的管理员可以向该网站添加图片 但是 当添加图片时 我想要一个命令来运行调整目录中所有图片的大小 简而言之 我想知道当新文件添加到特定位置时如何使服务器
  • 为什么 fopen("any_path_name",'r') 不给出 NULL 作为返回值?

    在调试一些代码时 我得到如下内容 include
  • CMake 链接 glfw3 lib 错误

    我正在使用 CLion 并且正在使用 glfw3 库编写一个程序 http www glfw org docs latest http www glfw org docs latest 我安装并正确执行了库中的所有操作 我有 a 和 h 文
  • 如何在 Linux 中使用 C 语言使用共享内存

    我的一个项目有点问题 我一直在试图找到一个有据可查的使用共享内存的例子fork 但没有成功 基本上情况是 当用户启动程序时 我需要在共享内存中存储两个值 当前路径这是一个char and a 文件名这也是char 根据命令参数 启动一个新进
  • Intel 上的 gcc 中的 _mm_pause 用法

    我参考过这个网页 https software intel com en us articles benefitting power and performance sleep loops https software intel com
  • C修改printf()输出到文件

    有没有办法修改printf为了将字符串输出到文件而不是控制台 我尝试在互联网上查找一些内容 发现了类似的电话dup dup2 and fflush这可能与此有关 EDIT 也许我不清楚 问题是这是C考试问题 问题如下 解释一个通常将字符串输
  • 使用 python 脚本更改 shell 中的工作目录

    我想实现一个用户态命令 它将采用其参数之一 路径 并将目录更改为该目录 程序完成后 我希望 shell 位于该目录中 所以我想实施cd命令 但需要外部程序 可以在 python 脚本中完成还是我必须编写 bash 包装器 Example t
  • 使用 gdb 调试 Linux 内核模块

    我想知道 API 在内核模块 中返回什么 从几种形式可以知道 这并不是那么简单 我们需要加载符号表来调试内核模块 所以我所做的就是 1 尝试找到内核模块的 text bss和 data段地址 2 在 gdb 中使用 add symbol f
  • 如何在 Mac OSX Mavericks 中正确运行字符串工具?

    如何在 Mac OSX Mavericks 中正确运行字符串工具 我尝试按照我在网上找到的示例来运行它 strings a UserParser class 但我收到此错误 错误 Applications Xcode app Content
  • ansible unarchive 模块如何查找 tar 二进制文件?

    我正在尝试执行一个 ansible 剧本 该剧本的任务是利用unarchive模块 因为我是在 OSX 上执行此操作 所以我需要使用它gnu tar 而不是bsd tar通常与 OSX 一起提供 因为BSD tar 不受官方支持 https

随机推荐