简体   繁体   English

具有多个客户端的简单 TCP 服务器 C/unix

[英]Simple TCP server with multiple clients C/unix

I'm having problems understanding socket programming and need some help.我在理解套接字编程时遇到问题,需要一些帮助。 I am suppose to modify my server code that I have written to accept 3 clients.我想修改我编写的服务器代码以接受 3 个客户端。 I know I am suppose to use a fork for each client, but I am not sure how to implement this into my code.我知道我应该为每个客户端使用一个 fork,但我不确定如何将其实现到我的代码中。 Here is my original code that I wrote for one client.这是我为一位客户编写的原始代码。 Any help would be appreciated.任何帮助,将不胜感激。

Server:服务器:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno;
     socklen_t clilen;
     char buffer[256];
     struct sockaddr_in serv_addr, cli_addr;
     int n;
     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error("ERROR opening socket");
     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);
     if (bind(sockfd, (struct sockaddr *) &serv_addr,
              sizeof(serv_addr)) < 0) 
              error("ERROR on binding");
     listen(sockfd,5);
     clilen = sizeof(cli_addr);
     newsockfd = accept(sockfd, 
                 (struct sockaddr *) &cli_addr, 
                 &clilen);
     if (newsockfd < 0) 
          error("ERROR on accept");
     bzero(buffer,256);
     n = read(newsockfd,buffer,255);
     if (n < 0) error("ERROR reading from socket");
     printf("Here is the message: %s\n",buffer);
     n = write(newsockfd,"I got your message",18);
     if (n < 0) error("ERROR writing to socket");
     close(newsockfd);
     close(sockfd);
     return 0; 
}

Client:客户:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    char buffer[256];
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port\n", argv[0]);
       exit(0);
    }
    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    //printf("h_addr: %s\n", inet_ntoa(serv_addr.sin_addr));
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
        error("ERROR connecting");
    printf("Please enter the message: ");
    bzero(buffer,256);
    fgets(buffer,255,stdin);
    n = write(sockfd,buffer,strlen(buffer));
    if (n < 0) 
         error("ERROR writing to socket");
    bzero(buffer,256);
    n = read(sockfd,buffer,255);
    if (n < 0) 
         error("ERROR reading from socket");
    printf("%s\n",buffer);
    close(sockfd);
    return 0;
}

Here is your's modified server code to handle multiple clients using fork这是您修改后的服务器代码,用于使用 fork 处理多个客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

void error(const char *msg) {
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[]) {
    int sockfd, newsockfd, portno;
    socklen_t clilen;
    char buffer[256];
    struct sockaddr_in serv_addr, cli_addr;
    int n;
    if (argc < 2) {
         fprintf(stderr, "ERROR, no port provided\n");
         exit(1);
    }
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
         error("ERROR opening socket");
    bzero((char *) &serv_addr, sizeof(serv_addr));
    portno = atoi(argv[1]);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);
    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        error("ERROR on binding");
    listen(sockfd, 5);
    clilen = sizeof(cli_addr);
    //Below code is modified to handle multiple clients using fork
    //------------------------------------------------------------------
    int pid;
    while (1) {
         newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
         if (newsockfd < 0)
                error("ERROR on accept");
         //fork new process
         pid = fork();
         if (pid < 0) {
              error("ERROR in new process creation");
         }
         if (pid == 0) {
            //child process
            close(sockfd);
            //do whatever you want
            bzero(buffer, 256);
            n = read(newsockfd, buffer, 255);
            if (n < 0)
                error("ERROR reading from socket");
            printf("Here is the message: %s\n", buffer);
            n = write(newsockfd, "I got your message", 18);
            if (n < 0)
                 error("ERROR writing to socket");
            close(newsockfd);
          } else {
             //parent process
             close(newsockfd);
          }
    }
    //-------------------------------------------------------------------
   return 0;
}

akashchandrakar's answer is correct, except it'll accept infinite -bound to how many process you can spawn- clients. akashchandrakar 的答案是正确的,除了它会接受无限绑定到您可以生成多少进程的客户端。

Doing this with threads is much easier but I guess your requirement is doing it with processes.用线程来做这件事要容易得多,但我猜你的要求是用进程来做。

To limit it to 3, you need to communicate with spawned processes and get information about their lifetime.要将其限制为 3,您需要与生成的进程通信并获取有关其生命周期的信息。 I can think of 4 ways.我能想到4种方法。

It's also important to make those variables volatile , since they shouldn't be optimized away.使这些变量volatile也很重要,因为它们不应该被优化掉。

  • When child process is terminated, you receive SIGCHLD signal.当子进程终止时,您会收到 SIGCHLD 信号。 So you can set a signal handler, keep a global counter, increase when you spawn and decrease when you receive the signal.所以你可以设置一个信号处理程序,保持一个全局计数器,当你产生时增加,当你收到信号时减少。 See.看。 https://docs.oracle.com/cd/E19455-01/806-4750/signals-7/index.html https://docs.oracle.com/cd/E19455-01/806-4750/signals-7/index.html

  • You can create a shared memory for mutex, condition variable and counter.您可以为互斥锁、条件变量和计数器创建共享内存。 Every child process can bump up to counter, and if it's 3, you can wait on condition variable.每个子进程都可以撞到计数器,如果是 3,则可以等待条件变量。 When child process exited, it can decrease the counter and signal the condition variable.当子进程退出时,它可以减少计数器并通知条件变量。

  • You can create a semaphore, starts from 3, and use that.您可以创建一个信号量,从 3 开始,然后使用它。 In order to share it with child processes, I guess SysV IPC semaphores can be shared between processes.为了与子进程共享,我猜 SysV IPC 信号量可以在进程之间共享。 See.看。 https://stackoverflow.com/a/6848998 https://stackoverflow.com/a/6848998

  • Also there were pipes and message queues but it's been 5 years since I got systems programming class.还有管道和消息队列,但自从我上系统编程课已经 5 年了。 :D :D

Last 3 is problematic since if child process went to non-happy path, segmentation fault etc, it can't update the counter and signal other processes.最后 3 个是有问题的,因为如果子进程进入非快乐路径、分段错误等,它无法更新计数器并向其他进程发送信号。 So I'd go with first one.所以我会选择第一个。 But I don't think school project would have any problems with last 3 either.但我不认为学校项目对最后 3 项也有任何问题。

Edit: Ah, there was also something like ADDR_REUSEPORT flag, when you spawn multiple process, all of them could listen the socket and OS would round robin the incoming connections.编辑:啊,还有类似 ADDR_REUSEPORT 标志的东西,当你产生多个进程时,它们都可以监听套接字,操作系统会循环传入连接。 So you can just spawn 3 processes which listens same socket.因此,您可以生成 3 个侦听相同套接字的进程。 Actually I'm not even sure if you need that flag for child processes, it was for all processes spawned by same effective user ID.实际上,我什至不确定您是否需要为子进程设置该标志,它适用于由相同有效用户 ID 生成的所有进程。

Server.c服务器.c

         #define RUNNING_DIR     "/tmp "
        define LOCK_FILE       "exampled.lock"
        #define LOG_FILE        "exampled.log"
        #include <fcntl.h>
   #include <signal.h>
     #include <unistd.h>
     #include <stdlib.h>
     #include <sys/types.h>
   #include  <sys/socket.h>
   #include  <stdio.h>
  #include  <netinet/in.h>
     #include  <sys/time.h>
      #include  <sys/ioctl.h>
    #include  <unistd.h>
    #include <string.h>
  #include <arpa/inet.h>
       #include <errno.h>
 #include <time.h>
  #include <string.h>
        #define MAXCLIENT 100
        #define TRUE 1
        #define FALSE 0
       #define MINUTE 5
      struct Client
      {
    char name[256];
char *clientAddr;
int fd;
     };
    int client;
      int i;
    int minute;
    struct Client p[100];
     void remove_client(int); 
     int search_addr(char [],int *);
     void log_message(filename,message)
char *filename;
char *message;
      {
FILE *logfile;
logfile=fopen(filename,"a");
if(!logfile) return;
fprintf(logfile,"%s\n",message);
fclose(logfile);
    }

      void catch_int(sig)
int sig;
  {
log_message(LOG_FILE,strsignal(sig));
     }

    void daemonize()
   {
int i,lfp,lfp1;
char str[10];
    signal(SIGINT, catch_int);
for ( i=0;i<65;i++)
{
    if ( i!=32 && i!=33 )
        if (signal(i, SIG_IGN) != SIG_IGN)
            signal(i, catch_int);
}
if(getppid()==1) return; /*    already a daemon */
i=fork();
if (i<0) exit(1); /*    fork error */
if (i>0) exit(0); /*    parent exits */
/*    child (daemon) continues */
setsid(); /*    obtain a new process group */
for (i=getdtablesize();i>=0;--i) close(i); /*    close all descriptors */
i=open("/dev/null",O_RDWR); dup(i); dup(i); /*    handle standart I/O */
umask(027); /*    set newly created file permissions */
chdir(RUNNING_DIR); /*    change running directory */
lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0640);
lfp1=open(LOG_FILE,O_RDWR|O_CREAT,0640);
if (lfp<0) exit(1); /*    can not open */
if (lockf(lfp,F_TLOCK,0)<0) exit(0); /*    can not lock */
/*    first instance continues */
sprintf(str,"%d\n",getpid());
write(lfp,str,strlen(str)); /*    record pid to lockfile */
   }
       int main()
         {
     daemonize();
   time_t rawtime;
  struct tm *info;
      time(&rawtime);
     info = localtime(&rawtime );
     minute=MINUTE + info->tm_min;
      int server_sockfd, client_sockfd;
     int server_len, client_len;
      struct sockaddr_in server_address;
       struct sockaddr_in client_address;
      int result;
        fd_set readfds, testfds;
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(5000);
server_len = sizeof(server_address);
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
listen(server_sockfd, 5);
FD_ZERO(&readfds);
FD_SET(server_sockfd, &readfds);
while(1) {
    char ch;
    int fd;
    int nread;
    testfds = readfds;
    result = select(FD_SETSIZE,&testfds,NULL,NULL,NULL);
    if(result < 1) {
        perror("server5");
        exit(1);
    }
    for(fd = 0; fd < FD_SETSIZE; fd++) {
        time(&rawtime);
        info = localtime(&rawtime );
        if(FD_ISSET(fd,&testfds)) {
            if(fd == server_sockfd) {
                int j=0;
                char Clients[1096];
                memset( Clients, '\0', sizeof(Clients) );
                client_len = sizeof(client_address);
                client_sockfd = accept(server_sockfd,
                        (struct sockaddr          *)&client_address, &client_len);
                client++;
                char *sAddress = inet_ntoa(client_address.sin_addr);
                p[i].clientAddr=strdup(sAddress);
                sprintf(p[i].name,"client%d",client);
                p[i].fd=client_sockfd;
                for (j =0; j < client; j++)
                {
                    strcat(Clients,p[j].clientAddr);
                    strcat(Clients," ");
                    strcat(Clients,p[j].name); 
                    strcat(Clients,"\n");  
                }
                for ( j=0; j < client ; j++)
                {
                    send(p[j].fd,Clients,strlen(Clients),0);
                }
                i++;
                FD_SET(client_sockfd, &readfds);
            }
            else {
                ioctl(fd, FIONREAD, &nread);
                if(nread == 0) {
                    close(fd);
                    remove_client(fd);
                    FD_CLR(fd, &readfds);
                }
                else {
                    char addr[100];
                    char *msg;
                    char sucess[]="Message from Ip Address:";
                    int n;
                    int des=0;
                    int found;
                    n=recv(fd,addr,sizeof(addr),0);
                    addr[n]='\0';
                    strtok_r (addr, "\n", &msg);
                    found=search_addr(addr,&des);
                    if ( found )
                    {
                        strcat(sucess,addr); 
                        send(fd,"Message Has been  
                                sucessfully sended\n",36,0); 
                        strcat(sucess,"\n");
                        strcat(sucess,msg);
                        send(des,sucess,strlen(sucess),0); 
                    }
                    else
                    {
                        send(fd,"Message Sending    Failed..\n",27,0);
                    } 
                    sleep(5);
                }

            }
        }
        if ( minute == info->tm_min)
        {
            int j=0;
            char Clients[1096];
            memset( Clients, '\0', sizeof(Clients) );
            sprintf(Clients,"Now Currently Available ip:\n");
            for (j =0; j < client; j++)
            {
                strcat(Clients,p[j].clientAddr);
                strcat(Clients," ");
                strcat(Clients,p[j].name); 
                strcat(Clients,"\n");  
            }
            for ( j=0; j < client ; j++)
            {
                send(p[j].fd,Clients,strlen(Clients),0);
            }
            minute=minute+MINUTE;
        }
    }
}

} }

         void remove_client(int fd)
      {
int j=0;
int pos;
for  ( j=0; j< client ; j++)
{
    if ( p[j].fd == fd )
    {
        pos=j;
        break;         
    }            
}
for ( j=pos+1 ; j < client ; j++)
{
    sscanf( p[j].name, "%s",p[pos].name);
    p[pos].clientAddr=p[j].clientAddr;
    p[pos].fd=p[j].fd;
    pos++;

}
client--;
i--;

} int search_addr(char address[],int *des) { char *name; } int search_addr(char address[],int *des) { char *name; int j;国际j; char temp_addr[100];字符临时地址[100]; strcpy(temp_addr,address); strcpy(temp_addr,地址); strtok_r (temp_addr, " ", &name); strtok_r (temp_addr, " ", &name); for ( j=0; j< client ; j++ ) { for ( j=0; j< 客户端 ; j++ ) {

         if ( (strcmp(temp_addr,p[j].clientAddr)==0) &&          (strcmp(name,p[j].name)==0))
    {
        *des=p[j].fd;
        return TRUE;
    }
}
return FALSE;

} }

Client.c客户端

 #include <stdlib.h>
    #include <sys/socket.h>
  #include <sys/types.h>
   #include <netinet/in.h>
   #include <netdb.h>
 #include <stdio.h>
   #include <string.h>
   #include <stdlib.h>
   #include <unistd.h>
      #include <errno.h>
        #include <arpa/inet.h> 
      // #define  SERVERIP "192.168.12.61"
       #define  PORT 5000
    #include <setjmp.h>
    #define MAXSLEEP 128
     int main(int argc, char *argv[])
        {
int Response=1;
int sockfd = 0, n = 0;
char recvBuff[1024];
struct sockaddr_in serv_addr; 
jmp_buf env;
if(argc != 2)
{
    printf("\n Usage: %s <ip of server> \n",argv[0]);
    return 1;
} 
memset(recvBuff, '0',sizeof(recvBuff));
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
    printf("\n Error : Could not create socket \n");
    return 1;
} 

memset(&serv_addr, '0', sizeof(serv_addr)); 

serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT); 

if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
{
    printf("\n inet_pton error occured\n");
    return 1;
} 
setjmp(env);
    if (connect_retry ( sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr) )< 0 )
{
    printf("Time Limited Exceeded....\n ");
    return 0;

} 
for (;;) {
    char str[100];
    char addr[100];
    if ( Response == 1 )
    {
        int z = recv(sockfd,recvBuff,sizeof(recvBuff),0); //MSG_DONTWAIT
        if ( z == -1 )
        {
        }
        else if ( z == 0 )
        {
            printf("Server Failed ....\n");
            longjmp(env, 2);
            break;
        }
        else
        {
            recvBuff[z] = 0;
            printf("'%s'",recvBuff);
            sleep(1);
        }
    }
    else
    {
        int z = recv(sockfd,recvBuff,sizeof(recvBuff),MSG_DONTWAIT); //MSG_DONTWAIT
        if ( z == -1 )
        {
        }
        else if ( z == 0 )
        {
            printf("Server Failed...\n");
            longjmp(env, 2);
            break;
        }
        else
        {
            recvBuff[z] = 0;
            printf("'%s'",recvBuff);
            sleep(1);
        }

    }
    fd_set rfdset;
    FD_ZERO(&rfdset);
    struct  timeval tv;
    tv.tv_sec = 10;
    tv.tv_usec = 0;
    FD_SET(STDIN_FILENO, &rfdset);
    int bReady = select(STDIN_FILENO+1,&rfdset,NULL,NULL,&tv);
    if (bReady > 0)
    {
        //  printf("Chat with Client Address: \n");
        if( fgets (addr, 4096, stdin)!=NULL ) {
        }
        printf("Enter Message:\n");
        if( fgets (str, 4096, stdin)!=NULL ) {
        }
        strcat(addr,str);
        send(sockfd,addr,strlen(addr),0);
    }
    Response=0;
}
return 0;

} }

int
     connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen)
      {
int nsec;

/*
 *          * Try to connect with exponential backoff.
 *                   */
for (nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) {
    if (connect(sockfd, addr, alen) == 0) {
        /*
         *                          * Connection accepted.
         *                                                   */
        return(0);
    }

    /*
     *                  * Delay before trying again.
     *                                   */
    printf("Waiting For Server....\n");
    if (nsec <= MAXSLEEP/2)
        sleep(nsec);
}
return(-1);

} }

server send the ipaddress of new client connection and each few minutes 192.168.12.61 client1 ENter the msg Hello服务器发送新客户端连接的 IP 地址,每隔几分钟 192.168.12.61 client1 输入 msg Hello

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM