【问题标题】:Send data to specific client via server通过服务器向特定客户端发送数据
【发布时间】:2013-08-14 08:56:41
【问题描述】:

我正在用 C 编写一个服务器和两个客户端。一个客户端是接收命令的“从”类型客户端,另一个是发送命令的“主”类型客户端。我希望从服务器的多个实例连接到服务器,并能够通过服务器从主服务器向特定的从服务器发送命令。

我的问题是如何指定要将命令发送到的客户端?

这是我的服务器的一个非常基本的示例(没有错误检查) 我的实际服务器具有错误检查等功能,但是发布时间太长(并且无论如何都不会为任何人编译,因为我的项目中存在对其他文件的依赖关系)

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

int server_portnumber = 51739;

int main() {
  int listenFD;
  int connectFD;
  socklen_t length;
  struct sockaddr_in s1;
  struct sockaddr_in s2;

  listenFD = socket( AF_INET , SOCK_STREAM , 0 );
  memset( &s1, 0, sizeof( s1 ) );
  s1.sin_family = AF_INET;
  s1.sin_addr.s_addr = INADDR_ANY;
  s1.sin_port = server_portnumber;

  bind( listenFD , (struct sockaddr*) &s1 , sizeof( s1 ) );

  length = sizeof( s1 );
  getsockname( listenFD ,  (struct sockaddr*) &s1 , &length );
  listen( listenFD , 512 );

  signal( SIGCHLD , SIG_IGN );

  while(1) {
    length = sizeof( s2 );
    connectFD = accept( listenFD , (struct sockaddr*) &s2 , &length );
    if( !fork() ){
      close( listenFD );
      int select = 0;
      while( read( connectFD , &select, sizeof( int ) ) ) {
        switch( select ) {
          case 10:
            // opcode
            break;
          case 20:
            // command
            break;
          default:
            break;
        }
      }
      close( connectFD );
      exit(0);
    }
  }
}

【问题讨论】:

  • 请显示实际编译的代码,并演示您需要帮助的问题。这段代码充满了不匹配的括号和大括号、错误类型的参数(例如,将 int 而不是 addrlen 的指针传递给 getsocknameaccept),明显缺少代码(如果你不调用 @987654324 @ 在某个地方获取您的端口号,您的代码将无法在任何英特尔机器上运行),不那么明显的缺失代码(您的 fork 循环最好不要像那样结束……如果您收到错误消息accept,你真的只是忽略它并尽可能快地一遍又一遍地得到同样的错误吗?)
  • 我已经更新了 OP 中的代码。抱歉,我只是从我的实际服务器中提取代码来举个例子,因为在这里发布的内容太多(而且它对我项目中的其他文件有很多依赖关系)为了简洁起见,我没有在这个例子中包含错误检查.

标签: c sockets network-programming


【解决方案1】:

您需要为每个客户端分配一个 ID,让 master 以某种方式了解这些 ID,并为服务器提供一些从 ID 映射到套接字的方法。

“某种方式”是相当模糊的……因为有无穷无尽的可能性,都具有不同的设计含义。例如,您可以通过以下方式引用客户:

  • Socket fd.
  • 对等地址(主机和端口)。
  • 自动编号索引。
  • 连接时间。
  • 随机生成的密钥。
  • 在协议中的某个登录步骤期间从客户端收到的密钥。

对于其中的大多数,您必须告诉主人有一个具有相关 ID 的新客户端可用。当主服务器想要向特定客户端发送消息时,它会将该 ID 发回,这意味着您将需要某种映射(如果它像 fd 或自动索引这样简单,则只是一个数组;可能是哈希表或平衡树(如果是任意字符串)来查找 ID 并获取 fds。

为了更具体,请考虑一个聊天协议,例如 IRC,但要简单得多。

当你第一次连接时,没有人可以和你说话,你也不能和其他人说话。

服务器维护一个哈希表来将昵称映射到套接字 fds。

当您发送/nick NICKNAME 命令时,其中NICKNAME 是一个有效的昵称(例如,任何其他人尚未使用的最多20 个字母和数字的序列),服务器会在哈希表映射中添加一个条目将您的昵称发送给您的 fd,并向所有人发送一条消息,说“NICKNAME 已加入”。

当您发送/msg NICKNAME MESSAGE 命令时,服务器会在哈希表中查找NICKNAME。如果它找到了,它会用相应的 fd 将 MESSAGE 发送到套接字。

【讨论】:

  • 我尝试将 socketFD(从 accept() 返回)保存在索引数组中,但是当我为每个连接的新客户端打印出 FD 时,它总是相同的。我在这里保存了错误的 FD 吗?
  • @PseudoPsyche:嗯,你给出的代码甚至还没有接近编译(大括号和括号不匹配,accept 的参数类型错误等),而且它没有在任何地方都没有任何名为 socketFD 的东西,这使得任何人都很难为您调试它……
  • 好的,与您的原始代码不同,您的新代码让父级立即在每个新套接字上执行close(connectFD)。除了意味着有问题的fd 现在可以重新分配之外,意味着,即使你确实有某种方式来识别客户,你也不会'无论如何都无法向它发送消息,因为您的套接字没有 fd。
  • 作为建议,使用线程服务器执行您尝试执行的操作可能要简单得多(非常相似,除了将if (!fork()) 主体包装在一个函数中并调用@987654330 @ 代替它),或 select 循环。无论哪种方式,您都不必了解有关如何跨进程共享文件和其他内容的问题。
  • @PseudoPsyche:至少还有一个问题——如果你不在任何地方调用htons,你将监听端口 7114 而不是 51739(除非你在PowerPC 或其他一些大端机器)。但除此之外,它看起来应该足够好,至少可以进行实验。
猜你喜欢
  • 2020-12-27
  • 2015-02-10
  • 2018-11-06
  • 1970-01-01
  • 2019-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-07
相关资源
最近更新 更多