1. 소개
실제 상황에서 사람들은 서버에 연결되는 여러 클라이언트를 자주 접하게 됩니다. 이전에 소개된 connect, recv, send 등의 기능은 모두 차단 기능이므로 리소스가 완전히 준비되지 않으면 해당 기능을 호출하는 프로세스가 슬립 상태에 들어가 I/O 다중화를 처리할 수 없게 됩니다. .
이 문서에서는 두 가지 I/O 멀티플렉싱 방법인 fcntl(), select()를 제공합니다. 리눅스는 소켓을 특별한 파일 디스크립터로 취급하기 때문에 사용자에게 큰 편리함을 제공한다는 것을 알 수 있습니다.
2. fcntl
fcntl() 함수에는 다음과 같은 특징이 있습니다.
1) 비차단 I/O: cmd를 F_SETFL로 설정하고 O_NONBLOCK으로 잠글 수 있습니다
2) 신호 기반 I/O : 예 cmd를 F_SETFL로 설정하고 O_ASYNC로 잠급니다.
Routine:
#include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/un.h> #include <sys/time.h> #include <sys/ioctl.h> #include <unistd.h> #include <netinet/in.h> #include <fcntl.h> #include <unistd.h> #define SERVPORT 3333 #define BACKLOG 10 #define MAX_CONNECTED_NO 10 #define MAXDATASIZE 100 int main() { struct sockaddr_in server_sockaddr,client_sockaddr; int sin_size,recvbytes,flags; int sockfd,client_fd; char buf[MAXDATASIZE]; /*创建socket*/ if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){ perror("socket"); exit(1); } printf("socket success!,sockfd=%d\n",sockfd); /*设置sockaddr结构*/ server_sockaddr.sin_family=AF_INET; server_sockaddr.sin_port=htons(SERVPORT); server_sockaddr.sin_addr.s_addr=INADDR_ANY; bzero(&(server_sockaddr.sin_zero),8); /*将本地ip地址绑定端口号*/ if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))==-1){ perror("bind"); exit(1); } printf("bind success!\n"); /*监听*/ if(listen(sockfd,BACKLOG)==-1){ perror("listen"); exit(1); } printf("listening....\n"); /*fcntl()函数,处理多路复用I/O*/ if((flags=fcntl( sockfd, F_SETFL, 0))<0) perror("fcntl F_SETFL"); flags |= O_NONBLOCK; if(fcntl( sockfd, F_SETFL,flags)<0) perror("fcntl"); while(1){ sin_size=sizeof(struct sockaddr_in); if((client_fd=accept(sockfd,(struct sockaddr*)&client_sockaddr,&sin_size))==-1){ //服务器接受客户端的请求,返回一个新的文件描述符 perror("accept"); exit(1); } if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))==-1){ perror("recv"); exit(1); } if(read(client_fd,buf,MAXDATASIZE)<0){ perror("read"); exit(1); } printf("received a connection :%s",buf); /*关闭连接*/ close(client_fd); exit(1); }/*while*/ }
프로그램 실행:
[root@localhost net]# ./fcntl socket success!,sockfd=3 bind success! listening.... accept: Resource temporarily unavailable
accept 리소스를 사용할 수 없으면 프로그램이 자동으로 반환되는 것을 볼 수 있습니다.
빨간색 굵은 코드를
if((flags=fcntl( sockfd, F_SETFL, 0))<0) perror("fcntl F_SETFL"); flags |= O_ASYNC; if(fcntl( sockfd, F_SETFL,flags)<0) perror("fcntl");
로 바꾸면 실행 결과는 다음과 같습니다.
[root@localhost net]# ./fcntl1 socket success!,sockfd = 3 bind success! listening...
프로세스가 다른 관련 신호가 이를 구동할 때까지 기다리고 있음을 알 수 있습니다.
셋, 선택
#include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/un.h> #include <sys/time.h> #include <sys/ioctl.h> #include <unistd.h> #include <netinet/in.h> #define SERVPORT 3333 #define BACKLOG 10 #define MAX_CONNECTED_NO 10 #define MAXDATASIZE 100 int main() { struct sockaddr_in server_sockaddr,client_sockaddr; int sin_size,recvbytes; fd_set readfd; fd_set writefd; int sockfd,client_fd; char buf[MAXDATASIZE]; /*创建socket*/ if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){ perror("socket"); exit(1); } printf("socket success!,sockfd=%d\n",sockfd); /*设置sockaddr结构*/ server_sockaddr.sin_family=AF_INET; server_sockaddr.sin_port=htons(SERVPORT); server_sockaddr.sin_addr.s_addr=INADDR_ANY; bzero(&(server_sockaddr.sin_zero),8); /*将本地ip地址绑定端口号*/ if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))==-1){ perror("bind"); exit(1); } printf("bind success!\n"); /*监听*/ if(listen(sockfd,BACKLOG)==-1){ perror("listen"); exit(1); } printf("listening....\n"); /*select*/ FD_ZERO(&readfd); // 将readfd 清空 FD_SET(sockfd,&readfd); //将sockfd加入到readfd集合中 while(1){ sin_size=sizeof(struct sockaddr_in); if(select(MAX_CONNECTED_NO,&readfd,NULL,NULL,(struct timeval(FD_ISSET(sockfd,&readfd)>0){ // FD_ISSET 这个宏判断 sockfd 是否属于可读的文件描述符。从 sockfd 中读入, 输出到标准输出上去. if((client_fd=accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size))==-1){ //client_sockaddr:客户端地址 perror("accept"); exit(1); } if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))==-1){ perror("recv"); exit(1); } if(read(client_fd,buf,MAXDATASIZE)<0){ perror("read"); exit(1); } printf("received a connection :%s",buf); }/*if*/ close(client_fd); }/*select*/ }/*while*/ } 运行结果如下: [root@localhost net]# gcc select1.c -o select1 [root@localhost net]# ./select1 socket create success! bind success! listening...
위 내용은 Linux 소켓을 사용하여 서버에 연결하기 위해 여러 클라이언트를 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!