• unix下网络编程之I/O复用(五)


    前言

    本章节是用基本的Linux/Unix基本函数加上select调用编写一个完整的服务器和客户端例子,可在Linux(ubuntu)和Unix(freebsd)上运行,客户端和服务端的功能如下:

    客户端从标准输入读入一行,发送到服务端

    服务端从网络读取一行,然后输出到客户端

    客户端收到服务端的响应,输出这一行到标准输出

    服务端

    代码如下:

    复制代码
    #include  <unistd.h>
    #include <sys/types.h> /* basic system data types */
    #include <sys/socket.h> /* basic socket definitions */
    #include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
    #include <arpa/inet.h> /* inet(3) functions */
    #include <sys/select.h> /* select function*/

    #include <stdlib.h>
    #include <errno.h>
    #include <stdio.h>
    #include <string.h>

    #define MAXLINE 10240

    void handle(int * clientSockFds, int maxFds, fd_set* pRset, fd_set* pAllset);

    int main(int argc, char **argv)
    {
    int servPort = 6888;
    int listenq = 1024;

    int listenfd, connfd;
    struct sockaddr_in cliaddr, servaddr;
    socklen_t socklen = sizeof(struct sockaddr_in);
    int nready, nread;
    char buf[MAXLINE];
    int clientSockFds[FD_SETSIZE];
    fd_set allset, rset;
    int maxfd;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0) {
    perror("socket error");
    return -1;
    }

    int opt = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
    perror("setsockopt error");
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(servPort);


    if(bind(listenfd, (struct sockaddr*)&servaddr, socklen) == -1) {
    perror("bind error");
    exit(-1);
    }

    if (listen(listenfd, listenq) < 0) {
    perror("listen error");
    return -1;
    }

    int i = 0;
    for (i = 0; i< FD_SETSIZE; i++)
    clientSockFds[i] = -1;
    FD_ZERO(&allset);
    FD_SET(listenfd, &allset);
    maxfd = listenfd;

    printf("echo server use select startup, listen on port %d ", servPort);
    printf("max connection: %d ", FD_SETSIZE);

    for ( ; ; ) {
    rset = allset;
    nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
    if (nready < 0) {
    perror("select error");
    continue;
    }
    if (FD_ISSET(listenfd, &rset)) {
    connfd = accept(listenfd, (struct sockaddr*) &cliaddr, &socklen);
    if (connfd < 0) {
    perror("accept error");
    continue;
    }

    sprintf(buf, "accept form %s:%d ", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
    printf(buf, "");

    for (i = 0; i< FD_SETSIZE; i++) {
    if (clientSockFds[i] == -1) {
    clientSockFds[i] = connfd;
    break;
    }
    }
    if (i == FD_SETSIZE) {
    fprintf(stderr, "too many connection, more than %d ", FD_SETSIZE);
    close(connfd);
    continue;
    }
    if (connfd > maxfd)
    maxfd = connfd;

    FD_SET(connfd, &allset);
    if (--nready <= 0)
    continue;
    }

    handle(clientSockFds, maxfd, &rset, &allset);
    }
    }


    void handle(int * clientSockFds, int maxFds, fd_set* pRset, fd_set* pAllset) {
    int nread;
    int i;
    char buf[MAXLINE];
    for (i = 0; i< maxFds; i++) {
    if (clientSockFds[i] != -1) {
    if (FD_ISSET(clientSockFds[i], pRset)) {
    nread = read(clientSockFds[i], buf, MAXLINE);//读取客户端socket流
    if (nread < 0) {
    perror("read error");
    close(clientSockFds[i]);
    FD_CLR(clientSockFds[i], pAllset);
    clientSockFds[i] = -1;
    continue;
    }
    if (nread == 0) {
    printf("client close the connection ");
    close(clientSockFds[i]);
    FD_CLR(clientSockFds[i], pAllset);
    clientSockFds[i] = -1;
    continue;
    }

    write(clientSockFds[i], buf, nread);//响应客户端 有可能失败,暂不处理
    }
    }
    }

    }
    复制代码

    客户端

    代码如下:

    复制代码
    #include  <unistd.h>
    #include <sys/types.h> /* basic system data types */
    #include <sys/socket.h> /* basic socket definitions */
    #include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
    #include <arpa/inet.h> /* inet(3) functions */
    #include <sys/select.h> /* select function*/

    #include <stdlib.h>
    #include <errno.h>
    #include <stdio.h>
    #include <string.h>

    #define MAXLINE 10240
    #define max(a,b) ((a) > (b) ? (a) : (b))
    //typedef struct sockaddr SA;

    void handle(int sockfd);

    int main(int argc, char **argv)
    {
    char * servInetAddr = "127.0.0.1";
    int servPort = 6888;
    char buf[MAXLINE];
    int connfd;
    struct sockaddr_in servaddr;

    if (argc == 2) {
    servInetAddr = argv[1];
    }
    if (argc == 3) {
    servInetAddr = argv[1];
    servPort = atoi(argv[2]);
    }
    if (argc > 3) {
    printf("usage: selectechoclient <IPaddress> <Port> ");
    return -1;
    }

    connfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(servPort);
    inet_pton(AF_INET, servInetAddr, &servaddr.sin_addr);

    if (connect(connfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
    perror("connect error");
    return -1;
    }
    printf("welcome to selectechoclient ");
    handle(connfd); /* do it all */
    close(connfd);
    printf("exit ");
    exit(0);
    }


    void handle(int connfd)
    {
    FILE* fp = stdin;
    char sendline[MAXLINE], recvline[MAXLINE];
    fd_set rset;
    FD_ZERO(&rset);
    int maxfds = max(fileno(fp), connfd) + 1;
    int nread;
    for (;;) {
    FD_SET(fileno(fp), &rset);
    FD_SET(connfd, &rset);

    if (select(maxfds, &rset, NULL, NULL, NULL) == -1) {
    perror("select error");
    continue;
    }

    if (FD_ISSET(connfd, &rset)) {
    //接收到服务器响应
    nread = read(connfd, recvline, MAXLINE);
    if (nread == 0) {
    printf("server close the connection ");
    break;
    }
    else if (nread == -1) {
    perror("read error");
    break;
    }
    else {
    //server response
    write(STDOUT_FILENO, recvline, nread);
    }
    }

    if (FD_ISSET(fileno(fp), &rset)) {
    //标准输入可读
    if (fgets(sendline, MAXLINE, fp) == NULL) {
    //eof exit
    break;
    }
    else {
    write(connfd, sendline, strlen(sendline));
    }
    }

    }
    }
    复制代码

    下载和编译

    下载地址

    编译和启动服务端

    gcc selectechoserver.c -o selectechoserver

    编译和启动客户端

    gcc selectechoclient.c -o selectechoclient
  • 相关阅读:
    Docker
    Web
    爬虫
    Python
    软件脱壳
    网络抓包
    HTTPS单向认证,双向认证
    新版无完整背景图片滑块验证码
    Frida Hook
    闭包函数与装饰器
  • 原文地址:https://www.cnblogs.com/jiangzhaowei/p/8972188.html
Copyright © 2020-2023  润新知