Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/268.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么';t IServerStreamWriter是否发送通知响应?_C#_Protocol Buffers_Grpc - Fatal编程技术网

C# 为什么';t IServerStreamWriter是否发送通知响应?

C# 为什么';t IServerStreamWriter是否发送通知响应?,c#,protocol-buffers,grpc,C#,Protocol Buffers,Grpc,我在一个跨平台的应用程序上工作。对于它们之间的连接,我使用gRPC技术。当客户端连接到服务器时,它将添加到位于服务器实现中的观察者列表中。当一个客户端连接时,我想向其他连接的客户端发送一条消息,告诉他们新的客户端已连接。问题是,当我想使用列表中的观察者向新客户机连接的客户机发送响应时,会出现以下异常: Grpc.Core.RpcException: 'Status(StatusCode=Unknown, Detail="Exception was thrown by handler.")' 这

我在一个跨平台的应用程序上工作。对于它们之间的连接,我使用gRPC技术。当客户端连接到服务器时,它将添加到位于服务器实现中的观察者列表中。当一个客户端连接时,我想向其他连接的客户端发送一条消息,告诉他们新的客户端已连接。问题是,当我想使用列表中的观察者向新客户机连接的客户机发送响应时,会出现以下异常:

Grpc.Core.RpcException: 'Status(StatusCode=Unknown, Detail="Exception was thrown by handler.")'
这是我声明服务器的原始文件:

syntax = "proto3";

package com.example.grpc.chat;

message ChatMessage {
    string from = 1;
    string message = 2;
}

message ChatMessageFromServer {
    ChatMessage message = 2;
}

service ChatService {
    rpc Login(ChatMessage ) returns (stream ChatMessageFromServer);
}
服务器代码:

 public class ChatServiceImpl : ChatService.ChatServiceBase
    {
        private static HashSet<IServerStreamWriter<ChatMessageFromServer>> responseStreams = new HashSet<IServerStreamWriter<ChatMessageFromServer>>();

        /*
         * if the stream object (from "for" statement inside this method) isn't the responseStream object given in the list with parameters,
         * the rest of clients aren't notified when a new login request is pushed.
         */
        public override async Task Login(global::Com.Example.Grpc.Chat.ChatMessage request,
            IServerStreamWriter<global::Com.Example.Grpc.Chat.ChatMessageFromServer> responseStream, 
            ServerCallContext context)
        {
            Console.WriteLine("Login method from server");
            responseStreams.Add(responseStream);

            // Create a server message that wraps the client message
            var message = new ChatMessageFromServer
            {
                Message = new ChatMessage
                {
                    From = "login",
                    Message = "hello"
                }
            };
            // If stream variable isn't equal to responseStream from list of parameters, the client corresponding to that stream isn't notified and it's thrown the above exception
            foreach (var stream in responseStreams)
            {
                await stream.WriteAsync(message);
            }
        }

    }


您的
notifyObservers
方法是异步的,但是有一个
void
返回类型,这意味着您不能等待它。您正在有效地启动该方法,并在单击第一个使用不完全可等待的
await
运算符(可能是第一个
WriteAsync
调用)时立即返回

然后返回带有
ReservationResponse
的任务,操作完成

当第一个等待的调用完成时,
notifyObservers
将继续,但此时操作已经完成,因此当您尝试写入响应流时,系统将抛出您看到的错误

我强烈怀疑您应该从
notifyObservators
返回
任务
,并等待主输入方法返回:

// Names changed to be conventional C#
public override async Task<ReservationResponse> SaveReservation(
    global::Res.Protocol.ReservationRequest request, ServerCallContext context)
{
    // some code for saving my reservation in repository database
    ReservationResponse response = new Res.Protocol.ReservationResponse
    {
        Type = ReservationResponse.Types.Type.Savereservation,
        Journey = GetProtoJourney(journey)
    };

    await NotifyObserversAsync(response);

    // Note: no Task.FromResult, as you're in an async method. The response
    // will already be wrapped in a task.
    return new ReservationResponse
    {
        Type = ReservationResponse.Types.Type.Savereservation
    };
}

public async Task NotifyObserversAsync(Res.Protocol.ReservationResponse response)
{
    foreach (var ob in responseStreams)
    {
        await ob.WriteAsync(response);
    }
}
//名称更改为常规C#
公共覆盖异步任务保存保留(
全局::Res.Protocol.ReservationRequest请求,ServerCallContext上下文)
{
//在存储库数据库中保存我的预订的一些代码
ReservationResponse=新的Res.Protocol.ReservationResponse
{
Type=ReservationResponse.Types.Type.Savereservation,
旅程=旅行(旅程)
};
等待NotifyObserversAsync(响应);
//注意:没有Task.FromResult,因为您使用的是异步方法。响应
//将已包装在任务中。
返回新的ReservationResponse
{
Type=ReservationResponse.Types.Type.Savereservation
};
}
公共异步任务NotifyObserversAsync(Res.Protocol.ReservationResponse)
{
foreach(响应团队中的var ob)
{
等待ob.WriteAsync(响应);
}
}

我按照您的建议修改了方法。不再抛出异常,但永远不会到达更新的代码。我在login()方法中做了一个测试:我迭代了所有观察者,并做出了一个SaveReservationResponse(就像我的NotifyObserversAsync方法一样)。如果我的IServerStreamWriter对象不是方法参数列表中当前的IServerStreamWriter对象,则不会为列表中以前的IServerStreamWriter对象调用我的update方法。@CatalinHoria:啊-我想我看到了问题所在。我没有仔细阅读这个问题,没有注意到它们是来自不同操作的流。如果您的
Login
方法正在添加到流列表中,您希望每个
Login
方法何时完成?如果你能用一个-我们不需要看Java,但是更完整的服务器代码会很有帮助。每个客户端都有注销选项(服务器代码中还没有实现),我将从观察员列表中删除它。这是我希望该客户端完成login()方法的时候。@CatalinHoria:对。你会有一个自然的比赛条件,除非你有某种锁定。我将很快删除这个答案,因为我认为我没有正确理解它-但是如果你能提供一个能让事情变得更简单的答案。我通过一个简单的例子从真实的应用程序中重现了我的问题。当新客户端连接到服务器时,登录方法(即服务器流式登录方法)应向所有连接的客户端发送notify响应。但如果流不是方法中作为参数给定的流,则永远不会发送响应。我附上C#服务器和Java客户端实现:
// Names changed to be conventional C#
public override async Task<ReservationResponse> SaveReservation(
    global::Res.Protocol.ReservationRequest request, ServerCallContext context)
{
    // some code for saving my reservation in repository database
    ReservationResponse response = new Res.Protocol.ReservationResponse
    {
        Type = ReservationResponse.Types.Type.Savereservation,
        Journey = GetProtoJourney(journey)
    };

    await NotifyObserversAsync(response);

    // Note: no Task.FromResult, as you're in an async method. The response
    // will already be wrapped in a task.
    return new ReservationResponse
    {
        Type = ReservationResponse.Types.Type.Savereservation
    };
}

public async Task NotifyObserversAsync(Res.Protocol.ReservationResponse response)
{
    foreach (var ob in responseStreams)
    {
        await ob.WriteAsync(response);
    }
}