我在tcp上实现了ping-pong。我用P#写的Pinger。还有两个人。一个用C#编写,另一个用C ++编写。 Pinger只是向pongers发送消息,pongers回复他。问题是,当C#ponger正常工作时,一切正常,但是当C ++ ponger工作时,来自Pinger的行
var res = client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
不运行Callback(ReceiveCallback)。而ReceiveCallback只被调用一次(来自Receive函数)。 pinger代码的片段在这里:
private void Receive(Socket client)
{
try
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
} finally
{
}
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
int bytesRead = 0;
try
{
// Read data from the remote device.
bytesRead = client.EndReceive(ar);
}
catch (ObjectDisposedException e)
{
Console.WriteLine(client.Connected);
if (_isDown)
return;
else
{
throw e;
}
}
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
try
{
// Get the rest of the data.
var res = client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (ObjectDisposedException e)
{
if (_isDown)
return;
else
{
throw e;
}
}
}
else
{
// All the data has arrived; put it in _response.
if (state.sb.Length > 1)
{
_response = state.sb.ToString();
}
// Signal that all bytes have been received.
_receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
C#Ponger在这里:
public void AsyncListenFor()
{
// Bind the socket to the local endpoint and listen for incoming connections.
while (_doPong)
{
try
{
_localEndPoint = new IPEndPoint(_ipAddress, _port);
if (!_listener.IsBound)
{
_listener = CreateSocket(_listener);
BindAndListen(_listener, _localEndPoint);
}
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
//Console.WriteLine("Waiting for a connection...");
_listener.Blocking = true;
_listener.BeginAccept(new AsyncCallback(AcceptCallback), _listener);
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
catch (Exception e)
{
// здесь не будем показывать эксепшон WatchDog`у чтобы не перезагружать прогу
//(если автостартер не будет работать)
if (_listener != null)
{
try
{
//_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//Socket dataSocket = AsyncSocket.EndAccept(_IAsyncResult);
_listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(false, 0));
_listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
}
catch (Exception e2)
{ CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
try
{
_listener.Shutdown(SocketShutdown.Both);
}
catch (Exception e2)
{ CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
try
{
_listener.Disconnect(false);
}
catch (Exception e2)
{ CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
try
{
_listener.Close();
}
catch (Exception e2)
{ CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
}
CLogger.WriteLog(CLogger.ELogLevel.ERROR, "");
CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e.Message + "\n" + e.StackTrace);
Thread.Sleep(1000);
}
}
}
public Socket CreateSocket(Socket listener)
{
if (listener != null)
{
try
{
listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(false, 0));
listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
}
catch (Exception e2)
{ CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
try
{
_listener.Shutdown(SocketShutdown.Both);
}
catch (Exception e2)
{ CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
try
{
_listener.Disconnect(false);
}
catch (Exception e2)
{ CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
try
{
_listener.Close();
}
catch (Exception e2)
{ CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e2.Message + "\n" + e2.StackTrace); }
}
try
{
listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(false, 0));
listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, false);
} catch (Exception e)
{
CLogger.WriteLog(CLogger.ELogLevel.ERROR, "Не смогли создать сокет:\t" + e.Message);
}
return listener;
}
public void BindAndListen(Socket listener, IPEndPoint localEndPoint)
{
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
} catch (Exception e)
{ CLogger.WriteLog(CLogger.ELogLevel.DEBUG, e.Message + "\n" + e.StackTrace); }
}
public void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1)
{
// All the data has been read from the
// client. Display it on the console.
//Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
// content.Length, content);
// Echo the data back to the client.
Send(handler, _progName);
}
else
{
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
private void Send(Socket handler, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
private void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket handler = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
//Console.WriteLine("Sent {0} bytes to client.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
C ++ Ponger就在这里:
// listen the sotket and send
WSADATA wsaData;
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
while(true) {
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
LOG(logINFO) << "Could not initialize Winsock";
continue;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Create a SOCKET for connecting to server
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET) {
LOG(logINFO) << "Could not create socket.";
WSACleanup();
continue;
}
// Setup the TCP listening socket
struct sockaddr_in client;
client.sin_family = AF_INET;
client.sin_port = htons(port_);
client.sin_addr.s_addr = inet_addr(ip_.c_str());
iResult = bind(ListenSocket, (struct sockaddr *)&client, sizeof(struct sockaddr_in));
if (iResult == SOCKET_ERROR) {
LOG(logINFO) << "Bind failed.";
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
continue;
}
iResult = listen(ListenSocket, 0);
if (iResult == SOCKET_ERROR) {
LOG(logINFO) << "listen failed";
closesocket(ListenSocket);
WSACleanup();
continue;
}
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
LOG(logINFO) << "accept failed";
closesocket(ListenSocket);
WSACleanup();
continue;
}
closesocket(ListenSocket);
// Receive until the peer shuts down the connection
do {
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
// Echo the buffer back to the sender
iSendResult = send( ClientSocket, recvbuf, iResult, 0 );
if (iSendResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
continue;
}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iResult == 0)
printf("Connection closing...\n");
else {
LOG(logINFO) << "recv failed";
closesocket(ClientSocket);
WSACleanup();
continue;
}
} while (iResult > 0);
}
答案 0 :(得分:1)
除非我遗漏了某些内容,否则在C ++服务器中实际上并没有关闭客户端的套接字。
如果recv返回0表示正常断开连接,我们将进入else if (iResult == 0)
分支。然后我们打印“Connection closing ...”并退出do ... while
循环。我们实际上没有调用shutdown
或closesocket
。
您的程序也有非传统结构。通常这样的简单服务器看起来像:
create the listen socket
bind the listen socket
listen
while (!needToShutdown)
accept
read client request
do work
send response
shutdown client socket
close client socket
// we are now shutting down
close listen socket
请注意,在接受客户端后,我们不会关闭侦听套接字。我们可以使用相同的listen套接字一个接一个地接受多个客户端。 Wikipedia有一个例子。 (使此服务一次处理多个客户端太复杂,无法在此讨论。)