socket 编程 基于 select 实现的回射客户端/服务程序

github 代码 地址

  • unp.h
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAXLINE 4096
#define SERV_PORT 9877
#define LISTENQ 1024

void str_echo(int sockfd);
void err_sys(const char *, ...);


// client interface
void str_cli(FILE *fp, int sockfd);
  • echo_client.c
#include    "unp.h"
#include 
#include 
#include 

int main(int argc, char **argv) {
    int sockfd[5];
    struct sockaddr_in servaddr;
    int i=0;
    for (i=0; i<5; i++) {
        sockfd[i] = 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, "127.0.0.1", &servaddr.sin_addr);
    
        connect(sockfd[i], (struct sockaddr*)&servaddr, sizeof(servaddr));
    }
    
    str_cli(stdin, sockfd[0]);
    exit(0);    
}
  • str_cli.c
#include "unp.h"

// maxline 4096

int max(int a, int b) {
    return a>b? a : b;
}

void str_cli(FILE *fp, int sockfd) {
	int maxfdp1, stdineof;
    fd_set rset;
    char sendline[MAXLINE], receline[MAXLINE];
    int n;

    stdineof = 0;
    FD_ZERO(&rset);
    for (; ;) {
        // fileno 将标准 I/O 文件指针转换为对应的描述符
        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)) { // socket is readable
		    if ((n = read(sockfd, receline, MAXLINE)) == 0) {
				if (stdineof == 1) {
				    return;
				} else {
			 	   printf("str_cli: server terminated prematurely");
				   return;
				}
			}
       		write(fileno(stdout), receline, strlen(receline));
			bzero(receline, strlen(receline));
		}

		if (FD_ISSET(fileno(fp), &rset)) {
			if ( (n = read(fileno(stdin), sendline, MAXLINE) == 0) ){
				stdineof = 1;
				shutdown(sockfd, SHUT_WR);
				FD_CLR(fileno(fp), &rset);
				continue;
			}
	    	write(sockfd, sendline, strlen(sendline));
	    	bzero(sendline, strlen(sendline));
		}
	}
}
  • echo_service.c
#include    "unp.h"

int main(int argc, char **argv) {
    int i, maxi, maxfd, listenfd, connfd, sockfd;
	int nready, client[FD_SETSIZE];
	size_t n;
	fd_set rset, allset;
	char buf[MAXLINE];
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;
    
    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, (struct sockaddr*)&servaddr, sizeof(servaddr));
    listen(listenfd, LISTENQ);

	maxfd = listenfd;
	maxi = -1;	// client 数组当前使用项的最大下标
	for(i=0; i<FD_SETSIZE; i++) {
		client[i] = -1;
	}
	FD_ZERO(&allset);
	FD_SET(listenfd, &allset);

    for(;;) {
		rset = allset;
		nready = select(maxfd+1, &rset, NULL, NULL, NULL);
		
		if(FD_ISSET(listenfd, &rset)) {
			clilen = sizeof(cliaddr);
			connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);

			for (i=0; i<FD_SETSIZE; i++) {
				if (client[i] < 0 ) {
					client[i] = connfd;
					break;
				}
			}

			if (i == FD_SETSIZE) {
				exit(0);
			}

			FD_SET(connfd, &allset);
			if (connfd > maxfd) {
				maxfd = connfd;
			}

			if (i > maxi) {
				maxi = i;
			}

			if (--nready <= 0) {
				continue;
			}
		}

		for (i = 0; i <= maxi; i++) {
			if ((sockfd = client[i]) < 0) {
				continue;
			}

			if (FD_ISSET(sockfd, &rset)) {
				if ( ( n = read(sockfd, buf, MAXLINE)) == 0) {
					close(sockfd);
					FD_CLR(sockfd, &allset);
					client[i] = -1;
				} else {
					write(sockfd, buf, n);
				}

				if (--nready <= 0) {
					break;
				}
			}
		}
    }
}
  • CMakeList.txt
project(echo_srv)
set(CMAKE_CXX_STANDARD 11)
add_executable(echo_srv echo_service.c error.c)

# client
add_executable(echo_cli echo_client.c str_cli.c)

你可能感兴趣的:(Unix,网络编程)