int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
參數:
- nfds:監控的文件描述符集里最大文件描述符加1,因為此參數會告訴內核檢測前多少個文件文件描述符
- readfs:監控有讀數據到達文件描述符集合,傳入傳出參數
- writefds:監控寫數據到達文件描述符集合,傳入傳出參數
- exceptfds:監控異常發生到達文件描述符集合,如帶外數據到達異常,傳入傳出參數
- timeout:定時阻塞監控時間。
?
#include <sys/select.h>
void FD_CLR(int fd, fd_set *set); // 把文件描述符集里fd位清0
int FD_ISSET(int fd, fd_set *set); // 測試文件描述符集里fd是否置1
void FD_SET(int fd, fd_set *set); // 把文件描述符集合里fd位置1
void FD_ZERO(fd_set *set); // 把文件描述符集合里所有位清0
二、select函數用法圖解
三、實驗一
#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) {//ECONNABORTED 發生在重傳(一定次數)失敗后,強制關閉套接字//EINTR 進程被信號中斷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;n = connect(fd, sa, salen);if (n < 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;elsereturn -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;elsereturn -1;}return n;
}int Close(int fd)
{int n;if ((n = close(fd)) == -1)perr_exit("close error");return n;
}
?
#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
- 服務端server.c
//server.c
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/select.h>
#include "wrap.h"#define SERV_PROT 6666int main(int argc, char *argv[])
{int i, j, n, nread;int maxfd = 0;int listenfd, connfd;char buf[BUFSIZ];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_PROT);Bind(listen, (struct sockaddr*)&serv_addr, sizeof(serv_addr));Listen(listenfd, 128);fd_set rset, allset;maxfd = listenfd;FD_ZERO(&allset);FD_SET(listenfd, &allset);while (1){rset = allset;nread = select(maxfd + 1, &rset, NULL, NULL, NULL); //每次循環都從新設置select監控信號集if(nread < 0)perr_exit("select error");if (FD_ISSET(listenfd, &rset)) //說明有新的客戶端連接請求{clie_addr_len = sizeof(clie_addr);connfd = Accept(listenfd, (struct sockaddr *)&clie_addr, &clie_addr_len);FD_SET(connfd, &allset); //向監控文件描述符集合allset添加新的文件描述符connfdif (maxfd < connfd)maxfd = connfd;if (0 == --nread) //說明select只返回一個,并且listenfd,后續執行無須執行continue;}for (i = listenfd + 1; i <= maxfd; i++){if (FD_ISSET(i, &rset)){if ((n = Read(i, buf, sizeof(buf))) == 0) //當client關閉鏈接時, 服務端也關閉對應鏈接{Close(i);FD_CLR(i, &allset); //解除select對此文件描述符的監控}else if(n == -1)perr_exit("read error");for (j = 0; j < n; ++j)buf[j] = toupper(buf[j]);Write(i, buf, n);}}}Close(listenfd);return 0;
}
- 客戶端:clent.c
#include <arpa/inet.h>
#include "wrap.h"#define SERV_IP "192.168.245.139"
#define SERV_PORT 6666int main(void)
{int sfd, len;struct sockaddr_in serv_addr;char buf[BUFSIZ];sfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);serv_addr.sin_port = htons(SERV_PORT);Connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));while (1) {fgets(buf, sizeof(buf), stdin);int r = Write(sfd, buf, strlen(buf));printf("Write r ======== %d\n", r);len = Read(sfd, buf, sizeof(buf));printf("Read len ========= %d\n", len);Write(STDOUT_FILENO, buf, len);}Close(sfd);return 0;
}
- 公共頭文件集程序:wrap.h、wrap.c
輸出結果:
客戶端:
服務端: