Maison  >  Article  >  Opération et maintenance  >  Comment implémenter plusieurs clients pour se connecter au serveur à l'aide du socket Linux

Comment implémenter plusieurs clients pour se connecter au serveur à l'aide du socket Linux

WBOY
WBOYavant
2023-05-20 23:10:102160parcourir

1. Introduction

Dans des situations réelles, les gens rencontrent souvent la situation où plusieurs clients se connectent au serveur#🎜 🎜 #. Étant donné que les fonctions introduites précédemment telles que connect, recv, send, etc. sont toutes des fonctions bloquantes, si les ressources ne sont pas entièrement préparées, le processus appelant la fonction entrera en état de veille, ce qui ne pourra pas gérer le multiplexage d'E/S. .

Cet article donne

deux méthodes de multiplexage d'E/S : fcntl(), select(). On peut voir que puisque Linux traite le socket comme un descripteur de fichier spécial, cela apporte une grande commodité aux utilisateurs.

2. fcntl

fcntl() a les caractéristiques suivantes :

1) Non bloquant I /O : Vous pouvez définir cmd sur F_SETFL et verrouiller sur O_NONBLOCK

2) E/S pilotées par signal : Vous pouvez définir cmd sur F_SETFL et verrouiller sur 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*/
}
Exécutez le programme :


[root@localhost net]# ./fcntl
socket success!,sockfd=3
bind success!
listening....
accept: Resource temporarily unavailable
Vous pouvez voir que lorsque la ressource d'acceptation n'est pas disponible, le programme reviendra automatiquement.


Si vous remplacez le code rouge gras par :

if((flags=fcntl( sockfd, F_SETFL, 0))<0)
      perror("fcntl F_SETFL");
    flags |= O_ASYNC;
    if(fcntl( sockfd, F_SETFL,flags)<0)
      perror("fcntl");
Le résultat en cours d'exécution est le suivant :


[root@localhost net]# ./fcntl1
socket success!,sockfd = 3
bind success!
listening...
Vous pouvez le voir, le processus attend qu'un autre signal pertinent le conduise.


三、select

#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...

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer