nodejs+c实现的回射程序

最近在看《UNIX编程卷1:套接字联网API》,一般都是ssh到本地的linux虚拟机里启动服务器端,然后再另一个ssh窗口启动客户端来测试。今天想了想可用nodejs在windows下实现回射程序的客户端:

echoCli.js:

var net = require('net'),
  readline = require('readline');
var serverInfo = {
  "host": "192.168.147.128",
  "port": 9983
};
var client = net.connect(serverInfo);
client.on('data', function(data) {
  console.log(data.toString());
});
client.on('end', function() {
  console.log('client disconnected');
});
var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});
rl.on('line', function (str) {
  client.write(str);
});

echoServer.c:

#include <stdlib.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "../common.h"
#line 18 "tcpechoserv01.c"
void str_echo(int fd);
void sig_chld(int signo);
int main(void)
{
    // 注册信号处理函数
    signal(SIGCHLD, sig_chld);
    int sockfd, connfd;
    struct sockaddr_in servaddr, cliaddr, tmpaddr;
    char addr_str[INET_ADDRSTRLEN + 1];
    socklen_t cliaddrlen, tmplen;
    pid_t pid;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0) {
        perror("socket error");
        return 1;
    }
    bzero(&cliaddr, sizeof(cliaddr));
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(9983);
    if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {
        perror("bind error");
        return 1;
    }
    if (listen(sockfd, 1024) != 0) {
        perror("listen error");
        return 1;
    }
    // 输出服务器绑定的ip和端口号,tmplen初始化是必须的,不然会出错
    tmplen = sizeof(tmpaddr);
    if (getsockname(sockfd, (struct sockaddr *)&tmpaddr, &tmplen) != 0) {
        perror("getsockname error");
        return 1;
    }
    inet_ntop(AF_INET, (struct in_addr *)&tmpaddr.sin_addr, addr_str, sizeof(addr_str));
    printf("listen IP: %s, PORT: %d\n", addr_str, ntohs(tmpaddr.sin_port));
    for(;;) {
        connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &cliaddrlen);
        if (connfd < 0) {
            // 处理由信号引起的系统调用中断
            if (errno == EINTR) {
                continue;
            }
            perror("accept error");
            exit(1);
        }
        // 子进程
        if ((pid = fork()) == 0) {
            // 打印客户端IP和端口
            printf("child-process: client IP: %s, PORT: %d\n", inet_ntop(AF_INET, (struct in_addr *)&cliaddr.sin_addr, addr_str, sizeof(addr_str)), ntohs(cliaddr.sin_port));
            // 关闭引用
            if (close(sockfd) != 0){
                perror("child-process: close sockfd error");
                exit(1);
            }
            // 回射
            str_echo(connfd);
            // 子进程结束会关闭所有打开的描述符
            exit(0);
        }
        // 父进程关闭connfd,减少引用数
        if (close(connfd) != 0){
            perror("parent-process: close connfd error");
            exit(1);
        }
    }
}
/**
 * 回射字符串函数
 */
void str_echo(int fd)
{
    ssize_t n;
    char buff[MAXLINE];
    printf("str_echo: before read.\n");
    again:
    while((n = read(fd, buff, MAXLINE-1)) > 0) {
        writen(fd, buff, n);
    }
    if (n < 0 && errno == EINTR) {
        goto again;
    } else if (n < 0) {
        perror("str_echo: read error");
        exit(1);
    }
}
/**
 * 信号:SIGCHLD处理函数
 */
void sig_chld(int signo)
{
    pid_t pid;
    int stat;
    // wait会错过信号
    //pid = wait(&stat);
    // -1: 等待第一个终止的子进程
    while((pid = waitpid(-1, &stat, WNOHANG)) > 0) {
        // 在信号处理函数中调用IO操作是不合理的
        printf("child %d terminated\n", pid);
    }
    return;
}

common.c:

#include "common.h"
// static 会把int默认值设置为0
static int read_cnt;
static char *read_ptr;
static char read_buff[MAXLINE- 1];
/**
 * 每次读一个字符
 */
static size_t my_read(int fd, char *ptr)
{
    if (read_cnt <= 0) {
        again:
            if ((read_cnt = read(fd, read_buff, sizeof(read_buff))) < 0) {
                if (errno == EINTR) {
                    goto again;
                }
                return (-1);
            } else if (read_cnt == 0) {
                return (0);
            }
            read_ptr = read_buff;
    }
    read_cnt --;
    *ptr = *read_ptr++;
    return (1);
}
/**
 * 指示buff的函数,void指针会自动转换
 */
size_t readlinebuf(void **vptrptr)
{
    if (read_cnt) {
        *vptrptr = read_ptr;
    }
    return (read_cnt);
}
/**
 * 每次读n个字符
 */
size_t readn(int fd, void *vptr, size_t n)
{
    size_t nleft;
    size_t nread;
    char *ptr;
    ptr = vptr;
    nleft = n;
    while(nleft > 0) {
        if ((nread = read(fd, ptr, nleft)) < 0) {
            if (errno == EINTR) {
                nread = 0;
            } else {
                return (-1);
            }
        } else if (nread == 0) {
            break;
        }
        nleft -= nread;
        ptr   += nread;
    }
    return (n - nleft);
}
/**
 * 每次写n个字符
 */
size_t writen(int fd, const void *vptr, size_t n)
{
    size_t nleft;
    size_t nwritten;
    const char * ptr;
    ptr = vptr;
    nleft = n;
    while(nleft > 0) {
        if ((nwritten = write(fd, ptr, nleft)) <= 0) {
            if (nwritten < 0 && errno == EINTR) {
                nwritten = 0;
            } else {
                return (-1);
            }
        }
        nleft -= nwritten;
        ptr += nwritten;
    }
    return (n);
}
size_t readline(int fd, void *vptr, size_t maxlen)
{
    size_t nleft, nread;
    char *ptr, c;
    ptr = vptr;
    nleft = maxlen;
    // 保留一字节的空字符位置
    while(nleft > 1 && (nread = my_read(fd, &c)) != 0) {
        if(nread == 1) {
            *ptr++ = c;
            nleft --;
            if (c == '\n') {
                break;
            }
        } else if (nread < 0) {
            if (errno == EINTR) {
                continue;
            }
            return (-1);
        }
    }
    *ptr = 0;
    return (maxlen - nleft);
}
/**
 * 老的readline函数
 */
size_t readline_old(int fd, void *vptr, size_t maxlen)
{
    size_t n, rc;
    char c, *ptr;
    ptr = vptr;
    // n = 1,保留一个空字符
    for (n = 1; n < maxlen; n++) {
        again:
        if((rc = read(fd, &c, 1)) == 1) {
            *ptr++ = c;
            if (c == '\n') {
                break;
            }
        } else if (rc == 0) {
            // 尾部放入空字符
            *ptr = 0;
        } else {
            if (errno == EINTR) {
                goto again;
            }
            return (-1);
        }
    }
    *ptr = 0;
    return (n);
}

common.h:

#include <unistd.h>
#include <errno.h>
#ifndef MAXLINE
#define MAXLINE 4096
#endif
size_t readn(int fd, void *buff, size_t n);
size_t writen(int fd, const void *buff, size_t n);
size_t readline(int fd, void *buff, size_t maxlen);
size_t readline_old(int fd, void *buff, size_t maxlen);

协议是沟通的桥梁。

Tags: nodejs c unp
Time: 1385048934876