I need to make the code more readable, but it has a lot of cycle, can I fix this somehow?
The variable i constantly jumps there and there, it is very inconvenient to watch in debugging.
for(;;)
{
//
// For connection orientated protocols, we will handle the
// packets received from a connection collectively. For datagram
// protocols, we have to handle each datagram individually.
//
//
// Check to see if we have any sockets remaining to be served
// from previous time through this loop. If not, call select()
// to wait for a connection request or a datagram to arrive.
//
for (i = 0; i < numSocket; i++)
{
if (FD_ISSET(servSock[i], &SockSet))
break;
}
if (i == numSocket)
{
for (i = 0; i < numSocket; i++)
{
FD_SET(servSock[i], &SockSet);
}
if (select(numSocket, &SockSet, 0, 0, 0) == SOCKET_ERROR)
continue;
}
for (i = 0; i < numSocket; i++)
{
if (FD_ISSET(servSock[i], &SockSet))
{
FD_CLR(servSock[i], &SockSet);
break;
}
}
...
}
To make the code more readable (and testable and maintainable) you can encapsulate some of the logic into separate functions with meaningful names. Consider this version of your loop
for(;;)
{
int i = find_socket_to_be_served(numSocket, servSock, &SockSet);
if (i == numSocket)
{
set_all_sockets(numSocket, servSock, &SockSet);
if (select(numSocket, &SockSet, 0, 0, 0) == SOCKET_ERROR)
continue;
}
// ...
}
Where the two functions used are defined as
int find_socket_to_be_served(int n_sockets, int *fds, fd_set *fdset)
{
int i = 0;
for (; i < n_sockets; i++)
{
if (FD_ISSET(fds[i], fdset))
{
FD_CLR(fds[i], fdset);
break;
}
}
return i;
}
void set_all_sockets(int n_sockets, int *fds, fd_set *fdset)
{
for (int i = 0; i < n_sockets; i++)
{
FD_SET(fds[i], fdset);
}
}
As far as I understand your code, you don't need FD_CLEAR() your sockets one by one.
Just FD_ZERO() all of them at once, then use a single loop to FD_SET() them again.
Note that after select(), many sockets could be ready to use; you don't have to break when one has been used.
Note also that the first parameter of select() is unused on Windows but should be 1+ the max of your socket file descriptors (not the number of them) everywhere else.
for(;;)
{
FD_ZERO(&SockSet);
for (int i = 0; i < numSocket; i++)
{
FD_SET(servSock[i], &SockSet);
}
if (select(numSocket, &SockSet, 0, 0, 0) == SOCKET_ERROR)
continue; // or probably break...
}
for (int i = 0; i < numSocket; i++)
{
if (FD_ISSET(servSock[i], &SockSet))
{
// use servSock[i]
}
}
}
Related
I have a word guessing game which I needs to repeat until the user types Q:. Generally, the answer is in a format A: answer. The game's using sockets!
Instead of while(1), I tried using while(buffer[0]!='Q') but that does not seem to work.
How do I keep making the client play the game?
Additionally, I also need to tell the user how many games he passed correctly?
while (1)
{
FD_ZERO(&readfds);
FD_SET(server_fd, &readfds);
int max_fd = server_fd;
for (int i = 0; i < NUMCLIENT; i++)
{
file_descriptor = client_sockets[i];
if (file_descriptor > 0)
{
FD_SET(file_descriptor, &readfds);
}
if (file_descriptor > max_fd)
{
max_fd = file_descriptor;
}
}
int return_value = select(max_fd + 1, &readfds, NULL, NULL, NULL);
if (return_value == -1)
{
printf("Select Error\n");
}
if (FD_ISSET(server_fd, &readfds))
{
if ((new_socket = accept(server_fd, (struct sockaddr *) &address,
(socklen_t*) &addrlen)) < 0)
{
perror("accept");
}
send(new_socket, greetings[0], strlen(greetings[0]), 0);
send(new_socket, greetings[1], strlen(greetings[1]), 0);
for (int i = 0; i < NUMCLIENT; i++)
{
if (client_sockets[i] == 0)
{
my_fortune(i);
client_sockets[i] = new_socket;
send(new_socket, client_challenges->question, strlen(client_challenges->question), 0);
break;
}
}
}
for (int i = 0; i < NUMCLIENT; i++)
{
file_descriptor = client_sockets[i];
if (file_descriptor == 0)
{
continue;
}
if (FD_ISSET(file_descriptor, &readfds))
{
memset(buffer, 0, 1024);
valread = read(file_descriptor, buffer, 1024);
if (valread == 0)
{
close(file_descriptor);
client_sockets[i] = 0;
continue;
}
printf("%s\n", buffer);
if (buffer[0] == 'A')
{
if (strlen(client_challenges->answer) == (strlen(buffer) - 4))
{
char store[100];
for (int i = 1; i <= strlen(client_challenges->answer); i++)
{
store[i - 1] = buffer[2 + i];
}
store[strcspn(store, "\r\n")] = 0;
if (strcmp(store, client_challenges->answer) == 0)
{
send(file_descriptor, correct, strlen(correct), 0);
//printf("O: Congratulation - challenge passed!\n");
}
else
{
send(file_descriptor, wrong, strlen(wrong), 0);
send(file_descriptor, client_challenges->answer, strlen(client_challenges->answer), 0);
}
//printf("Wrong guess - expected: %s",client_challenges->answer);}
}
else
{
send(file_descriptor, wrong, strlen(wrong), 0);
send(file_descriptor, client_challenges->answer, strlen(client_challenges->answer), 0);
}
//{printf("Wrong guess - expected: %s",client_challenges->answer);}
}
else if (buffer[0] == 'Q')
{
send(file_descriptor, ending, strlen(ending), 0);
exit(1);
}
else
{
send(file_descriptor, error, strlen(error), 0);
exit(1);
}
}
}
}
If there will only be 1 client, you can simply do
// Actual function to start the game
int play() {
int run = 1;
while (run) {
// run the game
// if game over,
run = 0;
}
return 0; // indicate the game ends gracefully
}
int main(int argc, char** argv) {
int run = 1;
int playAgain = 0;
while (run) {
if (play()) {
// Error: the game ends ungracefully
}
// Prompt for play again
if (!playAgain) {
run = 0;
}
}
// Bye
}
The reason is that the variable run is used to control the game flow. If the user quits or something went wrong, you can set run to 0 and the game will stop running.
If there are going to be multiple clients, you might want to have a look at pthread. Every time you receive a new connection, you can create a new thread with pthread_create and set the play function as start_routine and the fd as the argument. For example, you can try
int MAX_CLIENT = 5;
struct pthread_args {
int fd;
};
void* play(void* arg) {
struct pthread_args* args = (struct pthread_args*)arg;
int fd = args->fd;
// Start the game
}
int main(int argc, char** argv) {
pthread_t pth[MAX_CLIENT];
struct pthread_args pth_args;
int new_fd;
int i = 0;
while(1) { // The server continuously receive connections
// Accept connections
new_fd = accept(/*...*/)
if (new_fd < 0) {
// Error: accepting new connection
}
pth_args.fd = new_fd;
int r = pthread_create(&pth[i], NULL, play, &pth_args);
if (!r) {
// Error: creating thread
}
++i;
}
}
PS: You may want to have a look at pthread_detach as well.
Instead of while(1), I tried using while(buffer[0]!='Q') but that does not seem to work.
The reason while(buffer[0] == 'Q') doesn't work is because by the time the while check for the condition, the buffer (assuming it was declared outside of the while loop) might not be the same buffer anymore because you are looping over all clients and save the buffers from clients to the same buffer variable. So if there are 2 clients and the first client says 'Q' and the second client says 'A', the while sees buffer[0] as 'A' instead of 'Q' and hence continuing the game.
Also, I don't think exit(1) in else if (buffer[0] == 'Q') is a good idea. It kills the server and all clients will be forced to quit the game. Just remove the particular client from the client_sockets array.
:) Hope it helps
It's my first post so ask for remotely anything if it can help and I didn't provide it.
My application requires multiple sockets being opened at once from Master, then the slaves connect to WiFi, and then to the sockets
Problem is: I have to make it "bulletproof" against constant reconnecting from slaves and i get Accept error:
E (23817) TCP SOCKET: accept error: -1 Too many open files in system
It appears when I reconnect client for 5th time, when Max Number of Open Sockets = 5 in menuconfig,
I disconnect clients from the server when they don't send anything in 1second -> then i assume they got DC-d.
I do it with close() procedure.
void closeOvertimedTask(void * ignore)
{
while(1)
{
for(int i = 0; i < openedSockets;)
{
if(needsRestart[i] == 1)
{
ESP_LOGI("RESTARTING", " task#%d",i);
//lwip_close_r(clientSock[i]);
//closesocket(clientSock[i]);
//ESP_LOGI("closing result", "%d", close(clientSock[i]));
stopSocketHandler(i);
needsRestart[i] = 0;
//if(isSocketOpened[i])
{
}
ESP_LOGI("close", "%d", lwip_close_r(clientSock[i]));
isSocketOpened[i] = 0;
xTaskCreate( handleNthSocket, "TCP_HANDLER", 10*1024, &(sockNums[i]) , tskIDLE_PRIORITY, &socketHandlerHandle[i]);
configASSERT(socketHandlerHandle[i]);
needsRestart[i] = 0;
}
if(isSocketOpened[i])
{
int diff = ((int)((uint64_t)esp_timer_get_time()) - lastWDT[i]) - 2*TCPWDT;
if(diff > 0)
{
if(isSocketOpened[i])
{
ESP_LOGI("I FOUND OUT HE DC-d","");
//closesocket(clientSock[i]);
}
ESP_LOGI("close", "%d", close(clientSock[i]));
stopSocketHandler(i);
isSocketOpened[i] = 0;
xTaskCreate( handleNthSocket, "TCP_HANDLER", 10*1024, &(sockNums[i]) , tskIDLE_PRIORITY, &socketHandlerHandle[i]);
configASSERT(socketHandlerHandle[i]);
}
}
}
}
}
For each socket I run 1 task that is supposed to receive from that socket and act further.
For all of them I have an other task that checks last time a message arrived and restarts tasks when time has exceeded (it's 2 seconds)
I need around 16 sockets opened in the final version so there is no room to have sockets that are still closing after Slave has restarted whole connection
How to properly close a Task with running recv() procedure in it to properly close Socket.
Is there a way to read from Server side that socket has been closed if WiFi hasn't realized STA DC-d
Is this about TIME_WAIT from tcp stack ?
Socket read code:
void handleNthSocket(void * param) // 0 <= whichSocket < openedSockets
{
int whichSocket = *((int *) param);
ESP_LOGI("TCP SOCKET", "%s #%d", getSpaces(whichSocket), whichSocket);
struct sockaddr_in clientAddress;
while (1)
{
if(needsRestart [whichSocket] == 0)
{
socklen_t clientAddressLength = sizeof(clientAddress);
clientSock[whichSocket] = accept(sock[whichSocket], (struct sockaddr *)&clientAddress, &clientAddressLength);
if (clientSock[whichSocket] < 0)
{
ESP_LOGE("TCP SOCKET", "accept error: %d %s", clientSock[whichSocket], strerror(errno)); //HERE IT FLIPS
//E (232189) TCP SOCKET: accept error: -1 Too many open files in system
isSocketOpened[whichSocket] = 0;
needsRestart[whichSocket] = 1;
continue;
}
//isSocketOpened[whichSocket] = 1;
// We now have a new client ...
int total = 1000;
char dataNP[1000];
char *data;
data = &dataNP[0];
for(int z = 0; z < total; z++)
{
dataNP[z] = 0;
}
ESP_LOGI("TCP SOCKET", "%snew client",getSpaces(whichSocket));
ESP_LOGI(" ", "%s#%d connected",getSpaces(whichSocket), whichSocket);
lastWDT[whichSocket] = (uint64_t)esp_timer_get_time() + 1000000;
isSocketOpened[whichSocket] = 1;
// Loop reading data.
while(isSocketOpened[whichSocket])
{
/*
if (sizeRead < 0)
{
ESP_LOGE(tag, "recv: %d %s", sizeRead, strerror(errno));
goto END;
}
if (sizeRead == 0)
{
break;
}
sizeUsed += sizeRead;
*/
ssize_t sizeRead = recv(clientSock[whichSocket], data, total, 0);
/*for (int k = 0; k < sizeRead; k++)
{
if(*(data+k) == '\n')
{
ESP_LOGI("TCP DATA ", "%sthere was enter", getSpaces(whichSocket));
//ESP_LOGI("TIME ", "%d", (int)esp_timer_get_time());
}
//ESP_LOGI("last wdt", "%d", (int)lastWDT[whichSocket]);
}*/
lastWDT[whichSocket] = (uint64_t)esp_timer_get_time();
int diff = ((int)((uint64_t)esp_timer_get_time()) - lastWDT[whichSocket]) - 2*TCPWDT;
ESP_LOGI("last wdt", "%d, data = %s", (int)lastWDT[whichSocket], data);
if(diff > 0)
{
ESP_LOGI("last wdt", "too long - %d", diff);
isSocketOpened[whichSocket] = 0;
}
if (sizeRead < 0)
{
isSocketOpened[whichSocket] = 0;
}
//TODO: all RX from slave routine
for(int k = 0; k < sizeRead; k++)
{
*(data+k) = 0;
}
// ESP_LOGI("lol data", "clientSock[whichSocket]=%d,
/*if(sizeRead > -1)
{
ESP_LOGI("TCP DATA: ", "%c", *(data + sizeRead-1));
}
else
{
ESP_LOGI("TCP DC ", "");
goto END;
}*/
}
if(isSocketOpened[whichSocket])
{
ESP_LOGI("closing result", "%d", close(clientSock[whichSocket]));
}
}
}
}
I don't see you closing your sockets anywhere?
Sockets, no matter the platform, is usually a limited resource, and a resource that will be reused. If you don't close the sockets then the system will think that you still use then, and can't reuse those sockets for new connections (and on POSIX systems even opening files will be affected).
So close connections immediately when they are not needed any more.
Usually this is done by checking what recv and send returns: If they return a value less than zero an error occured and in most cases it's a non-recoverable errors, so connection should be closed. Even if it is a recoverable error, it's easier to close the connection and let the client reconnect.
For recv there's also the special case when it returns zero. That means the other end has closed the connection. That of course you need to close your end as well.
this post solved all my problems
https://www.esp32.com/viewtopic.php?t=911
So far everything have been working perfectly. Just the problem I've been having is that I can't think of a way of assigning or i would say giving names to the clients who are connect to the server so that other clients who know is wrote a message
server:
while (1) {
FD_ZERO(&read_set);
//FD_ZERO(&write_set);
FD_SET(fd, &read_set);
//FD_SET(fd, &write_set);
for (i = 0; i < num_clients; i++) { //at first this part will not excute
FD_SET(clients[i], &read_set);
}
select(fd + num_clients + 1, &read_set, NULL, NULL, NULL);
if (FD_ISSET(fd, &read_set)) {
if ((clients[num_clients++] = accept(fd, NULL, NULL)) == -1) {
perror("accept error");
continue;
}
printf("we got a connection!\n");
}
for (i = 0; i < num_clients; i++) {
if (FD_ISSET(clients[i], &read_set)) {
msg = read(clients[i], buf, sizeof(buf));
if (msg > 0) {
int savedclnt = clients[i];
printf("client %d says: %s\n", i, buf);
for (int p = 0; p<num_clients; p++)
{
if (clients[p] != savedclnt) {
//write("from %d",clients[p]);
//char msg2 = strcat(clients[i],msg);
write(clients[p], buf, msg);
}
}
}
}
}
}
For what you're doing, you don't really need threads. From personal experience, a single threaded server would do the job just fine. Instead of having the main body wrap around a call to accept() you should use select() to see which file descriptors have input, something along the lines of...
fd_set readfds;
struct timeval timeout;
unsigned int num_clients;
unsigned int clients[100];
while(1)
{
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
for(i=0; i<num_clients; i++)
{
FD_SET(clients[i],&readfds);
}
timeout.tv_sec=1;
timeout.tv_usec=0;
select(max_fds, &readfds, NULL, NULL, &timeout);
if(FD_ISSET(sock,&readfds))
{
clients[num_clients++] = accept(sock,(struct sockaddr *)&server,&size);
}
for(i=0;i<num_clients;i++)
{
if(FD_ISSET(clients[i],&readfds))
{
int err=read(clients[i],buf,1000);
if(err>0)
{
for(p=0;p<num_clients;p++)
{
write(clients[i],buf,err);
}
}
}
}
}
That code is just an example and needs error checking to see if a client has disconnected, a way of updating the list of clients and seeing if a client is ready to receive data. But hopefully it gives you the basic idea of where to go?
Got some trouble with TCP socket multiplexing.
//socket is non-blocking
const int MAX = 4096;
char *buff[MAX];
char *p = buff;
int fd, rvalue;
rvalue = 0;
if ( (fd = open(path, O_RDONLY)) < 0 ) {
return errno;
} else {
int didsend, didread;
int shouldsend;
while ((didread = read(fd, buff, MAX)) > 0) {
p = buff;
shouldsend = didread;
while ( 1 ) {
didsend = send(sockfd, p, shouldsend, 0);
//if send succeeds and returns the number of bytes fewer than asked for then try to send rest part in next time.
if (didsend < shouldsend) {
p += didsent;
shouldsend -= didsend;
continue;
}
//if there is no place for new data to send, then wait a brief time and try again.
if ( didsend < 0 && (errno == EWOULDBLOCK || errno == EAGAIN) ) {
usleep(1000);
continue;
}
//if all data has been sent then sending loop is over.
if (didsend == shouldsend) {
break;
}
//send error
if ( didsend < 0 ) {
rvalue = errno;
break;
}
}
}
close(fd);
if (didread == -1) {
return errno;
}
return rvalue;
}
Assume I use an I/O Multiplexing function poll() or kqueue(), and non-blocking socket, then if there are only some small data like send a short message, it works fine.
But if it comes to large data, I mean larger than send()'s buffer size, since using non-blocking socket, send() will just send a portion of data, and return how much data it sends, the rest part of data can only be sent in another call of send(), but it takes time, and can't tell how long it will takes. So the second while() is actually a blocking send which using non-blocking socket.
Equivalent to:
//socket is blocking
const int MAX = 4096;
char *buff[MAX];
int fd, n;
if ( (fd = open(path, O_RDONLY)) < 0 ) {
return errno;
} else {
while ((n = read(fd, buff, MAX)) > 0) {
if (send(sockfd, buff, n, 0) < 0) {
return errno;
}
}
close(fd);
return 0;
}
So, what is the solution to this, multithreading might work but that's kind of wasting resource maybe.
This is the general pattern for a single-threaded server that works with multiple connections and non-blocking sockets.
It's primarily pseudo-code in C and doesn't do the necessary error checking. But it gives you an idea that for each accepted connection, you keep a struct instance that maintains the socket handle, request parsing state, response stream, and any other "state" members of that connection. Then you just loop using "select" to wait or having multiple threads doing this same thing.
Again this is only pseudo-code and uses select/poll as an example. You can get even more scalability with epoll.
while (1)
{
fd_set readset = {};
fd_set writeset = {};
for (int i = 0; i < number_of_client_connections; i++)
{
if (client_connections[i].reading_request)
FD_SET(client_connection.sock, &readset);
else
FD_SET(client_connection.sock, &writeset);
}
// add the listen socket to the read set
FD_SET(listen_socket, &readset);
select(n + 1, &readset, &writeset, &timeout); // wait for a socket to be ready (not shown - check for errors and return value)
if (FD_ISSET(listen_socket, &readset))
{
int new_client_socket = accept(listen_socket, &addr, &addrlength);
// create a struct that keeps track of the connection state data
struct ConnectionData client_connection = {};
client_connection.sock = new_client_socket;
client_connection.reading_request = 1; // awaiting for all the request bytes to come in
client_connections[number_of_client_connections++] = client_connection; // pseudo code, add the client_connection to the list
}
for (int i = 0; i < number_of_client_connections; i++)
{
if (client_connections[i].reading_request)
{
if (FD_ISSET(client_connections[i], &readset))
{
char buffer[2000];
int len = recv(client_connections[i].sock, buffer, 2000, 0);
// not shown - handle error case when (recv < 0)
// not shown - handle case when (recv == 0)
ProcessIncomingData(client_connections[i], buffer, len); // do all the request parsing here. Flip the client_connections[i].reading_request to 0 if ready to respond
}
}
else if (client_connections[i].reading_request == 0)
{
if (FD_ISSET(client_connections[i], &writeset))
{
client_connection* conn = &client_connections[i];
int len = send(conn->sock, conn->response_buffer + conn->txCount, conn->response_size - conn->txCount, 0);
conn->txCount += len;
if (conn->txCount == conn->response_size)
{
// done sending response - we can close this connection or change it to back to the reading state
}
}
}
}
I want to make a non-blocking send() and recv() with select() and FD_ISSET(). I run into this behavior in which FD_ISSET() will be true for the first socket, and all other sockets are always not ready. I am confused why that is happening and I am wondering if I am using select() properly.
If I sent 100 requests, eventually one of the socket other than the first will be ready to recv(), but I am not getting that behavior.
for(int i = 0; i < P - 1; i++) {
sockArr[i] = GetSocket(server, port);
if (sockArr[i] < 0) {
// handle error
}
fcntl(sockArr[i], F_SETFL, O_NONBLOCK);
if(sockArr[i] > maxfd) {
maxfd = sockArr[i];
}
}
fd_set sockSet;
for(int i = 0; i < P - 1; i++) {
numBytes = send(sockArr[i], request, requestLen, 0);
if (numBytes < 0 || numBytes != requestLen) {
// handle error
}
// read from other stackoverflow post you need to rest
// per select() call
FD_ZERO(&sockSet);
for(int i = 0; i < P - 1; i++) {
FD_SET(sockArr[i], &sockSet);
}
if(select(maxfd+1, &sockSet, NULL, NULL, NULL)) {
for(int j = 0; j < i; j++) {
if(FD_ISSET(sockArr[j], &sockSet)) { // <------ only true for j = 0
numBytes = recv(sockArr[j], buffer, MAXBUFLEN - 1, 0);
if (numBytes < 0) {
// handle error
}
}
}
}
}
Your first for loop needs to terminate after the send() calls, and you should then start another to handle the selects and receives.
You need to check for recv() returning zero as well as -1, and close the socket and remove it from the FD set in either case.
You also need to loop while j <= P.