select实现简单TCP通信(ubuntu 18.04)

一、服务器程序(server.c) 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <strings.h>

#define SERV_PORT 9999
#define MAXLINE 4096

#define SA struct sockaddr

int max(int, int);
void proSession(FILE*, int, const struct sockaddr*);
ssize_t writen(int, const void*, size_t);
char *sock_ntop(const struct sockaddr*, socklen_t);

int main(int argc, char *argv[]) {
    int listenfd, connfd;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_in servaddr;
    struct sockaddr_in cliaddr;
    
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    
    bind(listenfd, (SA *)&servaddr, sizeof(servaddr)); /* 将套接字和套接字地址结构绑定 */

    listen(listenfd, 1); /* 讲套接字转换为监听套接字 */

    clilen = sizeof(cliaddr);
    if ( (connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) > 0) {
        proSession(stdin, connfd, (SA *)&cliaddr); /* 处理会话 */
    }
    exit(1);
}

void proSession(FILE *fp, int sockfd, const struct sockaddr *addr) {
    int maxfdp1, stdineof;
    fd_set rset;
    char buf[MAXLINE];
    int n;

    stdineof = 0; 
    FD_ZERO(&rset);
    for ( ; ; ) {
        if (stdineof == 0) {
            FD_SET(fileno(fp), &rset);
        }
        FD_SET(sockfd, &rset);
        maxfdp1 = max(fileno(fp), sockfd) + 1;
        select(maxfdp1, &rset, NULL, NULL, NULL);

        if (FD_ISSET(sockfd, &rset)) {    /* 套接字描述符可读 */
            if ( (n = read(sockfd, buf, MAXLINE)) == 0) {
                if (stdineof == 1) {
                    return;
                } else {
                    exit(1);
                }
            }
            printf("%s\n", sock_ntop(addr, sizeof(addr)));
            write(fileno(stdout), buf, n);
            printf("\n");
        }
        if (FD_ISSET(fileno(fp), &rset)) { /* 输入描述符可读 */
            if ( (n = read(fileno(fp), buf, MAXLINE)) == 0) {
                stdineof = 1;
                shutdown(sockfd, SHUT_WR); /* 发送 FIN */
                FD_CLR(fileno(fp), &rset);
                continue;
            }
            writen(sockfd, buf, n);
        }
    }
}

int max(int numberone, int numbertwo) {
    return ( (numberone >= numbertwo)?numberone:numbertwo);
}

二、客户端程序(client.c)

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define SA struct sockaddr
#define SERV_PORT 9999
#define MAXLINE 4096

int max(int, int);
void proSession(FILE*, int, const struct sockaddr*);
ssize_t writen(int, const void*, size_t);
char *sock_ntop(const struct sockaddr*, socklen_t);

int main(int argc, char *argv[]) {
    pid_t childpid;
    int sockfd;
    struct sockaddr_in servaddr;

    if (argc != 2) {
        printf("usage: %s <IPaddress>\n", argv[0]);
        exit(1);
    }
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

    if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) == 0) {
        proSession(stdin, sockfd, (SA *)&servaddr); /* 处理会话 */
    }

    exit(1);
}

void proSession(FILE *fp, int sockfd, const struct sockaddr* addr) {
    int maxfdp1, stdineof;
    fd_set rset;
    char buf[MAXLINE];
    int n;

    stdineof = 0; 
    FD_ZERO(&rset);
    for ( ; ; ) {
        if (stdineof == 0) {
            FD_SET(fileno(fp), &rset);
        }
        FD_SET(sockfd, &rset);
        maxfdp1 = max(fileno(fp), sockfd) + 1;
        select(maxfdp1, &rset, NULL, NULL, NULL);

        if (FD_ISSET(sockfd, &rset)) {    /* 套接字描述符就绪 */
            if ( (n = read(sockfd, buf, MAXLINE)) == 0) {
                if (stdineof == 1) {
                    return;
                } else {
                    exit(1);
                }
            }
            printf("%s\n", sock_ntop(addr, sizeof(addr)));
            write(fileno(stdout), buf, n);
            printf("\n");
        }
        if (FD_ISSET(fileno(fp), &rset)) { /* 输入描述符就绪 */
            if ( (n = read(fileno(fp), buf, MAXLINE)) == 0) {
                stdineof = 1;
                shutdown(sockfd, SHUT_WR); /* 发送 FIN */
                FD_CLR(fileno(fp), &rset);
                continue;
            }
            writen(sockfd, buf, n);
        }
    }
}

int max(int numberone, int numbertwo) {
    return ( (numberone >= numbertwo)?numberone:numbertwo);
}

三、服务器程序和客户端程序都用到的程序

 (1)sock_ntop.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <sys/socket.h>

char *sock_ntop(const struct sockaddr *sa, socklen_t salen) {
    
    char portstr[8];    
    static char str[128];
   
    switch (sa->sa_family) {
        case AF_INET: {
            struct sockaddr_in    *sin = (struct sockaddr_in *) sa;

            if (inet_ntop(AF_INET, &sin->sin_addr, str, 
                sizeof(str)) == NULL) {
                return(NULL);
            }
            if (ntohs(sin->sin_port) != 0) {
                snprintf(portstr, sizeof(portstr), ":%d", 
                    ntohs(sin->sin_port));
                strcat(str, portstr);
            }
            return(str);
        }
        case AF_INET6: {
            struct sockaddr_in6    *sin6 = (struct sockaddr_in6 *) sa;

            str[0] = '[';
            if (inet_ntop(AF_INET6, &sin6->sin6_addr, str + 1, 
                sizeof(str) - 1) == NULL) {
                return(NULL);
            }
            if (ntohs(sin6->sin6_port) != 0) {
                snprintf(portstr, sizeof(portstr), "]:%d",
                    ntohs(sin6->sin6_port));
                strcat(str, portstr);
                return(str);
            }
            return (str + 1);
        }
        case AF_UNIX: {
            struct sockaddr_un    *unp = (struct sockaddr_un *) sa;

            if (unp->sun_path[0] == 0) {
                strcpy(str, "(no pathname bound)");
            } else {
                snprintf(str, sizeof(str), "%s", unp->sun_path);
            }
            return(str);
        }
        default: {
            snprintf(str, sizeof(str), "sock_ntop: unknown AF_xxx: %d, len %d",
                 sa->sa_family, salen);
            return(str);
        }
    }
    return (NULL);
}

 (2)writen.c

#include <unistd.h>
#include <errno.h>

ssize_t writen(int fd, const void *vptr, size_t n) {
    size_t nleft;
    ssize_t nwriten;
    const char *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ( (nwriten = write(fd, ptr, nleft)) <= 0) {
            if (nwriten < 0 && errno) {
                nwriten = 0; /* call write() again */
            } else {
                return (-1); /* error */
            }
        } 
        nleft -= nwriten;
        ptr += nwriten;
    }
    return (n - nwriten);
}

四、Makefile文件

 (1)服务器 

target=server
cc=gcc
$(target):writen.o server.o sock_ntop.o
    $(cc)  sock_ntop.o writen.o server.o -o $(target)
sock_ntop.o:sock_ntop.c
    $(cc) -c sock_ntop.c -o sock_ntop.o
writen.o:writen.c
    $(cc) -c writen.c -o writen.o
server.o:server.c
    $(cc) -c server.c -o server.o
clean:
    rm -rf *.o $(target)

 (2)客户端

target=client
cc=gcc
$(target):writen.o client.o sock_ntop.o
    $(cc)  writen.o client.o sock_ntop.o -o $(target)
writen.o:writen.c
    $(cc) -c writen.c -o writen.o
server.o:client.c
    $(cc) -c client.c -o client.o
sock_ntop.o:sock_ntop.c
    $(cc) -c sock_ntop.c -o sock_ntop.o
clean:
    rm -rf *.o $(target)
~                                  

 

上一篇:多进程并发模型--TFTP文件服务器


下一篇:getsockname和getpeername函数