//client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define ERR_EXIT(m)                        \
            do                             \
            {                              \
                perror(m);                 \
                exit(EXIT_FAILURE);        \
                                           \
            }while(0)

ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nleft = count;
    ssize_t nread;
    char *bufp = (char*)buf;

    while (nleft > 0)
    {
        if ((nread = read(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR)
                continue;
            return -1;
        }
        else if (nread == 0) //若对方已关闭
            return count - nleft;

        bufp += nread;
        nleft -= nread;
    }

    return count;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
    size_t nleft = count;
    ssize_t nwritten;
    char *bufp = (char*)buf;

    while (nleft > 0)
    {
        if ((nwritten = write(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR)
                continue;
            return -1;
        }
        else if (nwritten == 0)
            continue;

        bufp += nwritten;
        nleft -= nwritten;
    }

    return count;

}

ssize_t recv_peek(int sockfd, void *buf, ssize_t len)
{

    while (1)
    {

        int ret = recv(sockfd, buf, len, MSG_PEEK);

        if (ret == -1 && errno == EINTR)
        {

            continue;
        }

        return ret;
    }

}
ssize_t readline(int sockfd, void *buf, size_t maxline)
{
    int ret;
    int nread;
    char *bufp = buf;
    int nleft = maxline;
    while (1)
    {
        ret = recv_peek(sockfd, bufp, nleft);

        if (ret < 0)
            return  ret;
        else if (ret == 0)
            return ret;

        nread = ret;
        int i;
        for (i = 0; i < nread; ++i)
        {
            if (bufp[i] == '\n')
            {
                ret = readn(sockfd, bufp, i + 1);
                if ( ret != (i + 1))
                    exit(EXIT_FAILURE);
                return ret;
            }
        }

        if (nread > nleft)
            exit(EXIT_FAILURE);

        nleft -= nread;
        ret = readn(sockfd, bufp, nread);

        if (ret != nread)
            exit(EXIT_FAILURE);

        bufp += nread;

    }
    return -1;
}

void echo_cli (int conn)
{
    char sendbuf[1024] = {0};
    char recvbuf[1024] = {0};
    while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    {

        writen(conn, sendbuf, strlen(sendbuf));
        //read(conn, recvbuf, 1024);

        int ret = readline(conn, recvbuf, 1024);
        if (ret == -1)
            ERR_EXIT("readline failed\n");
        if (ret == 0)
        {
            printf("server close\n");
            break;
        }

        fputs(recvbuf, stdout);
        memset(sendbuf, 0, sizeof(sendbuf));
        memset(recvbuf, 0, sizeof(recvbuf));

    }

}

int main()
{

    int sockfd;

    //建立socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if (sockfd == -1)
    {
        perror("create socket failed");
        return 1;
    }

    //connect 连接server
    struct sockaddr_in serveraddr_in;
    memset(&serveraddr_in, 0, sizeof(serveraddr_in));

    serveraddr_in.sin_family = AF_INET;
    serveraddr_in.sin_port = htons(12345);   /* port in network byte order */
    serveraddr_in.sin_addr.s_addr = inet_addr("127.0.0.1");

    if ( connect(sockfd, (struct sockaddr *)&serveraddr_in, sizeof(serveraddr_in) ) == -1)
    {
        perror("connect failed");
        return 1;
    }

    struct  sockaddr_in localaddr;
    socklen_t addrlen = sizeof(localaddr);
    if (getsockname(sockfd, (struct sockaddr*)&localaddr, &addrlen) < 0)
    {

        ERR_EXIT("getsockname failed\n");
    }

    printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port));

    echo_cli(sockfd);

    return 0;
}


//server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#define ERR_EXIT(m)                        \
            do                             \
            {                              \
                perror(m);                 \
                exit(EXIT_FAILURE);        \
                                           \
            }while(0)
/*

 struct sockaddr {
               sa_family_t sa_family;
               char        sa_data[14];
           }

 struct sockaddr_in {
               sa_family_t    sin_family;
               in_port_t      sin_port;
               struct in_addr sin_addr;
           };

 struct in_addr {
               uint32_t       s_addr;
           };

*/

/*

    typedef void (*sighandler_t)(int);

    sighandler_t signal(int signum, sighandler_t handler);

*/

ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nleft = count;
    ssize_t nread;
    char *bufp = (char*)buf;

    while (nleft > 0)
    {
        if ((nread = read(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR)
                continue;
            return -1;
        }
        else if (nread == 0) //若对方已关闭
            return count - nleft;

        bufp += nread;
        nleft -= nread;
    }

    return count;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
    size_t nleft = count;
    ssize_t nwritten;
    char *bufp = (char*)buf;

    while (nleft > 0)
    {
        if ((nwritten = write(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR)
                continue;
            return -1;
        }
        else if (nwritten == 0)
            continue;

        bufp += nwritten;
        nleft -= nwritten;
    }

    return count;

}

ssize_t recv_peek(int sockfd, void *buf, ssize_t len)
{
    while (1)
    {
        int ret = recv(sockfd, buf, len, MSG_PEEK);
        if (ret == -1 && errno == EINTR)
            continue;
        return ret;
    }

}

ssize_t readline(int sockfd, void *buf, size_t maxline)
{
    int ret;
    int nread;
    char *bufp = buf;
    int nleft = maxline;
    while (1)
    {
        ret = recv_peek(sockfd, bufp, nleft);
        if (ret < 0)
            return  ret;
        else if (ret == 0)
            return ret;

        nread = ret;
        int i;
        for (i = 0; i < nread; ++i)
        {
            if (bufp[i] == '\n')
            {
                ret = readn(sockfd, bufp, i + 1);
                if ( ret != (i + 1))
                    exit(EXIT_FAILURE);
                return ret;
            }
        }

        if (nread > nleft)
            exit(EXIT_FAILURE);

        nleft -= nread;
        ret = readn(sockfd, bufp, nread);

        if (ret != nread)
            exit(EXIT_FAILURE);

        bufp += nread;

    }
    return -1;
}

void echo_srv (int conn)
{
    char recvbuf[1024];

    while (1)
    {
        memset(recvbuf, 0, sizeof(recvbuf));
        int ret = read(conn, recvbuf, 1024);/*test*/
        //int ret = readline(conn, recvbuf, 1024);
        if (ret == -1)
        {
            ERR_EXIT("readline failed");
            break;
        }
        if (ret == 0)
        {
            printf("client close\n");
            break;
        }

        fputs(recvbuf, stdout);
        writen(conn, recvbuf, strlen(recvbuf));
    }
    kill(getppid(),SIGCHLD);

}

void handle_sigchld(int sig)
{
    int mypid = 0;
    while ( (mypid = waitpid(-1, NULL, WNOHANG) ) > 0)
    {
        ;
    }

}

int main()
{
    //避免Zombie process的方法一,发送不可靠信号:
    //signal(SIGCHLD, SIG_IGN);

    //避免Zombie process的方法二,推荐使用:
    signal(SIGCHLD, handle_sigchld);

    int serverfd;

    //建立socket
    serverfd = socket(AF_INET, SOCK_STREAM, 0);

    if (serverfd == -1)
    {
        perror("create socket failed");
        return 1;
    }

    //给TCP设置地址可重用
    int on = 1;
    if (setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
    {
        printf("setsockopt failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }

    //bind 绑定端口,IP
    struct sockaddr_in serveraddr_in;
    memset(&serveraddr_in, 0, sizeof(serveraddr_in));

    serveraddr_in.sin_family = AF_INET;
    serveraddr_in.sin_port = htons(12345);   /* port in network byte order */
    serveraddr_in.sin_addr.s_addr = inet_addr("0.0.0.0");

    if ( bind(serverfd, (struct sockaddr *)&serveraddr_in, sizeof(serveraddr_in)) == -1)
    {
        perror("bind failed");
        return 1;
    }

    //listen 被动套接字,监听端口
    if (listen(serverfd, 20) == -1)
    {
        perror("listen failed");
        return 1;
    }

    while (1)
    {

        //accept 阻塞等待client连接

        int clientfd = -1;
        struct sockaddr_in clientaddr_in;

        memset(&clientaddr_in, 0, sizeof(clientaddr_in));
        socklen_t clientaddr_len = sizeof(clientaddr_in);
        if ( (clientfd = accept(serverfd, (struct sockaddr *)&clientaddr_in, &clientaddr_len) ) == -1)
        {
            perror("accept failed");
            return 1;
        }
        else
        {
            printf("\n\nclient %s:%d  connected\n\n", inet_ntoa(clientaddr_in.sin_addr), ntohs(clientaddr_in.sin_port));
        }

        int pid =fork();

        if( pid == -1 )
        {
            ERR_EXIT("fork failed");
        }
        else if (pid == 0)
        {
            //子进程关闭被动套接字
            close(serverfd);

            echo_srv(clientfd);

            return EXIT_FAILURE;
        }
        else
        {
            //主进程关闭一个主动套接字clientfd
            close(clientfd);

        }

    }

    //close 正常关闭
    close(serverfd);

    return 0;
}


备份地址: 【Socket PRGM: chat_1Vn