C 如何仅在使用套接字的服务器/客户机程序中完成上一个命令后执行该命令

C 如何仅在使用套接字的服务器/客户机程序中完成上一个命令后执行该命令,c,linux,sockets,select,server,C,Linux,Sockets,Select,Server,我正在用C语言使用套接字制作一个客户机/服务器程序。服务器必须能够处理多个客户端 服务器正确设置后,客户端将连接到服务器,并发送“就绪”或“终止”。服务器收到消息,并回复“Go”或“Destroy” 如果客户端收到的回复为“Go”,则它将执行传入参数的命令。该命令的执行方式如下:系统(命令) 如果客户端收到的回复为“销毁”,则客户端关闭,服务器销毁 服务器必须能够处理多个客户端,但一次只能允许一个客户端执行命令。此外,如果客户机连接到服务器并发送“Kill”,则必须关闭队列中的所有客户机,并且必

我正在用C语言使用套接字制作一个客户机/服务器程序。服务器必须能够处理多个客户端

服务器正确设置后,客户端将连接到服务器,并发送“就绪”或“终止”。服务器收到消息,并回复“Go”或“Destroy”

如果客户端收到的回复为“Go”,则它将执行传入参数的命令。该命令的执行方式如下:系统(命令)

如果客户端收到的回复为“销毁”,则客户端关闭,服务器销毁

服务器必须能够处理多个客户端,但一次只能允许一个客户端执行命令。此外,如果客户机连接到服务器并发送“Kill”,则必须关闭队列中的所有客户机,并且必须立即销毁服务器

因此,为了实现这一点,我使用select()

所以我对这个规范有一些问题:它一次只能允许一个客户机执行一个命令

基本上,该命令是在客户端执行的。我不希望两个客户端能够同时执行一个命令

下面是一个示例(这不是代码):

因此,我的程序现在的工作方式是打印:

hello1
hello2
goodbye1
goodbye2
但我想要的是:

hello1
goodbye1
hello2
goodbye2
-->我不明白服务器如何知道命令是否完成,因为它是在客户端执行的

这是我的服务器代码结构(我不是在这里发布完整的代码,因为我的问题不是写代码本身,而是更多关于结构,以及我应该在哪里向客户端发送“Go”…所以我省略了不相关的代码):

//将所有客户端\u套接字[]初始化为0
//创建master_套接字
//bind()主\u套接字
//listen()主插槽
//接受传入连接
addrlen=sizeof(地址);
while(TRUE){
//清除插座组
FD_零(&readfds);
//将主套接字添加到集合
FD_集(主_插座和读FDS);
最大sd=主插座;
//添加要设置的子套接字
对于(i=0;i0){
FD_集(sd和readfds);
}
//最高文件描述符编号,用于选择函数
如果(标准差>最大标准差){
最大标准偏差=标准偏差;
}
}
//等待其中一个套接字上的活动,超时为NULL,因此无限期等待
活动=选择(最大sd+1,&readfds,NULL,NULL,NULL);
如果(活动<0){
printf(“选择()”);
}
//如果主套接字上发生了什么,那么它是一个传入连接
if(FD_ISSET(主插槽和读FDS)){
if((新套接字=接受(主套接字,(结构sockaddr*)和地址,(socklen\u t*)和addrlen))
我不明白服务器怎么知道命令是否正确
完成与否

简单的答案是,它不能,原因正是你提到的。服务器无法知道客户端内部发生了什么,除非客户端告诉它

因此,该问题的一个解决方案是让客户机在完成执行命令后向服务器发送另一条消息(例如“完成”)。在收到“完成”消息后,服务器将知道客户机已完成执行,并可以在另一个客户机上授权另一条命令(如果它愿意)


(顺便说一句,让客户端从网络接收字符串并将该字符串传递给system()是一个巨大的潜在安全漏洞——“邪恶”服务器可能会传递类似“rm-rf*”的字符串,并导致客户端删除其所有文件!因此,在学术环境之外运行客户端软件时要小心)

谢谢你的回答!事实上,我只发送了第一个字母(R代表ready等)。我在问题中写下了完整的文字,因为我认为这会简化问题。但很高兴知道它实际上可能会导致安全问题。至于发送另一条类似“完成”的消息这是一个好主意,但在我的作业中,我不允许添加任何其他消息。我希望有一个解决方案,比如使用PID或类似的东西。但看起来这是不可能的。我想我没有提到的另一个细节是,一次只能有一个客户机处于活动状态。这就是为什么下一个客户机只能在任务完成后启动上一个已完成。因此我不知道是否有办法知道客户端是否仍处于活动状态。(抱歉,忘记标记您了)@Jeremyth服务器可以检测客户端的TCP连接何时关闭(recv()将返回0以指示已关闭的TCP连接),因此这是检测客户端是否不再处于活动状态的一种方法(至少对于“活动”的某些定义而言)我认为这是可行的。但我一次连接了多个客户端,所以当所有客户端关闭时,或者当一个客户端关闭时,recv()会返回0吗?(经过一些测试,我想当所有客户端关闭时,它只会返回0)@Jeremy
hello1
goodbye1
hello2
goodbye2
//initialize all client_socket[] to 0
//Create master_socket
//bind() master_socket
//listen() master_socket

//accept the incoming connection 
addrlen = sizeof(address);

while(TRUE) {
    //clear the socket set
    FD_ZERO(&readfds);

    //add master socket to set
    FD_SET(master_socket, &readfds);
    max_sd = master_socket;

    //add child sockets to set
    for ( i = 0 ; i < max_clients ; i++) {
        //socket descriptor
        sd = client_socket[i];

        //if valid socket descriptor then add to read list
        if(sd > 0) {
            FD_SET( sd , &readfds);
        }

        //highest file descriptor number, need it for the select function
        if(sd > max_sd) {
            max_sd = sd;
        }
    }

    //wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely
    activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL);

    if (activity < 0) {
        printf("select()");
    }

    //If something happened on the master socket , then its an incoming connection
    if (FD_ISSET(master_socket, &readfds)) {
        if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
            perror("accept()");
            return -1;
        }

        //Check message received from the Client
        //if message == Kill then destroy everything   (this is working)

        //if message == Ready, then send Go
        //When the Client receives Go, it will proceed with the execution of his command

        //add Client to the queue
        for (i = 0; i < max_clients; i++) {
            //if position is empty
            if( client_socket[i] == 0 ) {
                client_socket[i] = new_socket;              
                break;
            }
        }
    }
}