Socket编程-客户端服务器原理

4

很不好意思,我要问这个问题,但是在谷歌上搜索了很久(甚至去看MSDN),我决定发帖问一下:

我刚开始学习客户端-服务器编程(使用C#),并尝试着使用tcpClient编写我的第一个代码。我同时编写了服务器端和客户端。

我的问题是:客户端不断地向服务器发送一个字符串,然后服务器回复一个字符串给客户端,如此反复。但是,服务器不能连续发送2个字符串吗?他必须等待客户端的响应吗?这是客户端-服务器的原则吗?

再次感到抱歉我提出了这个问题。 谢谢...

<> 我将尝试发布一些我的代码(有点长...)。 我尽量删除了冗余部分,所以希望代码有意义...(我用 //********* 标记了主要问题)

public class MServer2
{
Dictionary<String, String> nameAndPass = new Dictionary<String, String>();
Dictionary<String, List<String> > nameAndMail = new Dictionary<String, List<String>>();

public static void Main()
{
    new MServer2();
}

public MServer2()
{
    TcpListener server = new TcpListener(8500);
    try
    {
        server.Start();
        Console.WriteLine("started " + server);
        while (true)
        {
            TcpClient client = server.AcceptTcpClient();
            Console.WriteLine("connection accepted " + client);
            new Server1(client, nameAndPass, nameAndMail);
        }
    }
    catch (Exception e)
    {
        Console.WriteLine("exception" + e);
    }
    finally
    {
        server.Stop();
    }
}

class Server1
{
TcpClient client;
NetworkStream netStream;
Dictionary<String, String> nameAndPass1 = new Dictionary<String, String>();
Dictionary<String, List<String>> nameAndMail1 = new Dictionary<String, List<String>>();

internal Server1(TcpClient client, Dictionary<String, String> nameandPassFromFile, Dictionary<String, List<String> > nameAndMailsFromFile)
{
    nameAndPass1 = nameandPassFromFile;
    nameAndMail1 = nameAndMailsFromFile;

    this.client = client;
    Thread thr = new Thread(new ThreadStart(Run));
    thr.Start();
}

public void Run()
{
    try
    {
        netStream = client.GetStream();
        StreamReader reader = new StreamReader(netStream);
        StreamWriter writer = new StreamWriter(netStream);
        writer.AutoFlush = true;
        Console.WriteLine("beginning to receive loop");

        writer.WriteLine("Choose your user name.");
        strFromClient = reader.ReadLine();
        userName = strFromClient;

        writer.WriteLine("Choose your user password.");
        strFromClient = reader.ReadLine();

        password = strFromClient;

        writer.WriteLine("Do you want to see the list of email addresses? (y/n)");
        strFromClient = reader.ReadLine();

//***********************************************************************************
//HERE'S MY PROBLAM:
//HERE THE CLIENT WILL GET A STRING SHOWING HIS EMAILS, AND I WANT HIM TO GET ANOTHER     STRING ASKING "Do you want to add an email address? (y/n)", BUT IT LOOKS LIKE THE SERVER     "WAITS" FOR A RESPONSE FROM THE CLIENT TO SHOW THE NEXT STRING...
        if (strFromClient == "y")
        {   
            String tmpStr = null;
            List<String> tmp = nameAndMail1[userName];
            for(int i=0; i<nameAndMail1[userName].Count; i++)
            {
                tmpStr += tmp[i] + " ";
            }

            writer.WriteLine(tmpStr);                            
        } 
        writer.WriteLine("Do you want to add an email address? (y/n)");
        strFromClient = reader.ReadLine();

    }    

}
    catch (Exception e)
    {
        Console.WriteLine("{0} Exception caught.", e);
    }

编辑第二版

好的!经过两个小时的痛苦,我认为我在Phil Frost(天才!)的帮助下找到了问题 --- 问题可能出在客户端上...(我真是个混蛋!)。

服务器确实连续发送了两个字符串,但我愚蠢的客户端实现不能显示来自服务器的消息,除非它跟在客户端发送的消息后面......

所以我再次需要你们的帮助。这是我设计的客户端表单的视图:

enter image description here

由于经验不足,我在按下“连接到服务器”按钮时连接到服务器,只有在按下“发送消息”按钮时,才会显示来自服务器的消息。问题是当没有向服务器发送消息时接收到2(或更多)个来自服务器的消息时,客户端不知道有新消息接收到!我应该在哪里接收来自服务器的消息?(“where”意味着在哪个函数下,例如- 现在它发生在sendMessage_click函数中)。

非常感谢大家迄今为止的所有帮助!!


3
一个快速的学习提示-如果你使用TCP,它是面向流的 - 如果想要"消息"(如字符串),需要自行实现此概念-否则,服务器可能会发送两个字符串,但客户端可能会在一次、两次或多次调用Read()中接收到它们. 即使客户端在两次接收到它们时,可能会收到第一个字符串的一半,接着是第一个字符串和第二个字符串连接在一起的后半部分. 接收方需要知道要读取多少消息(定长或长度前缀)并不断调用Read(),直到完全接收消息。 - Damien_The_Unbeliever
我对C#知之甚少,所以这只是一次猜测,但输出是否被缓冲了呢?如果在WriteLine之后立即进行flush,会发生什么? - Phil Frost
刷新操作没有帮助……(无论如何,它已经设置为自动刷新)。无论如何,谢谢 :) - omi
1
嗯。我建议你使用调试器或者一些打印语句,来检查 writer.WriteLine("Do you want to add an email address? (y/n)"); 这行代码是否真正执行了。虽然我不知道为什么它不会被执行,但你永远无法确定。你确定客户端没有问题吗?你能否通过Wireshark、Tcpdump等工具来验证网络情况? - Phil Frost
哇,Phil,我觉得你真的帮了我...我想我的问题是客户端在接收消息时没有刷新自己...如果是这样,我会宣布你是个天才...我现在会尝试修复它-马上回来! - omi
3个回答

6
这个问题既不差劲又不琐碎。您正在触及协议设计中的一个重要点。
据我所知,有两种解决方法:
1. "对话"(您的方式)......请求后跟随回复,然后是下一个请求,依此类推。这样做的优点是简单易行,缺点是每次只能处理一个连接中的一个命令;
2. "异步"(双方都可以在不等待回答的情况下发送)......这种方式实现起来比较困难,你需要某种请求ID来确定哪个回答属于哪个请求:想象一下客户端发送了两个请求,第一个处理时间比第二个长,那么回复将会与请求的顺序不同。
这两种变体之间的选择并不总是轻松的,但趋势是朝着异步模式发展。
异步模式中的额外困难包括确保每次只发送一个消息的机制(交织消息很可能导致无法解析的流)和超时考虑。

1
+1。确切地说,这不是关于“客户端服务器技术”一般而言,而是一种选择。您是否在双向运行命令和数据流,还是拥有对话(请求/响应)?更容易,但最终取决于您的需求。您甚至可以拥有多个连接,就像我一样-一个命令,一个数据。命令:对话,数据:从服务器到客户端的流。该命令包括诸如“开始发送X上的更新”的内容,这将返回“OK”或“FAILURE”。这真的是一种设计选择。数据使用基本上需要这种选择。 - TomTom
好的,正如我所提到的,我刚开始学习客户端服务器编程,我想我的错误可能是尝试使用异步方法编写我的第一段代码...希望下次我会更明智并且先思考...无论如何,现在为时已晚——我没有勇气重新开始使用对话框方法编写程序 :) 谢谢! - omi
1
@omi 这样做可能会更困难,但你会从中获得更多的收益。 - Eugen Rieck

2

是的,服务器可以连续发送两个字符串。但如果您的服务器在read()上被阻塞,则可能不行。也许如果您提供有关程序的更多详细信息,就可以提供更具体的答案。


1
在TCP中,客户端到服务器和服务器到客户端的通道是两个独立的流。任何一方都可以随时发送。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接