I have a few questions about select function, i wrote this code:
void TCPSerwer::sel()
{
struct timeval tv = {1, 0};
fd_set temp_list = m_RecvList;
//if(select(m_fdmax + 1, &temp_list, NULL, NULL, &tv) == SOCKET_ERROR)
if(select(0, &temp_list, NULL, NULL, &tv) == SOCKET_ERROR)
{
perror( "select" );
exit( 1 );
}
for(int i = 0; i <= m_fdmax; i++ )
{
if(FD_ISSET(i, &temp_list))
{
// New connection
if(i == m_ListenSocket)
{
acceptClient();
}
// Data from client
else
{
PacketHeader header;
int nbytes = recv(i, (char*)(&header), sizeof(PacketHeader),
// Error
if(nbytes < 0)
{
disconnectClient(i);
}
// success
else
{
std::cout << "type: " << header.type << " len: " << header.length << std::endl;
}
}
}
}
}
I can give first arg to select function and i can don't do that, but why ? Why a should give first arg to select ? m_fdmax is highest number of socket, but this code working without this arg.
Next question is, why select need timeout ? When i don't give this arg select marks all socket as socket that can be readable but select doing this when socket haven't any data to read. When i give this arg i don't have this problem. But why ?
if m_fdmax is highest number of socket, i have to find next highest number of socket when i close connection, Right ? And i should doing this that:
int size = m_ClientVector.size();
for(int i = 0; i < size; i++)
{
if(m_ClientVector[i] > m_fdmax)
m_fdmax = m_ClientVector[i];
}
I can give first arg to select function and i can don't do that, but why ? Why a should give first arg to select ? m_fdmax is highest number of socket, but this code working without this arg.
Read the documentation. The select() function on Windows ignores the first parameter, so it does not matter what you pass to it.
Next question is, why select need timeout ?
It does not NEED a timeout, but you can OPTIONALLY provide a timeout if desired. This way, if the requested socket state is not reached before the timeout elapses, select() can still exit and not deadlock the calling thread indefinitely, allowing it to do other things.
When i don't give this arg select marks all socket as socket that can be readable but select doing this when socket haven't any data to read.
If you do not provide a timeout, select() waits indefinitely until the requested socket state actually occurs. A socket can be marked as readable if it has data to read, but it can also be marked as readible if it has been gracefully disconnected by the other party. A subsequent call to recv() will tell you which is the case (recv() returns -1 on error, 0 on disconnect, an >0 on data). Again, read the documentation.
if m_fdmax is highest number of socket, i have to find next highest number of socket when i close connection, Right ?
If you want to calculate the highest socket number (which Windows does not care about, but other platforms do), then you would have to re-calculate the highest socket number every time you call select(), or at least whenever you re-prepare the fd_set structure (which you need to do every time you call select() anyway).
And i should doing this that
On Windows, no. On other platforms, yes.
With that said, try this code on Windows instead:
void TCPSerwer::sel()
{
struct timeval tv = {1, 0};
fd_set temp_list = m_RecvList;
int ret = select(0, &temp_list, NULL, NULL, &tv);
if (ret == SOCKET_ERROR)
{
perror( "select" );
exit( 1 );
}
if (ret == 0) // timeout
return;
for(u_int i = 0; i < temp_list.fd_count; ++i)
{
SOCKET s = temp_list.fd_array[i];
// New connection
if (s == m_ListenSocket)
{
acceptClient();
continue;
}
// Data from client
PacketHeader header;
char *pheader = (char*) &header;
int nbytes = 0;
do
{
ret = recv(s, pheader, sizeof(PacketHeader)-nbytes, 0);
// success
if (ret > 0)
nbytes += ret;
}
while ((ret > 0) && (nbytes < sizeof(PacktHeader)));
// Error or disconnect
if (nbytes < sizeof(PacktHeader))
{
disconnectClient(i);
continue;
}
// success
std::cout << "type: " << header.type << " len: " << header.length << std::endl;
}
}
About the timeout:
select can use a struct timeval to timeout. If you pass a NULL pointer, select will wait until an event comes. If you pass an address to a struct timeval, select will return even if there's not event (in your code, select will return every second).
About the fdmax: Yes you have to find the highest socket, and your snippet is correct.
Other: You don't have any FD_SET in your code. Usually, the sockets are setted (via FD_SET so) in the loop which find the highest socket.
EDIT: My bad I didn't seen the fd_set temp_list = m_RecvList; in your code. We will need more code in order to analyze your problem with select.
Thanks for help, I want use this code on Windows and Linux, and now I doing that:
When I have a new connection:
bool TCPSerwer::acceptClient()
{
SOCKET new_client = accept(m_ListenSocket, 0, 0);
if(new_client == INVALID_SOCKET)
return false;
m_ClientVector.push_back(new_client);
// Add to FD
FD_SET(new_client, &m_RecvList);
if(new_client > m_fdmax)
m_fdmax = new_client;
return true;
}
When i want to close connection:
void TCPSerwer::disconnectClient(const SOCKET& client)
{
int size = m_ClientVector.size();
for(int i = 0; i < size; i++)
{
if(m_ClientVector[i] == client)
{
closesocket(m_ClientVector[i]);
// Delete from FD
FD_CLR(m_ClientVector[i], &m_RecvList);
m_ClientVector.erase(m_ClientVector.begin() + i);
break;
}
}
// re-calculateing the highest socket number
size = m_ClientVector.size();
for(int i = 0; i < size; i++)
{
if(m_ClientVector[i] > m_fdmax)
m_fdmax = m_ClientVector[i];
}
}
I havr one question to you Remy Lebeau, your recv function look that:
recv(s, pheader, sizeof(PacketHeader)-nbytes, 0);
but recv save data at the being of bufor ? Meybe this should look that:
recv(s, pheader + nbytes, sizeof(PacketHeader)-nbytes, 0);
Related
I made a server socket in C++ for Unix (TCP), that accepted only one client socket. Today, I attempted to make it accept multiple ones. For some reason, it ends up only accepting 1, and it's not receiving messages sent by the client that IS able to connect. I believe that the select function isn't working properly. FYI: I'm new to socket programming, so please understand if it's a stupid mistake.
Here's the code:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/select.h>
#define PORT 8080
#define MAXCLIENTS 30
int main()
{
//create a socket
int listening = socket(AF_INET, SOCK_STREAM, 0); //(returns int) - makes socket (returns what socket it is in terms of an int)
if(listening < 0) //check if we were able to make a socket!
{
std::cerr << "Can't create socket!" << std::endl;
return -1;
}
//bind the socket to an IP/Port
sockaddr_in hint{}; // This creates a structure for the ipv4 info of the socket.
hint.sin_family = AF_INET; //set the family to ipv4
hint.sin_port = htons(PORT); //set the port to the macro set above (use host-to-network-short to conver the int to the port)
hint.sin_addr.s_addr = INADDR_ANY; //set the ip to any address
if(bind(listening, (sockaddr*)&hint, sizeof(hint)) < 0) //attempt to bind socket (inticated by what number the `listening` socket is) to ip/port
{
std::cerr << "Can't bind to IP/Port" << std::endl;
return -1;
}
//mark the socket for listening
if(listen(listening, MAXCLIENTS) < 0 /*SOMAXCONN = maximum amount of connections, defined by sys/socket.h*/) //attempt to listen on the socket number indicated by `listening`
{
std::cerr << "Can't listen on the socket!" << std::endl;
return -1;
}
//FD_CLR() = Remove 1 from set
//FD_SET() = Add to set
//FD_ZERO() = Remove everything from set
//FD_ISSET() = Check if something is part of a set
fd_set master; //define the set
int max_sd;
int client_socks[MAXCLIENTS]{};
while (true)
{
FD_ZERO(&master); //make sure it's cleared
FD_SET(listening, &master); //add the listening socket (server) to the set
max_sd = listening; //max socket descriptor set to the listening socket (need this for the select func)
for (int i = 0; i < MAXCLIENTS; i++) {
if (client_socks[i] > 0) //make sure the particular socket exists
{
FD_SET(client_socks[i], &master); //add it to the set
}
if (client_socks[i] > max_sd) //if the socket is greater than our current maximum socket descriptor
{
max_sd = client_socks[i];
}
}
//wait for some action on any socket within the master fd (this will set the master fd_set to be equal to whatever socket had some action on it)
int activity = select(max_sd + 1, &master, nullptr, nullptr, nullptr);
if (activity < 0) //error!
{
std::cerr << "Error while trying to select!" << std::endl;
}
int addrlen = sizeof(hint);
if (FD_ISSET(listening, &master)) //if the select got that there was action on the listening (server) socket - most likely, a client socket is trying to connect!
{
int client_socket;
client_socket = accept(listening, (sockaddr *) &hint, &addrlen); //accept the first client "waiting to get in"
if (client_socket < 0) {
std::cerr << "Something went wrong when trying to accept a client socket!" << std::endl;
break;
}
std::cout << "New connection: " << inet_ntoa(hint.sin_addr) << " on port " << ntohs(hint.sin_port) << std::endl;
if (send(client_socket, "Welcome to the socket party!", strlen("Welcome to the socket party!"), 0) != strlen("Welcome to the socket party!")) { //greetings!
std::cerr << "Error when sending welcome message." << std::endl; //something went wrong ;(
}
for (int i = 0; i < MAXCLIENTS; i++) //for each index, set client_sock to the address of the index of client_socks, so that we can set the value of it!
{
if(client_socks[i] == 0) //if this position is null (0)
{
client_socks[i] = client_socket;
}
}
}
//else, there was action on a client socket (most likely a message is being sent!
char buffer[2048]; //we need somewhere to store clients messages!
for (int &client_sock : client_socks) //loop through the client sockets
{
if (client_sock != 0)
{
if (FD_ISSET(client_sock, &master) == 0) //check if the select got action on the particular index in the client_socks array
{
memset(&buffer, 0, sizeof(buffer)); //make sure the buffer is clear!
if (read(client_sock, &buffer, 2048) == 0) //check if nothing was recieved from the client
{
getpeername(client_sock, (sockaddr*)&hint, (socklen_t*)&addrlen) < 0; //gets networking info, based off of which socket is passed (the if statement checks for errors). In addition, it sets the values of hint to the info from the passed socket
//print that the client disconnected
std::cout << "A client has disconnected! IP: " << inet_ntoa(hint.sin_addr) << " Port: " << ntohs(hint.sin_port) << std::endl;
close(client_sock); //close the socket
client_sock = 0; //set its value in the array to 0, so that we can reuse it!
}
//else, we got message from the client
for (int &socket : client_socks)
//if (socket != client_sock)
send(socket , buffer , strlen(buffer) , 0 );
}
}
}
return 0;
}
}
Your client_socks variable must be initialised, as Igor said - just append braces: int client_socks[MAXCLIENTS]{};.
Then your for (int &client_sock : client_socks) loop doesn't ignore 0 values (which you obviously want to be a sentinel given "if(client_socks[i] == 0) //if this position is null (0)"). Inside that loop, add "if (client_sock[i] == 0) continue;. Because you try to read` from descriptor 0, it will block waiting for keyboard input.
Further, if you wanted your code to be robust, you would make the listening socket non-blocking, as it's possible for that socket to select readable, but by the time your application goes to accept from it, the client connection attempt has already been dropped: then you'd block waiting to accept another client connection attempt that might never come, and not be servicing existing clients.
I am new in Socket Programming. I am trying to create a Application that uses Sockets for Communication.
I have doubt in the Receive function because sometime it just hangs in recvfrom function.
I am using select function for polling. It works when Camera is connected but If I remove Camera it doesn't show the Error Message.
My Code for Polling:
FD_ZERO(&m_readFds);
FD_SET(Sock, &m_readFds);
m_timeInterval.tv_usec = 30; //30 Microseconds for Polling
m_socketLength = sizeof(m_cameraInfo);
m_lastBlockId = -1;
while (m_acquiringThreadStatus)
{
FD_CLR(Sock, &m_readFds);
FD_SET(Sock, &m_readFds);
m_receivingStatus = select(Sock + 1, &m_readFds, NULL, NULL, &m_timeInterval);
if (m_receivingStatus < 0)
{
std::cout << "No Data to Receive"<<std::endl;
}
else
{
if ((m_receivedBytes = recvfrom(Sock, m_packetBuffer, RECEIVING_BUFFER_SIZE, 0, (struct sockaddr*)&m_cameraInfo, &m_socketLength)) == SOCKET_ERROR)
{
std::cout << "ERROR" << std::endl;
}
else
{
std::cout<<"Data Received"<<std::endl;
}
}
}
An one more question is that when I continously printing the Data Received Statement after sometime It stop. So how can I increase the size of Socket Receiving Buffer.
Thanks in Advance
Edit
SOCKET m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(m_sock == INVALID_SOCKET)
{
// Error
}
else
{
//In the else part bind the socket
}
If you read select()'s documentation, you would see that select() returns -1 on error, 0 on timeout, and > 0 on requested event(s). However, that is not how you are treating the return value. You are treating -1 as timeout and >= 0 as data events. So, you end up calling recvfrom() when there is nothing available to read. If the socket is in blocking mode (its default mode), recvfrom() will block the calling thread until data is actually available.
Try this instead:
m_lastBlockId = -1;
while (m_acquiringThreadStatus)
{
FD_ZERO(&m_readFds);
FD_SET(Sock, &m_readFds);
m_timeInterval.tv_sec = 0;
m_timeInterval.tv_usec = 30; //30 Microseconds for Polling
m_receivingStatus = select(Sock + 1, &m_readFds, NULL, NULL, &m_timeInterval);
if (m_receivingStatus == SOCKET_ERROR)
{
std::cout << "ERROR" << std::endl;
break;
}
if (m_receivingStatus == 0)
{
std::cout << "No Data to Receive" << std::endl;
continue;
}
m_socketLength = sizeof(m_cameraInfo);
if ((m_receivedBytes = recvfrom(Sock, m_packetBuffer, RECEIVING_BUFFER_SIZE, 0, (struct sockaddr*)&m_cameraInfo, &m_socketLength)) == SOCKET_ERROR)
{
std::cout << "ERROR" << std::endl;
break;
}
std::cout << "Data Received" << std::endl;
}
As for the size of the socket's receive buffer, you can set it via setsockopt() using the SO_RCVBUF option, eg:
int bufsize = ...;
setsockopt(Sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, sizeof(bufsize));
heyho,
i'm trying to make a simple p2p program, that should be able to let at least 3 peers exchange musicdata (author/title) that is stored in 1 file per peer through port 50001-50010. to say it bluntly it's homework and we never really talked about p2p nor did i found any usefull and simple p2p examples without special libraries etc. with beej's guide to network programming and some other sources i got an already halfway working program but i have 1 big problem:
my select call is for the whole loop just for the first client/peer every time 1 for all the other it is just 0 and so they will never receive any data
my p2p code:
case 6:
{
if(!filename.empty())
{
time_t start, now;
time(&start);
double elapsedtime = 0;
char *recvbuf;
char *sendbuf;
recvbuf = new char[1024];
sendbuf = new char[1024];
int recvfd, sendfd,err, port;
int errormarker = 0;
if((recvfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
{
std::cout << "Error setting socket\n";
errormarker =1;
}
if(errormarker ==1)
break;
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(50001);
for (int i =0; i<=9;i++)
{
port = 50001+i;
errormarker = bind(recvfd,(sockaddr*) &server_address, sizeof (server_address));
if(errormarker ==-1)
server_address.sin_port = htons(server_address.sin_port+1);
else
break;
}
if(errormarker == -1)
{
std::cout << "all ports are used, please wait a minute and try again\n";
break;
}
std::cout << "Searching for other musicdata owners\n";
fd_set readset;
while (elapsedtime < 15)
{
if(errormarker != 0)
break;
memset(recvbuf, 0, sizeof(recvbuf));
memset(sendbuf, 0, sizeof(sendbuf));
struct timeval tv;
tv.tv_sec =0;
tv.tv_usec = rnd(10000,50000);
FD_ZERO(&readset);
FD_SET(recvfd, &readset);
int result = select(recvfd + 1, &readset, NULL, NULL, &tv);
std::cout << result << "\n";
if (result >0)
{
result = recvfrom(recvfd, recvbuf, 1024, NULL,NULL,NULL);
if (result != -1)
{
buftofile(recvbuf,filename);
addnewdata(filename);
}
else
std::cout << "error receiving data \n";
}
filetobuf(sendbuf,filename);
for(int i = 50001; i<=50010;i++)
{
struct addrinfo hints, *servinfo, *p;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype =SOCK_DGRAM;
std::string s_port = to_string(i);
err = getaddrinfo(NULL,s_port.c_str(),&hints,&servinfo);
for(p=servinfo;p!=NULL;p=p->ai_next)
{
if(i == port)
{
err = sendto(recvfd, sendbuf,strlen(sendbuf), 0, p->ai_addr,p->ai_addrlen);
continue;
}
if((sendfd = socket(p->ai_family, p->ai_socktype,p->ai_protocol)) == -1)
{
continue;
}
else
{
err=sendto(sendfd,sendbuf,strlen(sendbuf), 0, p->ai_addr,p->ai_addrlen);
close(sendfd);
}
}
freeaddrinfo(servinfo);
}
time(&now);
elapsedtime=difftime(now,start);
}
close(recvfd);
}
else
std::cout << "Error, please choose a file to save your data first!" << std::endl;
}
break;
the functions are all tested and work as they should.
(on a sitenote i got a little irregularity that startles me slightly:
when i run all 3 clients and the first one gets all the data he gets everytime the same line missing exactly 3 characters --- every time again --- i'm running the programm from one pc and can't completely understand, why that happens every time (i guess i should add a start- and an endsign to remove the problem, but the fact, that the same line comes every time fascinates me though). Is there an explanation to that phenomen?)
thanks in advance for the help.
Instead of checking which socket from the read set received data, you are directly calling recvfrom on recvfd descriptor. You need to check all of them in a loop.
This is what i could gather in a quick review.
But here is a tcp based example i wrote a long time ago. Your's is udp but you should be able to find the source of your problem easily.
P.S. There was one mistake in code that some one pointed out and fixed in the comments of the post.
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.
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.