2016-03-08 53 views
1

我的两个程序有问题--C#客户端可以使用套接字成功将数据写入C++客户端,但从服务器读取数据是不成功。C#客户端与C++服务器通信 - 服务器可以从套接字读取,但写入不成功

这里是我的C++代码:

int sock = socket(PF_INET, SOCK_STREAM, 0); 

sockaddr_in sockad; 
// sin_family, port etc defined here 

bind(sock, (sockaddr*)&sockad, sizeof(sockad)); 

listen(sock, 15); 

char buf; 
stringstream ss; 

while(running) { 

    socklen_t sockl = sizeof(sockad); 
    int sockd = accept(sock, (sockaddr*)&sockad, &sockl); 

    while(recv(sockd, &buf, 1, 0) > 0) { 
     ss << buf; 
    } 

    // THIS is printed 
    cout << ss.str() << endl; 

    string response = "asdgfh"; 

    // THIS never reaches the C# client 
    write(sockd, response.c_str(), strlen(response.c_str())); 

    close(sockd); 
} 

close(sock); 

这是我的C#代码:

string message = "message"; 
Int32 port = 4345; 
TcpClient client = new TcpClient("server.address.com", port); 
NetworkStream stream = client.getStream(); 

Byte[] data = new Byte[256]; 
data = Encoding.ASCII.GetBytes(message); 

// THIS SUCCEEDS 
stream.Write(data, 0, data.Length); 

// THIS FAILS, it also freezes the other program 
Int32 bytes = stream.Read(data, 0, data.Length); 

responseData = Encoding.ASCII.GetString(data, 0, bytes); 

stream.Close(); 
client.Close(); 

那么,什么是错我的代码,或者这是一个服务器的配置问题,或者类似的东西?

+0

我相信,而不是“写”你可能想使用“发送”在C++层。 – MattTanner

+0

@UrticaDioica这里是一个[文章]概述发送函数(https://msdn.microsoft.com/en-us/library/windows/desktop/ms740149(v=vs.85).aspx) – MattTanner

+0

将函数更改为发送没有解决问题... – mkkekkonen

回答

1

问题是一个典型的死锁。

在C++端,在TCP连接(半)关闭之前,您一直从套接字读取数据。在C#端,您写入一些数据,然后等待使TCP连接(半)关闭。任何一方都不关闭连接,所以两个程序都必须阻止各自的读取/接收,直到其他事件中断通信。这基本上意味着“拔掉网线”,因为默认情况下TCP不会执行任何心跳。

您需要了解有关TCP的两件事。首先,TCP是基于流的协议,而不是基于消息的。这意味着如果您想通过TCP发送消息,则需要添加自己的帧 - 在TCP流的顶部构建自己的消息传递协议。你假设在C#端写入将奇迹般地对应于C++端的读取,但这不仅仅是TCP的工作方式。

二,读取/接收返回零只有当流关闭。这与TCP是一个基于流的协议是一致的 - 它假设有一个无限数量的数据要被读取,如果你只是等待缓冲区被填充一点。

这个问题主要有两种解决方案。首先确保在继续写入之前只读取一个消息。这样做的简单方法是仅仅将发送的“消息”作为其长度的前缀,这样C++方知道当读取N个字节时,它具有所需的所有数据并可以继续。第二种是使用异步I/O读取同时写入。在任何时候都有出色的读取请求,并对读取的内容做出反应,而不是尝试执行读写读取顺序代码。

如果您只需要一个类似于HTTP的请求响应模式,那么您也可以仅对套接字进行部分关闭。您可以使用TcpClient.Client.Shutdown(SocketShutdown.Send)在C#端执行此操作。这两个应用程序都可以自由地继续处理响应。当然,当响应处理完成时,您又想要关闭,这次是从C++端开始的。结果是双方都关闭了连接,并形成一个完整的请求 - 响应循环。