I am trying to implement client-server communication via sockets. The main tasks are:
Sending commands from clients to server
Sending data from server to clients
Sending data from clients to server
Commands should come via port1, data via port2.
I got it working without without multi-threading but I have some problems with understanding how do I need to handle sockets.
Current scenario:
1.Server starts (socket, bind, listen for both command and info sockets) and goes for infinite loop with this function:
void FTPServer::waitForConnection()
{
sockaddr_in client;
int clientsize = sizeof(client);
SOCKET clientSocket = accept(_infoSocket, (struct sockaddr*)&client, &clientsize);
if (clientSocket == INVALID_SOCKET)
{
cout << " Accept Info Error" << endl;
}
else
{
cout << " Accept Info OK" << endl;
char* buff = new char[CHUNK_SIZE];
string fullRequest;
int rc = recv(clientSocket, buff, CHUNK_SIZE, 0);
if (rc == SOCKET_ERROR)
{
cout << " Recieve Info Error" << endl;
}
else
{
buff[rc] = NULL;
fullRequest.append(buff);
cout << " Recieve Info OK" <<endl;
if (executeCommand(fullRequest, clientSocket))
{
logOperation(client, fullRequest.c_str());
}
}
delete buff;
}
}
2.Client starts (socket, connect), creates 2 sockets on same ports, waits for user input.
3.User types "LIST", clients checks that it's a valid command and sends it.
bool FTPClient::sendToServer(string data, const bool verbose)
{
int n = 0;
while (data.size() > CHUNK_SIZE)
{
string s = data.substr(CHUNK_SIZE).c_str();
n += send(_infoSocket, data.substr(CHUNK_SIZE).c_str(), CHUNK_SIZE, 0);
data = data.substr(CHUNK_SIZE+1);
}
n+=send(_infoSocket, data.c_str(), data.size(), 0);
cout<<n<<endl;
if(n<0)
{
cout<<"Error: sending"<<endl;
return 0;
}
if (verbose)
cout<<"Send "<<n<<" bytes"<<endl;
return true;
}
4.Servers receives it, accepts on _dataSocket and sends the list of available files.
5.Client receives the list:
string FTPClient::getDataFromServer(const bool verbose)
{
char data[CHUNK_SIZE];
int size = recv(_dataSocket, data, strlen(data), 0);
if (size > 0)
{
int n = 0;
string res;
while (size > CHUNK_SIZE)
{
int buff = recv(_dataSocket, data, CHUNK_SIZE, 0);
res.append(data);
size -= buff;
n += buff;
}
n+= recv(_dataSocket, data, CHUNK_SIZE, 0);
res.append(data);
if (verbose)
cout<<"Recevied "<<n<<" bytes"<<endl;
res.resize(n);
return res;
}
else
{
return "";
}
}
Till this, it works. But if try to execute same command again, I got nothing.
I think, problem is that for each connect we need an accept on server side.
In main loop server gets only one connect from client. Is closing client command socket and reconnecting it on every request only option here? Any other advices (except for "Google it") are highly appreciated.
Related
My goal is to create a user-server connection. Most importantly I'm not willing to use threads.
For now, I want it to work as a simple chat. I believe The issue is that It goes from one user to another in a loop waiting for their getline input, so technically only one user can send a message at a time. What I wish is that my code could handle multiple messages from one user continuously, not having to wait for all other users to send a message
That's the running loop inside the server:
while (running)
{
fd_set copy = master;
// See who's talking to us
int socketCount = select(0, ©, nullptr, nullptr, nullptr);
// Loop through all the current connections / potential connect
for (int i = 0; i < socketCount; i++)
{
// Makes things easy for us doing this assignment
SOCKET sock = copy.fd_array[i];
// Is it an inbound communication?
if (sock == listening)
{
// Accept a new connection
sockaddr_in client;
int clientSize = sizeof(client);
SOCKET clientsocket = accept(listening, (sockaddr*)&client, &clientSize);
// Add the new connection to the list of connected clients
FD_SET(clientsocket, &master);
// Send a welcome message to the connected client
string welcomeMsg = "Welcome to the Awesome Chat Server!\r\n";
send(clientsocket, welcomeMsg.c_str(), welcomeMsg.size() + 1, 0);
}
else // It's an inbound message
{
char buf[4096];
ZeroMemory(buf, 4096);
// Receive message
int bytesIn = recv(sock, buf, 4096, 0);
if (bytesIn <= 0)
{
// Drop the client
closesocket(sock);
FD_CLR(sock, &master);
}
else
{
// Send message to other clients, and definiately NOT the listening socket
for (int i = 0; i < master.fd_count; i++)
{
SOCKET outSock = master.fd_array[i];
if (outSock != listening && outSock != sock)
{
ostringstream ss;
ss << "SOCKET #" << sock << ": " << buf << "\r\n";
string strOut = ss.str();
send(outSock, strOut.c_str(), strOut.size() + 1, 0);
}
}
}
}
}
}
I belive this code should work, the issue is in my client code:
char buf[4096];
string userInput;
do
{
// Prompt the user for some text
cout << "> ";
getline(cin, userInput); //THATS THE ISSUE, IT WAITS FOR GETLINE
if (userInput.size() > 0) // Make sure the user has typed in something
{
// Send the text
int sendResult = send(sock, userInput.c_str(), userInput.size() + 1, 0);
if (sendResult != SOCKET_ERROR)
{
// Wait for response
ZeroMemory(buf, 4096);
int bytesReceived = recv(sock, buf, 4096, 0);
if (bytesReceived > 0)
{
// Echo response to console
cout << "SERVER> " << string(buf, 0, bytesReceived) << endl;
}
}
}
} while (userInput.size() > 0);
My question is if its really caused by getline and if so, how can it be fixed?
The answer to your question is "yes," but with a big proviso: a properly designed server shouldn't care what the client is doing. You might want to look into select() or, if you anticipate a large user community, poll(). You don't want a multi-user server to depend on/wait for a single client.
The following is the client-side code for a UDP client-server echo program :
ret_val = sendmmsg(socket_id, msgs, no_of_packets, 0);
//I send message to the server
if(ret_val == -1)
std::cerr << "Message sending failed.\n";
else{
cout << ret_val << " messages sent\n";
/************************************************************************/
char buffers[no_of_packets][packet_size + 1];
msgs = new struct mmsghdr[no_of_packets];
iovecs = new struct iovec[no_of_packets];
memset(msgs, 0, sizeof(msgs));
memset(iovecs, 0, sizeof(iovecs));
for(int i = 0;i < no_of_packets;i++){
iovecs[i].iov_base = buffers[i];
iovecs[i].iov_len = packet_size;
msgs[i].msg_hdr.msg_iov = &iovecs[i];
msgs[i].msg_hdr.msg_iovlen = 1;
}
//and receive the packet here, but the program hangs here
ret_val = recvmmsg(socket_id, msgs, no_of_packets, 0, NULL);
My program hangs here, any idea why it's happening ? The following is the server-side code which first receives and then sends successfully, but after the server sends for the first time, my client isn't able to read it as it hangs.
ret_val = recvmmsg(socket_id, msgs, no_of_packets, 0, NULL);
if(ret_val < 0){
break;
}
else{
cout << ret_val << " messages received\n";
for(int i = 0;i < ret_val;i++){
buffers[i][msgs[i].msg_len] = 0;
printf("Trip %d : %s\n", trip, buffers[i]);
}
/************************************************************************/
if(connect(socket_id, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1){
perror("connect()");
exit(EXIT_FAILURE);
}
ret_val = sendmmsg(socket_id, msgs, no_of_packets, 0);
//This send is successful, but since my client hangs,
//the server hangs as well since the 'recvmmsg' at the top gets nothing from the client
if(ret_val == -1)
std::cerr << "Message sending failed.\n";
else
cout << ret_val << " messages sent\n";
This line in the server code looks suspicious:
if(connect(socket_id, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1)
What is server_addr? It's certainly not the source address of any of the packets return from the prior call to recvmmsg.
Just remove the connect call.
I could write more, but is there any particular reason you are using recvmmsg and sendmmsg instead of recvfrom and sendto ?
Below is a much simpler way of implementing an echo server with a udp socket:
const int MAX_UDP_MESSAGE_SIZE = 65535
unsigned char message[MAX_UDP_MESSAGE_SIZE+1];
int rcvReslt, sndResult;
sockaddr_in addr = {};
socklen_t addrLength = sizeof(addr);
rcvResult = recvfrom(socket_id, message, MAX_UDP_MESSAGE_SIZE, 0, (sockaddr*)&addr, &addrLength);
if (rcvResult > 0)
{
message[rcvResult] = 0; // null terminate the message
printf("Trip %d : %s\n", trip, message);
// echo back
sndResult = sendto(socket_id, message, rcvResult, 0, (sockaddr*)&addr, addrLength);
}
else
{
int error_code = errno;
printf("Error: %d\n", error_code);
}
Clearly you're connected to the wrong target. You don't need to connect at all. recvfrommsg() both return the source IP:port. Just send back to the same place.
I am building a server using Sockets on Linux, and I'm having a weird problem: if I try to send a request via my browser (Chrome), my code will create two sockets for communication using accept. Here's the code that accepts a connection:
server->poll();
if (server->canRead(server))
{
cout << "Connection!" << endl;
newSocket = server->accept(poolSize);
}
And here are the functions used (which are part of a wrapper I wrote for the C socket library).
int ServerSocket::poll(int timeout)
{
int rc = ::poll(this->descriptors, this->maxDescriptors, timeout);
if(rc == -1)
throw std::runtime_error(std::string("Error: ") + strerror(errno));
return rc;
}
bool ServerSocket::canRead(Socket *socket)
{
if(socket == NULL)
throw std::invalid_argument(std::string("Error: null socket!"));
for (int i = 0; i < this->maxDescriptors; i++)
{
if (socket->socketDescriptor == this->descriptors[i].fd)
{
if (this->descriptors[i].revents & POLLIN)
return true;
break;
}
}
return false;
}
ClientSocket* ServerSocket::accept(const int poolSize)
{
socklen_t endpointSize = sizeof(this->endpoint);
int newDescriptor = ::accept(this->socketDescriptor,
(struct sockaddr *)&endpoint, &endpointSize);
if(newDescriptor == -1)
throw std::runtime_error(std::string("Error in accept: ") + strerror(errno));
ClientSocket *newSocket = NULL;
try
{
newSocket = new ClientSocket(newDescriptor, this->port, poolSize);
}
catch (std::exception e)
{
std::cout << "Allocation error!" << endl;
throw e;
}
return newSocket;
}
If I compile and run this code, my output is:
Connection!
Connection!
I've used both telnet and netcat to analyse the requisition made by the browser, and it shows no anomaly. I've also sent requests manually via telnet and netcat, and the server works just fine, no duplicate connections. What could be causing this behaviour?
I have a TCP application written in C++, where a client and a server exchange data. I've istantiated a socket, believing that it would have been blocking by default; on the contrary, after server waits for a client, I have that client calls the recv function without waiting for data. This is the code in which I inizialize the socket fr the client.
int TCPreceiver::initialize(char* address, int port)
{
sock = socket (AF_INET, SOCK_STREAM, 0);
cout << "Socket: " << sock << endl;
sockaddr_in target;
target.sin_family = AF_INET;
target.sin_port = htons (port);
target.sin_addr.s_addr = inet_addr(address);
int fails=0;
while (connect(sock, (sockaddr*) &target, sizeof(target)) == -1)
{
fails++;
if (fails==10)
{
close(sock);
cout << "Error with connection to the server, try again"<< endl;
exit(-1);
}
}
cout << "Client connected (control channel)" << endl;
unsigned char text[10]; //Request message
//fill text[]
if(send(sock, (char*)text, 10, 0)==-1)
{
printf("send() failed with error code : %d" , -1);
exit(EXIT_FAILURE);
}
return 0;
}
I've tried adding this code:
int opts;
opts = fcntl(sock,F_GETFL);
if (opts < 0) {
perror("fcntl(F_GETFL)");
exit(0);
}
opts = (opts & (~O_NONBLOCK));
if (fcntl(sock,F_SETFL,opts) < 0) {
perror("fcntl(F_SETFL)");
exit(0);
}
but it still doesn't work, and if I call the recv(), the application doesn't block (and recv() always returns 0). Here is the function where I call the recv():
void TCPreceiver::receive(char* text, int& dim)
{
int ret;
ret = recv(sock, text, dim, 0);
dim=ret;
if(ret == -1){
printf("recv() failed with error (%d)\n", ret);
//system("PAUSE");
exit(1);
}
}
Where am I wrong?
recv() returning zero indicates either (1) you passed a zero length, which is just a programming error which I won't discuss further here, or (2) end of stream. The peer has close the connection. This isn't a non-blocking situation, this is the end of the connection. You must close the socket and stop using it. It will never return anything. It zero ever again.
See the man pages.
I got all of this code from the beej guide so all of the accepting can be seen from there. In the Beej's code, he gets a message from a client, and then sends the message to all the other clients. This can be seen from this snippet here:
// handle data from a client
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
//handle error
}
}
else {
// we got some data from a client
for(j = 0; j <= fdmax; j++) {
// send to everyone!
if (FD_ISSET(j, &master)) {
// except the listener and ourselves
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
} // END handle data from client
Instead of sending the same message to all the clients, i would like to adapt it into a request/reply feature and send a reply to the same client I received data from.
here is what I have so far:
long length = 0;
string stringRead;
messagebroker broker;
read( i, &length, sizeof( length ) );
length = ntohl( length );
if(length > -1)
while ( 0 < length ) {
char buffer[1024];
int cread;
cread = read( i, buffer, min( sizeof( buffer ), length ) );
stringRead.append( buffer, cread );
length -= cread;
}
cout << "Got Message: " + stringRead << endl;
string response = broker.handleMessage(stringRead.c_str());
cout << "sending response" << response << endl;
//socket ready for writing
if (FD_ISSET(i, &master)) { //should i check to see if write_fds? I have this here
//simply because the guide has it, but i am suspicious
//it is there so we can not write to the master.
length = htonl( response.length() );
cout << "sent length" << endl;
if(send( i, &length, sizeof(length) , 0) == 0){
fprintf(stderr, "Error sending data %d\n", errno);
exit(3);
}
if(send( i, response.data(), response.length(),0 )== 0){
fprintf(stderr, "Error sending data %d\n", errno);
exit(3);
}
} //end if
I receive all data from the client at the server. I then am not sure if the problem is writing the data back on the server, or reading from the client. I assume it is writing to the client from the server. As I hinted in the comments, I think I know where I went wrong, but I have removed this if statement, and I still wasn't able to read anything on the client side. Do I need to set a writable flag at the very beginning? Please let me know if you need anything else. Sorry this post was so long.
Just do the write. If it returns -1/EWOULDBLOCK, or a value indicating that it didn't write the full response, then you add the FD to the writefds, and continue the write when the FD becomes writable. You normally don't have any writefds, as FDs are normally writable, that is to say they normally have space in their socket send buffers.