APUE 第三版 程序 16-16&16-17 ruptimed (简单的TCP,C/S 实例)

如有错误,欢迎批评指正,本人也是才学APUE的菜鸟

实验的系统是 Ubuntu 18.04

代码先贴一下,和书上一样,没啥改变 ,主要是为了没书的时候看

connect_retry.c
//
// Created by hjm on 2020/5/3.
//

#include "apue.h"
#include <sys/socket.h>

#define MAXSLEEP 128

int connect_retry(int domain, int type, int protocol,
        const struct sockaddr *addr, socklen_t alen) {
    int numsec, fd;

    /*
     * Try to connect with exponential backoff.
     */
    for(numsec = 1;numsec <= MAXSLEEP;numsec <<= 1) {
        if((fd = socket(domain, type, protocol)) < 0)
            return -1;
        if(connect(fd, addr, alen) == 0) {
            /*
             * Connection accepted
             */
            return fd;
        }
        close(fd);
        /*
         * Delay before trying again
         */
        if(numsec <= MAXSLEEP / 2)
            sleep(numsec);
    }
    return -1;
}
ruptimeClient.c
//
// Created by hjm on 2020/5/3.
//

#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>

#define BUFLEN 128

extern int connect_retry(int, int, int, const struct sockaddr*, socklen_t);

void print_uptime(int sockfd) {
    int n;
    char buf[BUFLEN];

    while((n = recv(sockfd, buf, BUFLEN, 0)) > 0)
        write(STDOUT_FILENO, buf, n);
    if(n < 0)
        err_sys("recv error");
}

int main(int argc, char *argv[]) {
    struct addrinfo *ailist, *aip;
    struct addrinfo hint;
    int sockfd, err;

    if(argc != 2)
        err_quit("usage: ruptime hostname");
    memset(&hint, 0, sizeof(hint));
    hint.ai_socktype = SOCK_STREAM;
    hint.ai_canonname = NULL;
    hint.ai_addr = NULL;
    hint.ai_next = NULL;

    if((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)
        err_quit("getaddrinfo error: %s", gai_strerror(err));

    for(aip = ailist; aip != NULL; aip = aip -> ai_next) {
        if((sockfd = connect_retry(aip -> ai_family, SOCK_STREAM, 0,
                aip -> ai_addr, aip -> ai_addrlen)) < 0) {
            err = errno;
        } else {
            print_uptime(sockfd);
            exit(0);
        }
    }
    err_exit(err, "can't connect to %s", argv[1]);
}
initserver.c
//
// Created by hjm on 2020/5/3.
//

#include "apue.h"
#include <errno.h>
#include <sys/socket.h>

int initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen) {
    int fd;
    int err = 0;

    if((fd = socket(addr -> sa_family, type, 0)) < 0)
        return -1;
    if(bind(fd, addr, alen) < 0)
        goto errout;
    if(type == SOCK_STREAM || type == SOCK_SEQPACKET) {
        if(listen(fd, qlen) < 0)
            goto errout;
    }
    return fd;

    errout:
    err = errno;
    close(fd);
    errno = err;
    return -1;
}
ruptimeServer.c
//
// Created by hjm on 2020/5/3.
//

#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/resource.h>

#define BUFLEN 128
#define QLEN 10

#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif

extern int initserver(int, const struct sockaddr *, socklen_t, int);

void serve(int sockfd) {
    int clfd;
    FILE *fp;
    char buf[BUFLEN];

    set_cloexec(sockfd);
    for( ; ; ) {
        if((clfd = accept(sockfd, NULL, NULL)) < 0) {
            syslog(LOG_ERR, "ruptime: accept error: %s", strerror(errno));
            exit(1);
        }
        set_cloexec(clfd);
        if((fp = popen("/usr/bin/uptime", "r")) == NULL) {
            sprintf(buf, "error: %s\n", strerror(errno));
            send(clfd, buf, strlen(buf), 0);
        } else {
            while(fgets(buf, BUFLEN, fp) != NULL)
                send(clfd, buf, strlen(buf), 0);
            pclose(fp);
        }
        close(clfd);
    }
}

int main(int argc, char *argv[]) {
    struct addrinfo *ailist, *aip;
    struct addrinfo hint;
    int sockfd, err, n;
    char *host;

    if(argc != 1)
        err_quit("usage: ruptimed");
    if((n = sysconf(_SC_HOST_NAME_MAX)) < 0)
        n = HOST_NAME_MAX;  // best guess
    if((host = (char *)malloc(n)) == NULL)
        err_sys("malloc error");
    if(gethostname(host, n) < 0)
        err_sys("gethostname error");
    daemonize("ruptimed");
    hint.ai_next = NULL;
    memset(&hint, 0, sizeof(hint));
    hint.ai_flags = AI_CANONNAME;
    hint.ai_socktype = SOCK_STREAM;
    hint.ai_canonname = NULL;
    hint.ai_addr = NULL;

    if((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0) {
        syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s", gai_strerror(err));
        exit(1);
    }
    for(aip = ailist; aip != NULL; aip = aip -> ai_next) {
        if((sockfd = initserver(SOCK_STREAM, aip -> ai_addr, aip -> ai_addrlen, QLEN)) >= 0) {
            serve(sockfd);
            exit(0);
        }
    }
    exit(1);
}

void daemonize(const char *cmd) {
    int i, fd0, fd1, fd2;
    pid_t pid;
    struct rlimit rl;
    struct sigaction sa;
    
    /*
     * Clear file creation mask
     */
    umask(0);
    
    /*
     * Get maximum number of file descriptors
     */
    if(getrlimit(RLIMIT_NOFILE, &rl) < 0)
        err_quit("%s: can't get file limit", cmd);
    
    /*
     * Become a session leader to lost controlling TTY.
     */
    if((pid = fork()) < 0)
        err_quit("%s: can't fork", cmd);
    else if(pid != 0)
        exit(0);
    setsid();
    
    /*
     * Ensure future opens won't allocate controlling TTYs.
     */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if(sigaction(SIGHUP, &sa, NULL) < 0)
        err_quit("%s: can't ignore SIGHUP", cmd);
    if((pid = fork()) < 0)
        err_quit("%s: can't fork", cmd);
    else if(pid != 0)
        exit(0);
    
    /*
     * Change the current working directory to the root so
     * we won't prevent file systems from being unmounted.
     */
    if(chdir("/") < 0)
        err_quit("%s: can't change directory to /", cmd);
    
    /*
     * Close all open file descriptors.
     */
    if(rl.rlim_max == RLIM_INFINITY)
        rl.rlim_max = 1024;
    for(i = 0; i < rl.rlim_max; ++ i)
        close(i);
    
    /*
     * Attach file descriptors 0, 1, 2 to /dev/null.
     */
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);
    
    /*
     * Initialize the log file.
     */
    openlog(cmd, LOG_CONS, LOG_DAEMON);
    if(fd0 != 0 || fd1 != 1 || fd2 != 2) {
        syslog(LOG_ERR, "unexpected file descriptors %d %d %d",
                fd0, fd1, fd2);
        exit(1);
    }
}

简述遇到的问题:

  1. 对于代码中:getaddrinfo 的 “ruptime”,这是一个服务。
    需要在 /etc/service 文件中加上:ruptime 4000/tcp
    当然,4000 可以随便指定,大于 1024 且没有被占用即可。

  2. 对于服务器方面的问题,可以查看 /var/log/syslog 文件,找到相关其中的错误信息。因为 当服务器程序运行时,它是作为守护进程运行的,没有控制终端。
    然后是我遇到的。我某个地方把代码抄错了,出现了:Socket operation on non-socket
    多多注意一下 listen,accept 等,与 sockfd 有关地方的代码,特别是括号有没有漏了或者位置错了。

  3. 当运行客户端程序时,如果使用 localhost 作为 main 的第二个参数,终端无输出,最后:指明连接失败
    这需要注意服务器绑定的地址,通过:netstat -nap | grep Server.out 查看。(Server.out 是我的服务器编译后的执行文件)
    然后重新运行,输入该服务器绑定的地址即可。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值