int TcpServer::processSend() { char *buffer; size_t size, sent; if (this->stopping) return 0; //TODO: I find it dramatic to iterate over all the connections. Maybe // I could create one eventFd for each connection to notify that // Data is ready to be sent. for (auto it = clientList.begin(); it != clientList.end(); it++) { TcpClient* client = it->second; while ((size = client->getTxData(&buffer)) > 0) { sent = client->getSocket()->send(buffer, size); if (sent == -1) { if (errno != EAGAIN) { LOG("write error. returned " << errno); abort(); } //if EAGAIN, we stop sending and we will get notified by EPOLL when we are ready again } else if (sent == 0) { client->onDisconnected(); clientList.erase(it); delete it->second; break; } else { // this will move us to the next buffer or further in current if partially sent. client->onDataSent(sent); } } // after we are finished sending the tx queue, check if the client is failed and close the connection if (client->mFailed) { client->getSocket()->close(); client->onDisconnected(); delete client; clientList.erase(it); } } }
void* TcpClient::handleFunc(TcpClient* arg) { TcpClient* pClient = (TcpClient*)arg; bool bCon(false); for (int i = 0; i < CONN_TIMES; i++) { bCon = pClient->getSocket()->connectToTcp("192.168.208.128", 9999); if (bCon) break; pClient->getSocket()->close(); //pClient->getSocket() } if (bCon) { G.m_nStatus = STATUS_CONNECTED; thread t(responseFunc, pClient); t.detach(); while (!pClient->m_bStop) { unique_lock<mutex> lock(pClient->m_writeMutex); if (pClient->m_writeData.getPos() == 0) pClient->m_writeCv.wait(lock); int nRet = pClient->m_socket.send(pClient->m_writeData.getBuf(), pClient->m_writeData.getPos()); if (nRet < 0) { pClient->closeAndReConn(); break; } else { pClient->m_writeData.pop(nRet); } } //pClient->requestLogin(G.getSeq()); } else { G.m_nStatus = STATUS_DISCONNECT; } return NULL; }
int TcpServer::processReceive(int socket) { auto it = clientList.find(socket); if (it == clientList.end()) return 0; TcpClient* client = it->second; char tmpBuffer[1024]; //TODO: should get a buffer from a buffer pool size_t n = 1; while (n > 0) { if (this->stopping) { n = 0; break; } n = client->getSocket()->read((char*)&tmpBuffer[0], 1024); if (n == 0) { client->onDisconnected(); clientList.erase(it); delete client; } else if (n == -1) { n = 0; if (errno == ECONNRESET) { client->onDisconnected(); clientList.erase(it); delete client; } else { if (errno != EAGAIN) { LOG("Read error. Returned " << errno); abort(); } } break; } else { client->onReceive((char*)&tmpBuffer[0], n); } } return n; }
void* TcpClient::responseFunc(TcpClient* arg) { TcpClient* pClient = (TcpClient*)arg; DataQueue recvDataQueue; int nDataLen(-1); while (!pClient->m_bStop) { char recvBuf[65535]; int nLen(pClient->getSocket()->recv(recvBuf, sizeof(recvBuf) / sizeof(char), 0)); if (nLen < 0) { } else { recvDataQueue.push(recvBuf, nLen); int nPos(0); int nPackage(recvDataQueue.getPos()); while (1) { if (nPackage <= 0) break; CodedInputStream* coded_input = new CodedInputStream((uint8_t*)(recvDataQueue.getBuf() + nPos), recvDataQueue.getPos() - nPos); autoptr<CodedInputStream> inputguard(coded_input); int nOldPos = coded_input->CurrentPosition(); uint32_t datalen; if (!coded_input->ReadVarint32(&datalen)) { if (nPos > 0) { recvDataQueue.pop(nPos); } break; } int nNewPos = coded_input->CurrentPosition(); if (nNewPos + datalen > nPackage) { //一个包没有收整 if (nPos > 0) { recvDataQueue.pop(nPos); } break; } else if (nNewPos + datalen == nPackage) { char* data = new char[datalen]; autoptr_arr<char> arrdata(data); if (coded_input->ReadRaw(data, datalen)) { OnPackage(data, datalen); } recvDataQueue.setPos(0); break; } else { char* data = new char[datalen]; autoptr_arr<char> arrdata(data); if (coded_input->ReadRaw(data, datalen)) { OnPackage(data, datalen); } nPackage -= (datalen+nNewPos-nOldPos); nPos += (datalen+nNewPos-nOldPos); } } } } return NULL; }
void TcpServer::update() { struct pollfd pfd; pfd.fd = m_handle; pfd.events = POLLIN; pfd.revents = 0; int result = poll(&pfd, 1, 0); if(result > 0) { // connection established struct sockaddr_in addr; socklen_t len = sizeof(struct sockaddr_in); int newSock = accept(m_handle, (struct sockaddr *)(&addr), &len); if(newSock != -1) { TcpClient * ptc = new TcpClient(newSock, this); ptc->addRef(); m_connections.insert(std::make_pair(newSock, ptc)); struct pollfd readFd; readFd.fd = ptc->getSocket(); readFd.events = POLLIN|POLLERR|POLLHUP; readFd.revents = 0; m_connectionSockets.push_back(readFd); ptc->onConnectionOpened(); if(m_service) m_service->onConnectionOpened(ptc); ptc->release(); } } struct pollfd * clients = &m_connectionSockets[0]; int readResult = poll (clients, m_connectionSockets.size (), 0); if (readResult > 0) { std::vector<struct pollfd>::iterator i; std::vector<struct pollfd> cs = m_connectionSockets; for (i = cs.begin (); i != cs.end (); ++i) { if ((*i).revents & POLLERR) { std::map<int, TcpClient *>::iterator f = m_connections.find ( (*i).fd ); if (f != m_connections.end ()) { TcpClient * c = (*f).second; if (c->m_connection) c->m_connection->setDisconnectReason("TcpServer::update POLLERR"); c->onConnectionClosed(); } } // POLLERR is mutually exclusive with POLLIN and POLLHUP. // POLLIN and POLLHUP are not consistent cross-platform. Additionally, // a POLLHUP doesn't mean that there's not data available. The best, // cross-platform way to check for disconnection is to see if either // POLLHUP or POLLIN are set, and then read from the socket. // In cases where there's data, we'll just end up checking it next // frame anyway. In cases where there's no data, it means we've // disconnected so we can handle cleanup. else if( (*i).revents & (POLLIN|POLLHUP) ) { if (m_inputBuffer == 0) { m_inputBuffer = new unsigned char[1500]; m_inputBufferSize = 1500; } int bytesReceived = recv ( (*i).fd, m_inputBuffer, m_inputBufferSize, 0); std::map<int, TcpClient *>::iterator f = m_connections.find ( (*i).fd ); if (f != m_connections.end ()) { TcpClient * c = (*f).second; std::set<TcpClient *>::iterator pd = m_pendingDestroys.find(c); if(pd == m_pendingDestroys.end()) { c->addRef(); if(bytesReceived > 0) { c->onReceive(m_inputBuffer, bytesReceived); } else if (bytesReceived == -1) { if (c->m_connection) c->m_connection->setDisconnectReason("TcpServer::update recv returned %d, errno=%d", bytesReceived, errno); c->onConnectionClosed(); } else if (bytesReceived == 0) { // 0 bytes received on a read is the guaranteed signal for a // closed connection. if (c->m_connection) c->m_connection->setDisconnectReason("TcpServer::update recv returned no bytes, singaling a closed socket (bytes=%d, errno=%d", bytesReceived, errno); c->onConnectionClosed(); } c->release(); } } if (bytesReceived == m_inputBufferSize) { delete [] m_inputBuffer; m_inputBufferSize = m_inputBufferSize * 2; m_inputBuffer = new unsigned char [m_inputBufferSize]; } } } std::set<TcpClient *>::iterator pdIter; for(pdIter = m_pendingDestroys.begin(); pdIter != m_pendingDestroys.end(); ++pdIter) { removeClient((*pdIter)); } m_pendingDestroys.clear(); } }