select multiple IO transfer

Principle = ====: monitor, client connection and data communication events with the help of kernel and select.

correlation function

	Void FD zero (FD set * set); -- clears a set of file descriptors.

	fd_set rset;

	FD_ZERO(&rset);

	Void FD set (int FD, FD set * set); -- add the file descriptor to be monitored to the listening set

	FD_SET(3, &rset);	FD_SET(5, &rset);	FD_SET(6, &rset);


	void FD_CLR(int fd, fd_set *set); -- remove a file descriptor from the listening set.

	FD_CLR(4, &rset);

	Int fd_isset (int FD, fd_set * set); -- determines whether a file descriptor is in the listening set.

	Return value: at: 1; not at: 0;

	FD_ISSET(4, &rset);

	int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

	nfds: of all the file descriptors listening, the maximum file descriptor + 1

	readfds: read file descriptor listening collection. Incoming and outgoing parameters

	writefds: a collection of write file descriptor listeners. Incoming and outgoing parameters NULL

	exceptfds: exception file descriptor listening set incoming and outgoing parameters NULL

	Timeout: > 0: sets the timeout time for listening.

	NULL: blocking listening

	0: non blocking listening, polling
	Return value:

	>0: the total number of events in all listening sets (3).

	0: no file descriptors meeting the listening criteria

	-1:  	errno

Thinking analysis

int maxfd = 0;

lfd = socket() ;			Create socket

maxfd = lfd;

bind();					Binding address structure

listen();				Set listening upper limit

fd_set rset, allset;			Establish r Monitor set

FD_ZERO(&allset);				take r Clear listening set

FD_SET(lfd, &allset);			take lfd Add to read set.

while(1) {

rset = allset;			Save listening set

ret  = select(lfd+1, &rset, NULL, NULL, NULL);		Listens for file descriptor set events.

if(ret > 0) {							There are listening descriptors that meet the corresponding events

if (FD_ISSET(lfd, &rset)) {				// 1 in. 0 is not here.

cfd = accept();				Establish a connection and return the file descriptor used for communication

maxfd = cfd;

FD_SET(cfd, &allset);				Added to the collection of listening communication descriptors.
}

for (i = lfd+1; i <= Maximum file descriptor; i++){

FD_ISSET(i, &rset)				Yes read,write Event

read()

//Small -- big

write();
		}	
	}
}

Advantages and disadvantages of select

Disadvantage: the upper listening limit is limited by the file descriptor Max 1024.

Detect the fd that meets the conditions, and add the business logic to improve the efficiency The coding difficulty is improved.

Advantages: cross platform. win, linux, Mac OS, Unix, Unix like, mips

Test code

server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>

#include "wrap.h"

#define SERV_PORT 8965

int main(int argc, char *argv[])
{
    int listenfd, connfd;               // connect fd
    char buf[BUFSIZ];         /* #define INET_ADDRSTRLEN 16 */

    struct sockaddr_in clie_addr, serv_addr;
    socklen_t clie_addr_len;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);  

    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family= AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port= htons(SERV_PORT);
    Bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    Listen(listenfd, 128);
    
    fd_set rset, allset;                     // Define read set, backup set allset
    int ret, maxfd = 0, n, i, j;
    maxfd = listenfd;                       // Maximum file descriptor

    FD_ZERO(&allset);                       // Clear listening set
    FD_SET(listenfd, &allset);              // Add the fd to be monitored to the listening collection
    
    while (1) {
        rset = allset;                      // backups
        ret = select(maxfd+1, &rset, NULL, NULL, NULL);         // Monitor with select
        if (ret < 0) {
            perr_exit("select error");
        }

        if (FD_ISSET(listenfd, &rset)) {                        // listenfd satisfies the listening read event

            clie_addr_len = sizeof(clie_addr) ;
            connfd = Accept(listenfd, (struct sockaddr *)&clie_addr, &clie_addr_len);   // Link building - no blocking
            
            FD_SET(connfd, &allset);                            // Add the newly generated fd to the listening set to listen for data reading events

            if (maxfd < connfd)                 // Modify maxfd
                maxfd = connfd;

            if (ret == 1)                   // Note: select only returns one and is listenfd. Subsequent execution is unnecessary
                continue;
        }

        for (i = listenfd+1; i <= maxfd; i++) {         // Processing fd satisfying read events 

            if (FD_ISSET(i, &rset)) {                   // Find the fd that satisfies the read event
                n = Read(i, buf, sizeof(buf));
                if (n == 0) {                           // Detected client has closed link
                    Close(i);                           
                    FD_CLR(i, &allset);                 // Remove the closed fd from the listening set
                } else if (n == -1) {
                    perr_exit("read error");
                }

                for (j = 0; j < n; j++)
                    buf[j] = toupper(buf[j]);

                write(i, buf, n);
                write(STDOUT_FILENO, buf, n);
            }
        }
    }

    Close(listenfd);

    return 0;
}


client.c

/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 8965

int main(int argc, char *argv[])
{
    struct sockaddr_in servaddr;
    char buf[MAXLINE];
    int sockfd, n;

    sockfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr.s_addr);
    servaddr.sin_port = htons(SERV_PORT);

    Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    printf("------------connect ok----------------\n");

    while (fgets(buf, MAXLINE, stdin) != NULL) {
        Write(sockfd, buf, strlen(buf));
        n = Read(sockfd, buf, MAXLINE);
        if (n == 0) {
            printf("the other side has been closed.\n");
            break;
        }
        else
            Write(STDOUT_FILENO, buf, n);
    }
    Close(sockfd);

    return 0;
}


wrap.h

#ifndef __WRAP_H_
#define __WRAP_H_

void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
int Connect(int fd, const struct sockaddr *sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
ssize_t my_read(int fd, char *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);

#endif

wrap.c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>

void perr_exit(const char *s)
{
	perror(s);
	exit(-1);
}

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
	int n;

again:
	if ((n = accept(fd, sa, salenptr)) < 0) {
		if ((errno == ECONNABORTED) || (errno == EINTR))
			goto again;
		else
			perr_exit("accept error");
	}
	return n;
}

int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    int n;

	if ((n = bind(fd, sa, salen)) < 0)
		perr_exit("bind error");

    return n;
}

int Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
    int n;

	if ((n = connect(fd, sa, salen)) < 0)
		perr_exit("connect error");

    return n;
}

int Listen(int fd, int backlog)
{
    int n;

	if ((n = listen(fd, backlog)) < 0)
		perr_exit("listen error");

    return n;
}

int Socket(int family, int type, int protocol)
{
	int n;

	if ((n = socket(family, type, protocol)) < 0)
		perr_exit("socket error");

	return n;
}

ssize_t Read(int fd, void *ptr, size_t nbytes)
{
	ssize_t n;

again:
	if ( (n = read(fd, ptr, nbytes)) == -1) {
		if (errno == EINTR)
			goto again;
		else
			return -1;
	}
	return n;
}

ssize_t Write(int fd, const void *ptr, size_t nbytes)
{
	ssize_t n;

again:
	if ( (n = write(fd, ptr, nbytes)) == -1) {
		if (errno == EINTR)
			goto again;
		else
			return -1;
	}
	return n;
}

int Close(int fd)
{
    int n;
	if ((n = close(fd)) == -1)
		perr_exit("close error");

    return n;
}

/*Parameter 3: number of bytes to read*/
ssize_t Readn(int fd, void *vptr, size_t n)
{
	size_t  nleft;              //Number of unread bytes remaining in usigned int
	ssize_t nread;              //int bytes actually read
	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;
}

ssize_t Writen(int fd, const void *vptr, size_t n)
{
	size_t nleft;
	ssize_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;
}

static ssize_t my_read(int fd, char *ptr)
{
	static int read_cnt;
	static char *read_ptr;
	static char read_buf[100];

	if (read_cnt <= 0) {
again:
		if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
			if (errno == EINTR)
				goto again;
			return -1;
		} else if (read_cnt == 0)
			return 0;
		read_ptr = read_buf;
	}
	read_cnt--;
	*ptr = *read_ptr++;

	return 1;
}

ssize_t Readline(int fd, void *vptr, size_t maxlen)
{
	ssize_t n, rc;
	char    c, *ptr;

	ptr = vptr;
	for (n = 1; n < maxlen; n++) {
		if ( (rc = my_read(fd, &c)) == 1) {
			*ptr++ = c;
			if (c  == '\n')
				break;
		} else if (rc == 0) {
			*ptr = 0;
			return n - 1;
		} else
			return -1;
	}
	*ptr  = 0;

	return n;
}


test result

Tags: socket Unix Linux Mac

Posted on Wed, 06 Nov 2019 15:10:10 -0800 by adavis