【问题标题】:Efficient way to transfer data between 2 clients over TCP with intermediate server通过 TCP 与中间服务器在 2 个客户端之间传输数据的有效方法
【发布时间】:2012-12-21 08:39:23
【问题描述】:

这是出于教育目的(大学作业)。

我需要用 C 语言为 Linux 编写一个客户端-服务器程序。 (我已经有了那个部分。客户端连接到服务器,它可以毫无问题地发送和接收文件......)。

当客户端连接到服务器时,它会向服务器发送客户端上的文件列表。所以服务器有一个它的客户端上所有文件的列表。

客户端 A 可以向服务器请求文件“test.txt”,服务器知道该文件在客户端 B 上,该文件应该从 B 传输到 A。我正在考虑最好的方法这样做的方法。

  1. 从 B 接收到缓冲区,并立即将缓冲区发送()到 A?
  2. recv() 将 B 中的整个文件保存在服务器上,然后发送 给 A?

我的程序应该支持这种行为: 如果 A 向 B 请求文件,然后 C 向 B 请求文件,则 C 不应等到传输 AB 结束。这就是我卡住的地方。

非常感谢!!

编辑:我的服务器正在使用线程:每当有新客户端连接时,都会打开一个新线程来为其提供服务。目前,我的客户端不使用线程(可以更改)。

【问题讨论】:

  • 那么,到目前为止,您做了什么来尝试解决问题。请记住,我们不是来为您编写代码或解决您所有的学校作业的。您必须完成工作 - 这就是您学习的方式!

标签: c tcp pthreads client-server


【解决方案1】:

如果您想使用套接字一次进行多次传输,您有两种选择:

阻塞套接字和线程

这就是您编写服务器的方式。线程的问题在于它们可能导致难以调试的错误。将其与本身就很难调试的网络错误结合起来,您将面临一个潜在的噩梦般的调试会话。

非阻塞套接字和 select()

这种方式不需要线程,而是使用 select() 来查看哪些套接字有数据等待读取。将其与一些循环结合起来,您可以同时传输多个文件。将套接字设置为非阻塞很容易,使用正确的可能会稍微复杂一些,但考虑到线程+网络错误的可能性,这是我个人更喜欢编写网络代码的方式。

关于您的实际问题;我会建议这样的事情:

Client A connects to server S. You need to bind the local side for the next step.
A also opens another socket for data transfer on the next port upwards.
S sends file list to A. How you build up the file list I leave to you.
A requests file F from S.
S checks which client has F.
S sends "send F to A on port X" to B. You can check which remote port is used, and then you know which port to send the file on.
B recieves and executes the command.

【讨论】:

  • 嗯...谢谢达特。我将阅读所有相关内容。我很惊讶不需要线程(根本不需要线程?甚至在服务器上都不需要?)我已经完成了您在第 5 步(包括)之前的所有“步骤”,但我不需要考虑第 2 步(A 也打开另一个插座)。一些谷歌搜索可能会帮助我......我会发布我的成功和失败。谢谢。
  • 想想你为什么使用线程。是否一次处理多个套接字?如果是这样,那么您不必使用线程,您可以但您不必这样做。
  • 所以我真正需要做的是找出 select() 如何真正与多个套接字一起工作,没有线程..?此外,这个“A 还打开另一个套接字以在下一个端口向上传输数据”。有可能吗?
  • 只做你正在做的事,但绑定到下一个端口。
  • 我已经设法摆脱线程,并使用 select()。谢谢你的提示。所以现在我的程序工作正常,但我还没有实现文件发送的东西。我设法使用getsockname()发现客户端端口(来自客户端应用程序),现在我想向上绑定下一个端口 - 我的问题是 - 我应该创建一个新的socket(),绑定()它并且只有当我从服务器获取请求使用 connect() 来完成请求?
【解决方案2】:

您必须为每个客户端连接 2 个套接字,因此您将拥有 4 个这样的套接字:

1 A <-> C
2 B <-> C 
3 A <-> C <-> B 4

因此,您必须使用套接字 1 和 2 在客户端 A 和服务器 C(套接字 1)之间以及客户端 B 和服务器 C(套接字 2)之间传输文件。

然后你必须在服务器 C 中开发一个桥接器,使用套接字 3 和 4 将数据从 A 传输到 B。

我猜多线程解决方案应该可以工作!

【讨论】:

  • Mats-sure,我不是要你为我写代码。我只是想要你的帮助思考(-:(众包或类似的东西)。Joze-谢谢,我正在考虑这个方向。这是否意味着我的客户也应该使用线程?我的意思是,我的客户应该听传入来自服务器的请求以及用户输入...
  • 是的,当然,如果您想同时进行多次传输!
  • 我想详细说明你的答案:当我的客户端识别到服务器请求并启动一个新线程来处理请求时,它是否应该为服务器创建一个新套接字?服务器是否应该为该客户端创建另一个套接字?我真的很困惑......
  • 如果“A”想要一个文件,那么为“C”创建一个新套接字。 “C”知道文件在“B”中,因此“C”将套接字连接到“B”。然后 C 必须做 A 和 B 之间的桥接(当它从套接字 B 接收到数据时,它直接将其发送到套接字 A)。
【解决方案3】:

到目前为止我得到了什么: 服务器仅使用 select() 运行。 客户端有一个处于listen() 模式的套接字——称之为dataSocket(在它的常规套接字之上)。 如果客户端 A 想要一个文件,它会告诉服务器文件名。服务器通过文件找到用户,并对 dataSocket 进行 connet()。 (我设法实现了)。

小文件传输没有问题。大文件失败。

我认为这是因为我的客户出了问题。 我已经使用 select() 函数实现了客户端,如果它发现有人在执行 connect(),它会打开一个新线程并为请求提供服务。但是有什么不对。。 代码是这样的:

FD_SET(globalSocket,&origin);
FD_SET(dataSocket,&origin);
FD_SET(STDIN,&origin); //#define STDIN 0
while(1)
{
 readfds=origin;
 select(fdmax+1,&readfds,NULL,NULL,NULL);
 for(i=0;...)
  {
   if(FD_ISSET(i,&readfds)
   {
    if(i==STDIN)
     // get user input
    if(i==dataSocket)
     {
      printf("someone wants a file from me");
      pthread_create(...);
     }
   }
  }
}

我看到的一个问题是,当我第一次请求文件时,它会打印 2 到 3 次“有人想要我的文件”这一行,但只创建了一个线程。 当我尝试发送一个大文件时,我确实收到了一些文件,但随后弹出“对等连接重置”...

我希望这里有人能够回答我解释得不太好的问题。 谢谢。

【讨论】:

    猜你喜欢
    • 2023-03-21
    • 2017-08-29
    • 1970-01-01
    • 1970-01-01
    • 2019-11-16
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多