i ran into a problem , pls help:
What version of gRPC and what language are you using?
libgrpc++.so.1.26.0
What operating system (Linux, Windows,...) and version?
centos7
What did you do?
my scenario is : I have a grpc server named A and, when I receive a grpc message, send the message to another grpc server named B directly;
my server A initial(CRpcNetServer class):
int CRpcNetServer::Initialize()
{
std::string listenAddr = "this is listen address";
ServerBuilder builder;
builder.AddListeningPort(listenAddr, grpc::InsecureServerCredentials());
//register
builder.RegisterService(&AService_); //protobuf definition A
builder.RegisterService(&BService_); //protobuf definition A
//
cq_ = builder.AddCompletionQueue();
server_ = builder.BuildAndStart();
new CallData(&AService_, &BService_, cq_.get(), CallData::S_TYPE_GRPC_MESSAGE);
//new CallData(&AService_, &BService_, cq_.get(), {other message type});
return ERROR_CODE_SUCCESS;
}
void CRpcNetServer::handleRpcs()
{
void *tag;
bool ok;
while (true)
{
if (GPR_UNLIKELY(!(cq_->Next(&tag, &ok))))
{
spdlog::error("handleRpcs||get next message failed, try again");
continue;
}
if (GPR_UNLIKELY(!ok))
{
spdlog::error("handleRpcs||get next message notok, try again");
continue;
}
static_cast<CallData *>(tag)->process();
}
}
when message arrived(CallData class):
void CallData::process()
{
if (status_ == CREATE)
{
status_ = PROCESS;
switch (s_type_)
{
case S_TYPE_GRPC_MESSAGE:
BService_->RequestOnMessage(&ctx_, &GrpcUpLinkMessage, &processGrpcUplinkResponse, cq_, cq_, this);
//case other message type
default:
break;
}
}
else if (status_ == PROCESS)
{
status_ = FINISH;
new CallData(AService_, BService_, cq_, this->s_type_);
switch (s_type_)
{
case S_TYPE_GRPC_UPLINK_MESSAGE:
{
Status ret = this->onGrpcUplinkMessage(gRPCUpLinkMessage, emptyResponse_);
processGrpcUplinkResponse.Finish(emptyResponse_, ret, this);
}
break;
default:
break;
}
}
else
{
GPR_ASSERT(status_ == FINISH);
delete this;
}
}
when i received a grpc message, send it to another grpc server directly:
//init grpc client:
CRpcNetClient::CRpcNetClient(){
std::shared_ptr<Channel> channel = grpc::CreateChannel({B server address}, grpc::InsecureChannelCredentials());
stub_ = BServerApi::SendToBDownlinkService::NewStub(channel);
}
//send
std::chrono::time_point<std::chrono::system_clock> deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(100);
ClientContext context;
context.set_deadline(deadline);
std::time_t timestamp = getTimestamp();
Status status = stub_->publishMessage(&context, downLinkGroupRequest, &downLinkGroupResponse);
// process will hang here
std::chrono::time_pointstd::chrono::system_clock deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(100);
I tried to set milliseconds to 1ms, it will return failed , so I think the param is worked, but while set it to 100, it will occurs some time (not inevitable).
I want accept client's connecting request but i don't know how can i do this.
Serverside :
enum GameMessages
{
ID_GAME_MESSAGE_1 = ID_USER_PACKET_ENUM + 1
};
using namespace std;
int main(void)
{
RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
bool isServer;
RakNet::Packet *packet;
RakNet::SocketDescriptor sd(SERVER_PORT, 0);
peer->Startup(MAX_CLIENTS, &sd, 1);
isServer = true;
if (isServer)
{
printf("Sunucu baslatiliyor.\n");
// We need to let the server accept incoming connections from the clients
peer->SetMaximumIncomingConnections(MAX_CLIENTS);
}
while (1)
{
for (packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())
{
switch (packet->data[0])
{
case ID_REMOTE_DISCONNECTION_NOTIFICATION:
printf("Another client has disconnected.\n");
break;
case ID_REMOTE_CONNECTION_LOST:
printf("Another client has lost the connection.\n");
break;
case ID_REMOTE_NEW_INCOMING_CONNECTION:
printf("Another client has connected.\n");
break;
case ID_CONNECTION_REQUEST_ACCEPTED:
{
printf("Our connection request has been accepted.\n");
// Use a BitStream to write a custom user message
// Bitstreams are easier to use than sending casted structures, and handle endian swapping automatically
RakNet::BitStream bsOut;
bsOut.Write((RakNet::MessageID)ID_GAME_MESSAGE_1);
bsOut.Write("Oyuna hosgeldin emmolu.");
peer->Send(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false);
}
break;
case ID_NEW_INCOMING_CONNECTION:
printf("Bir baglanti geliyor.\n");
break;
case ID_NO_FREE_INCOMING_CONNECTIONS:
printf("The server is full.\n");
break;
case ID_DISCONNECTION_NOTIFICATION:
if (isServer) {
printf("A client has disconnected.\n");
}
else {
printf("We have been disconnected.\n");
}
break;
case ID_CONNECTION_LOST:
if (isServer) {
printf("A client lost the connection.\n");
}
else {
printf("Connection lost.\n");
}
break;
case ID_GAME_MESSAGE_1:
{
RakNet::RakString rs;
RakNet::BitStream bsIn(packet->data, packet->length, false);
bsIn.IgnoreBytes(sizeof(RakNet::MessageID));
bsIn.Read(rs);
printf("%s\n", rs.C_String());
}
break;
default:
printf("Message with identifier %i has arrived.\n", packet->data[0]);
break;
}
}
}
RakNet::RakPeerInterface::DestroyInstance(peer);
return 0;
}
When i try my work it's giving me .
[LOG]
Server : Server started.
Client : Sent request to connect server.
Server : A request received from client to connect server.
Server : Accept client's request. ( What i'm trying to do )
I have a twisted server that does something and then closes the connection. The python clients understand with
clientConnectionLost(self, connector, reason)
that the connection was closed and that works fine.
However, I also have a C++ client communicating with the same twisted server. It currently doesn't seem to understand that the connection/socket was closed. How do I check for this?
string tcp_client::receive(int size=1024)
{
char buffer[size];
string reply;
int msg = recv(sock , buffer , sizeof(buffer) , 0);
// Receive a reply from the server
if(msg < 0)
{
puts("recv failed");
// Exit the programm
exit(0);
}
reply = buffer;
return reply;
}
is the C++ client's receive code.
How do I reach the same/similar functionality of clientConnectionLost with the C++ client?
From man recv:
The return value will be 0 when the peer has performed an orderly shutdown.
So after recv call, you can write:
string tcp_client::receive(int size=1024)
{
char buffer[size];
string reply;
// Receive a reply from the server
int msg = recv(sock , buffer , sizeof(buffer) , 0);
if(msg < 0)
{
puts("recv failed");
// Exit the programm
exit(0);
}
else if (0 == msg)
{
// the connexion has been closed by server
puts("Connexion lost!");
// close the socket
close(sock);
// return something
return string("");
}
reply = buffer;
return reply;
}
I'm writing a TCP server (blocking socket model).
I'm having trouble implementing a valid normal program exit when the server is waiting (blocking) for new connection attempts on Accept (I use WSAccept).
The code for the server's listening socket is something like this (I omitted error handling and other irrelevant code):
int ErrCode = WSAStartup(MAKEWORD(2,2), &m_wsaData) ;
// Create a new socket to listen and accept new connection attempts
struct addrinfo hints, *res = NULL, *ptr = NULL ;
int rc, count = 0 ;
memset(&hints, 0, sizeof(hints)) ;
hints.ai_family = AF_UNSPEC ;
hints.ai_socktype = SOCK_STREAM ;
hints.ai_protocol = IPPROTO_TCP ;
hints.ai_flags = AI_PASSIVE ;
CString strPort ;
strPort.Format("%d", Port) ;
getaddrinfo(pLocalIp, strPort.GetBuffer(), &hints, &res) ;
strPort.ReleaseBuffer() ;
ptr = res ;
if ((m_Socket = WSASocket(res->ai_family, res->ai_socktype, res->ai_protocol, NULL, 0, 0)) == INVALID_SOCKET)
{
// some error
}
if(bind(m_Socket, (SOCKADDR *)res->ai_addr, res->ai_addrlen) == SOCKET_ERROR)
{
// some error
}
if (listen(m_Socket, SOMAXCONN) == SOCKET_ERROR)
{
// some error
}
So far so good... Then I implemented the WSAccept call inside a thread like this:
SOCKADDR_IN ClientAddr ;
int ClientAddrLen = sizeof(ClientAddr) ;
SOCKET TempS = WSAAccept(m_Socket, (SOCKADDR*) &ClientAddr, &ClientAddrLen, NULL, NULL);
Of course the WSAccept blocks until a new connection attempt is made but if I wish to exit
the program then i need some way to cause WSAccept to exit. I have tried several different approaches:
Attempt to call shutdown and/or closesocket with m_Socket from within another thread failed (program just hangs).
using WSAEventSelect indeed solves this issue but then WSAccept delivers only non-blocking sockets - which is not my intention. (Is there a way to make the sockets blocking?)
I Read about APC and tried to use something like QueueUserAPC(MyAPCProc, m_hThread, 1)) but it didn't work either.
What am I doing wrong ?
Is there a better way to cause this blocking WSAccept to exit ?
Use select() with a timeout to detect when a client connection is actually pending before then calling WSAAccept() to accept it. It works with blocking sockets without putting them into non-blocking mode. That will give your code more opportunities to check if the app is shutting down.
Go with non-blocking accepting socket (WSAEventSelect as you mentioned) and use non-blocking WSAccept. You can make a non-blocking socket that WSAccept returns into blocking socket with ioctlsocket (see msdn).
Do all the other stuff you absoultely have to on shutdown, (maybe you have DB connections to close, or files to flush?), and then call ExitProcess(0). That will stop your listening thread, no problem.
See log4cplus source for my take on this issue. I basically wait on two event objects, one is signaled when connection is being accepted (using WSAEventSelect()) and another is there to interrupt the waiting. The most relevant parts of the source is below. See ServerSocket::accept().
namespace {
static
bool
setSocketBlocking (SOCKET_TYPE s)
{
u_long val = 0;
int ret = ioctlsocket (to_os_socket (s), FIONBIO, &val);
if (ret == SOCKET_ERROR)
{
set_last_socket_error (WSAGetLastError ());
return false;
}
else
return true;
}
static
bool
removeSocketEvents (SOCKET_TYPE s, HANDLE ev)
{
// Clean up socket events handling.
int ret = WSAEventSelect (to_os_socket (s), ev, 0);
if (ret == SOCKET_ERROR)
{
set_last_socket_error (WSAGetLastError ());
return false;
}
else
return true;
}
static
bool
socketEventHandlingCleanup (SOCKET_TYPE s, HANDLE ev)
{
bool ret = removeSocketEvents (s, ev);
ret = setSocketBlocking (s) && ret;
ret = WSACloseEvent (ev) && ret;
return ret;
}
} // namespace
ServerSocket::ServerSocket(unsigned short port)
{
sock = openSocket (port, state);
if (sock == INVALID_SOCKET_VALUE)
{
err = get_last_socket_error ();
return;
}
HANDLE ev = WSACreateEvent ();
if (ev == WSA_INVALID_EVENT)
{
err = WSAGetLastError ();
closeSocket (sock);
sock = INVALID_SOCKET_VALUE;
}
else
{
assert (sizeof (std::ptrdiff_t) >= sizeof (HANDLE));
interruptHandles[0] = reinterpret_cast<std::ptrdiff_t>(ev);
}
}
Socket
ServerSocket::accept ()
{
int const N_EVENTS = 2;
HANDLE events[N_EVENTS] = {
reinterpret_cast<HANDLE>(interruptHandles[0]) };
HANDLE & accept_ev = events[1];
int ret;
// Create event and prime socket to set the event on FD_ACCEPT.
accept_ev = WSACreateEvent ();
if (accept_ev == WSA_INVALID_EVENT)
{
set_last_socket_error (WSAGetLastError ());
goto error;
}
ret = WSAEventSelect (to_os_socket (sock), accept_ev, FD_ACCEPT);
if (ret == SOCKET_ERROR)
{
set_last_socket_error (WSAGetLastError ());
goto error;
}
do
{
// Wait either for interrupt event or actual connection coming in.
DWORD wsawfme = WSAWaitForMultipleEvents (N_EVENTS, events, FALSE,
WSA_INFINITE, TRUE);
switch (wsawfme)
{
case WSA_WAIT_TIMEOUT:
case WSA_WAIT_IO_COMPLETION:
// Retry after timeout or APC.
continue;
// This is interrupt signal/event.
case WSA_WAIT_EVENT_0:
{
// Reset the interrupt event back to non-signalled state.
ret = WSAResetEvent (reinterpret_cast<HANDLE>(interruptHandles[0]));
// Clean up socket events handling.
ret = socketEventHandlingCleanup (sock, accept_ev);
// Return Socket with state set to accept_interrupted.
return Socket (INVALID_SOCKET_VALUE, accept_interrupted, 0);
}
// This is accept_ev.
case WSA_WAIT_EVENT_0 + 1:
{
// Clean up socket events handling.
ret = socketEventHandlingCleanup (sock, accept_ev);
// Finally, call accept().
SocketState st = not_opened;
SOCKET_TYPE clientSock = acceptSocket (sock, st);
int eno = 0;
if (clientSock == INVALID_SOCKET_VALUE)
eno = get_last_socket_error ();
return Socket (clientSock, st, eno);
}
case WSA_WAIT_FAILED:
default:
set_last_socket_error (WSAGetLastError ());
goto error;
}
}
while (true);
error:;
DWORD eno = get_last_socket_error ();
// Clean up socket events handling.
if (sock != INVALID_SOCKET_VALUE)
{
(void) removeSocketEvents (sock, accept_ev);
(void) setSocketBlocking (sock);
}
if (accept_ev != WSA_INVALID_EVENT)
WSACloseEvent (accept_ev);
set_last_socket_error (eno);
return Socket (INVALID_SOCKET_VALUE, not_opened, eno);
}
void
ServerSocket::interruptAccept ()
{
(void) WSASetEvent (reinterpret_cast<HANDLE>(interruptHandles[0]));
}
A not so neat way of solving this problem is by issuing a dummy WSAConnect request from the thread that needs to do the shutdown. If the dummy connect fails, you might resort to ExitProcess as suggested by Martin.
void Drain()
{
if (InterlockedIncrement(&drain) == 1)
{
// Make a dummy connection to unblock wsaaccept
SOCKET ConnectSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
if (ConnectSocket != INVALID_SOCKET) {
int iResult = WSAConnect(ConnectSocket, result->ai_addr, result->ai_addrlen, 0, 0, 0, 0);
if (iResult != 0) {
printf("Unable to connect to server! %d\n", WSAGetLastError());
}
else
{
closesocket(ConnectSocket);
}
}
}
}
I have a function to write data to the serial port with a certain protocol. When the function writes one frame, it waits for one answer of receiver. If no answer is received it has to resend data during 3 timeouts and in the end of 3 timeouts with no success, close the communication...
I have this function:
int serial_write(int fd, unsigned char* send, size_t send_size) {
......
int received_counter = 0;
while (!RECEIVED) {
Timeout.tv_usec = 0; // milliseconds
Timeout.tv_sec = timeout; // seconds
FD_SET(fd, &readfs);
//set testing for source 1
res = select(fd + 1, &readfs, NULL, NULL, &Timeout);
//timeout occurred.
if (received_counter == 3) {
printf(
"Connection maybe turned off! Number of resends exceeded!\n");
exit(-1);
}
if (res == 0) {
printf("Timeout occured\n");
write(fd, (&I[0]), I.size());
numTimeOuts++;
received_counter++;
} else {
RECEIVED = true;
break;
}
}
......
}
I have verified that this function, when it goes into timeout, does not resend the data. Why?