I have a non-blocking socket and using select to handle connections from multiple SSL clients :
I have tried suggestions regarding handling SSL_ERROR_WANT_READ, like repeat SSL_read call, or return back to Select, but it seems to be not working in my case.
while (1)
{
start:
if (readyCount = select(FD_SETSIZE, &read_fd_set, NULL, NULL, &timeout) < 0)
{
exit(EXIT_FAILURE);
}
for (int i = 0; i < FD_SETSIZE; ++i)
if (FD_ISSET(i, &read_fd_set))
{
if (i == sockfd)
{
newSocket = accept(sockfd, (struct sockaddr *) &clientname, (socklen_t*)&size);
}
else
{
ssl = SSL_new(ctx);
SSL_set_fd(ssl, i);
accept:
int err = SSL_accept(ssl);
if (0 >= err)
{ // it never goes here
int resultCode = SSL_get_error(ssl, err);
switch (resultCode)
{
case SSL_ERROR_NONE:
goto accept;
break;
case SSL_ERROR_ZERO_RETURN:
SSL_shutdown(ssl);
break;
case SSL_ERROR_SYSCALL:
break;
case SSL_ERROR_WANT_READ:
break;
case SSL_ERROR_WANT_WRITE:
break;
case SSL_ERROR_WANT_CONNECT:
break;
case SSL_ERROR_WANT_ACCEPT:
goto accept;
break;
case SSL_ERROR_WANT_X509_LOOKUP:
break;
case SSL_ERROR_SSL:
break; }
default:
break;
}
}
else
{
Call_Read(...)
}
SSL_free(ssl);
close(i);
FD_CLR(i, &active_fd_set);
}
}
}
Call_Read(...)
{
int fd = SSL_get_fd(ssl);
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
while (data_size > 0)
{
bytes_recv = SSL_read(ssl, data, data_size);
if (bytes_recv <= 0)
{
if (SSL_ERROR_WANT_READ == SSL_get_error(ssl, bytes_recv))
{
testtt = SSL_pending(ssl); //always 0
/*SSL_renegotiate(ssl);
SSL_do_handshake(ssl);*/
continue;
}
if (SSL_ERROR_WANT_WRITE == SSL_get_error(ssl, bytes_recv))
{
testtt = SSL_pending(ssl);
int blocked_on_SSL_ERROR_WANT_WRITE = 1;
continue; }
}
return bytes_recv;
}
data += bytes_recv;
data_size -= bytes_recv;
}
return 1;
}
Client Write:
{
while (data_size > 0)
{
bytes_sent = SSL_write(ssl, data_ptr, data_size);
if (errno == EPIPE) == doesn’t reach this line, debugger stops with EPIPE
{
printf("WRITE socket returned EPIPE”);
return -1;
}
if (bytes_sent < 0)
{
int err = SSL_get_error(ssl, bytes_sent);
switch (err)
{
case SSL_ERROR_WANT_READ:
printf(" SSL_ERROR_WANT_READ\n");
case SSL_ERROR_WANT_WRITE:
printf(" SSL_ERROR_WANT_WRITE\n");
default:
;
}
}
data_ptr += bytes_sent;
data_size -= bytes_sent;
}
return 1;
}
I don’t know how to handle the situation, when the server is in SSL_Read and some of clients calls SSL_Connect.
In such a scenario, the Call_Read will be in infinite loop for SSL_ERROR_WANT_READ. If I replace the “continue” statement with a flag that will finish the loop and the caller returns back to Select the client will break with broken pipe error, and it even doesn’t reach the line “WRITE socket returned EPIPE”. The same will happen if I return from the read loop back to Accept, instead of to Select.
I am missing here something and I am wondering if I should do some changes to the client also.
Any suggestions?
Related
I'm making an application to test TCP connections and study network protocols. The same application is Client or Server depending the user parameters.
I'm developing it on Windows 10 using Dev-Cpp 5.11.
But, when I try to run it like a server I get the following error:
When it call accept():
accept() returns SOCK_ERROR.
WSAGetLastError() return Bad Address (until listen() it returns NULL)
The application returns immediately, there is no client connection request or delay waiting connection request.
The output is:
:: SERVER MODE
:: PORT: 2020
:: Time Limit receiving: 1000ms
:: WSA Version: 0202
:: Criando SOCKET: OK
:: SOCKET Binding: OK
:: LISTENING . . .
WSA Error: Bad address
Socket Value: -1
ERRO 0 [Bad address]
--------------------------------
Process exited after 0.1634 seconds with return value 0
Pressione qualquer tecla para continuar. . .
And here is the code called with the parameters:
csock -listen 2020
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <winsock2.h>
void delay(int milisecond);
char* LastErrorDescription();
/*****************************************************************************************
EXEMPLO DE PARAMETRO:
csock -h IP_v_4_Address -p Remote_Port -t 500
csock -listen 2020
******************************************************************************************/
/**************************************************
PENDENCIAS:
- IMPLEMENTAR IPv6
- IMPLEMENTAR MODO SERVIDOR
- REFAZER PARAMETROS PACKAGE (-pack)
***************************************************/
int main(int argc, char *argv[]) {
struct sockaddr_in sckAddr, adrIncomming;
char request[512] = ""; //= "GET http://www.brasil.gov.br HTTP/1.1\r\n\r\n"; //"GET /index.html HTTP/1.1\nHost: www.brasil.gov.br\n";
char BufferIn[10240];
WSADATA wsaDADOS;
int retWSA;
int retCon;
int retSend;
int retRecv;
int retBind;
int i;
int imsg;
int time_limit = 1000; // milisegundos aguardando recebimento de pacotes
clock_t registrador_tempo;
_Bool closed_connection = 0;
_Bool serverMode = 0;
_Bool nonpersist = 0;
_Bool bIPv6 = 0;
SOCKET wsocket;
SOCKET wserver;
WORD sckVersion = (2) << 8 | (2); // sckVersion 2.2 -> 0000001000000010
//printf("Quantidade de Parametros: %i", argc);
for(i = 0; i < argc; i ++){
if(!strcmp(argv[i], "-h")){
printf("\n:: HOST: %s", argv[i + 1]);
sckAddr.sin_addr.s_addr = inet_addr(argv[i + 1]);
}
if(!strcmp(argv[i], "-p")){
printf("\n:: PORT: %s", argv[i + 1]);
sckAddr.sin_port = htons(atoi(argv[i + 1]));
}
if(!strcmp(argv[i], "-t")){
time_limit = atoi(argv[i + 1]);
}
//COMANDOS MODO SERVIDOR
else if(!strcmp(argv[i], "-listen")){
serverMode = 1;
printf("\n:: SERVER MODE");
sckAddr.sin_addr.s_addr = htonl(INADDR_ANY);
sckAddr.sin_port = htons(atoi(argv[i + 1]));
printf("\n:: PORT: %s", argv[i + 1]);
}
}
printf("\n:: Time Limit receiving: %ims", time_limit);
// TRABALHO COM ENDEREÇO
sckAddr.sin_family = AF_INET; //23 == AF_INET6 para usar IPv6
//sckAddr.sin_port = htons(80);
//sckAddr.sin_addr.s_addr = inet_addr("170.246.252.243");
//Inicialização WSA Socket Application
printf("\n\n:: WSA Version: ");
retWSA = WSAStartup(sckVersion, &wsaDADOS);
if (retWSA == 0) {
printf("%04X", wsaDADOS.wVersion);
//Cria o SOCKET
printf("\n:: Criando SOCKET: ");
if(serverMode){
wserver = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(wserver > 0){
printf("OK");
retBind = bind(wserver, (struct sockaddr*)&sckAddr, sizeof(sckAddr));
printf("\n:: SOCKET Binding: ");
if (retBind == 0)
printf("OK");
else
printf("ERRO %i [%s]", WSAGetLastError(), LastErrorDescription());
}
else
printf("ERRO %i [%s]", WSAGetLastError(), LastErrorDescription());
}
else { //CLIENT MODE
wsocket = socket(bIPv6 ? 23 : AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(wsocket > 0)
printf("OK");
else
printf("ERRO %i [%s]", WSAGetLastError(), LastErrorDescription());
}
//MODO SERVIDOR
if(serverMode){
printf("\n\n:: LISTENING . . .\n");
listen(wserver, 2);
//do {
wsocket = accept(wserver, (struct sockaddr*)&adrIncomming, (int*)sizeof(adrIncomming));
printf("WSA Error: %s\nSocket Value: %i\n\n", LastErrorDescription(), wsocket); //TO DEPURATE
//} while(wsocket == SOCKET_ERROR);
closesocket(wserver);
if(wsocket != SOCKET_ERROR){
printf("\n:: Conexao estabelecida com: %s", inet_ntoa(adrIncomming.sin_addr)); //Tentar obter ENDEREÇO REMOTO com getpeername
retCon = 0;
}
else
retCon = -1;
}
//MODO CLIENTE
else{
//Conecta
printf("\n:: Conectando: ");
retCon = connect(wsocket, (struct sockaddr *)&sckAddr, sizeof(sckAddr));
if(retCon == 0)
printf("OK");
else
printf("ERRO %i [%s]", WSAGetLastError(), LastErrorDescription());
}
//CONEXAO ESTABELECIDA
if (retCon == 0) {
// ENVIA E RECEBE
while(strcmp(&request[0], "exIt")){
//ENVIA PACOTE
if (!nonpersist){
printf("\n\n:: ENVIAR>");
for(i=0;i<sizeof(request);i++)
request[i] = '\0';
fgets(&request, sizeof(request), stdin);
// Trata os PACOTES a serem ENVIADOS
for (i = 0; i < sizeof(request); i++){
if(request[i] == '\0'){
// Finaliza STRING 1 posicao antes ELIMINANDO Lf do fgets()
for(imsg = i - 1; imsg < sizeof(request); imsg++)
request[imsg] = '\0';
break;
}
// \n
else if (request[i] == '\\' && request[i + 1] == 'n'){
request[i] = '\n';
// Traz todos os caracteres para 1 posicao antes
for (imsg = i + 1; imsg < sizeof(request); imsg++)
request[imsg] = request[imsg + 1];
}
// \r
else if (request[i] == '\\' && request[i + 1] == 'r'){
request[i] = '\r';
// Traz todos os caracteres para 1 posicao antes
for (imsg = i + 1; imsg < sizeof(request); imsg++)
request[imsg] = request[imsg + 1];
}
}
}
//Depuracao: checar string de pacotes e ASCII da string de pacotes
/*
printf("\n\n%s.", &request);
printf("\n\n.");
for(i=0;i<sizeof(request);i++)
printf("%i ", request[i]);
printf(".");
*/
//Envia pacotes
if(strcmp(&request, "exIt")){
printf("\n:: Enviando pacotes: ");
if(send(wsocket, request, sizeof(request), 0) == SOCKET_ERROR){ // Erro no ENVIO
printf("ERRO %i [%s]", WSAGetLastError(), LastErrorDescription());
}
else //PACOTES ENVIADOS COM EXITO
printf("OK");
delay(time_limit);
//RECEBE PACOTE
retRecv = recv(wsocket, BufferIn, sizeof(BufferIn), 0);
if (retRecv == 0) {
printf("\n\n:: %s", LastErrorDescription());
break;
}
else {
printf("\n\n:: RECEBIDO:\n");
printf("%s", BufferIn);
}
}
//Finaliza se for conexao nao persistente
if(nonpersist)
strcpy(&request, "exIt");
}
}
else { //Erro na CONEXÃO
printf("ERRO %i [%s]", WSAGetLastError(), LastErrorDescription());
}
//Fecha o SOCKET
closesocket(wsocket);
}
else { // Erro no WSAStartup
printf("ERRO %i [%s]", WSAGetLastError(), LastErrorDescription());
}
// Finaliza Utilização do SOCKET
WSACleanup();
return 0;
}
void delay(int milisecond){
clock_t registra_tempo = clock();
while(clock() < registra_tempo + milisecond)
;
}
char* LastErrorDescription(){
static char* ret;
switch(WSAGetLastError()){
case WSAEINTR:{
ret = "Interrupted function call";
break;
}
case WSAEBADF:{
ret = "WSAEBADF";
break;
}
case WSAEACCES:{
ret = "WSAEACCES";
break;
}
case WSAEFAULT:{
ret = "Bad address";
break;
}
case WSAEINVAL:{
ret = "Invalid argument";
break;
}
case WSAEMFILE:{
ret = "Too many open files";
break;
}
case WSAEWOULDBLOCK:{
ret = "Operation would block";
break;
}
case WSAEINPROGRESS:{
ret = "Operation now in progress";
break;
}
case WSAEALREADY:{
ret = "Operation already in progress";
break;
}
case WSAENOTSOCK:{
ret = "Socket operation on non-socket";
break;
}
case WSAEDESTADDRREQ:{
ret = "Destination address required";
break;
}
case WSAEMSGSIZE:{
ret = "Message too long";
break;
}
case WSAEPROTOTYPE:{
ret = "Protocol wrong type for socket";
break;
}
case WSAENOPROTOOPT:{
ret = "Bad protocol option";
break;
}
case WSAEPROTONOSUPPORT:{
ret = "Protocol not supported";
break;
}
case WSAESOCKTNOSUPPORT:{
ret = "Socket type not supported";
break;
}
case WSAEOPNOTSUPP:{
ret = "Operation not supported";
break;
}
case WSAEPFNOSUPPORT:{
ret = "Protocol family not supported";
break;
}
case WSAEAFNOSUPPORT:{
ret = "Address family not supported by protocol family";
break;
}
case WSAEADDRINUSE:{
ret = "Address already in use";
break;
}
case WSAEADDRNOTAVAIL:{
ret = "Cannot assign requested address";
break;
}
case WSAENETDOWN:{
ret = "Network is down";
break;
}
case WSAENETUNREACH:{
ret = "Network is unreachable";
break;
}
case WSAENETRESET:{
ret = "Network dropped connection on reset";
break;
}
case WSAECONNABORTED:{
ret = "Software caused connection abort";
break;
}
case WSAECONNRESET:{
ret = "Connection reset by peer";
break;
}
case WSAENOBUFS:{
ret = "No buffer space available";
break;
}
case WSAEISCONN:{
ret = "Socket is already connected";
break;
}
case WSAENOTCONN:{
ret = "Socket is not connected";
break;
}
case WSAESHUTDOWN:{
ret = "Cannot send after socket shutdown";
break;
}
case WSAETOOMANYREFS:{
ret = "WSAETOOMANYREFS";
break;
}
case WSAETIMEDOUT:{
ret = "Connection timed out";
break;
}
case WSAECONNREFUSED:{
ret = "Connection refused";
break;
}
case WSAELOOP:{
ret = "WSAELOOP";
break;
}
case WSAENAMETOOLONG:{
ret = "WSAENAMETOOLONG";
break;
}
case WSAEHOSTDOWN:{
ret = "Host is down";
break;
}
case WSAEHOSTUNREACH:{
ret = "No route to host";
break;
}
case WSAENOTEMPTY:{
ret = "WSAENOTEMPTY";
break;
}
case WSAEPROCLIM:{
ret = "Too many processes";
break;
}
case WSAEUSERS:{
ret = "WSAEUSERS";
break;
}
case WSAEDQUOT:{
ret = "WSAEDQUOT";
break;
}
case WSAESTALE:{
ret = "WSAESTALE";
break;
}
case WSAEREMOTE:{
ret = "WSAEREMOTE";
break;
}
case WSASYSNOTREADY:{
ret = "Network subsystem is unavailable";
break;
}
case WSAVERNOTSUPPORTED:{
ret = "WINSOCK.DLL version out of range";
break;
}
case WSANOTINITIALISED:{
ret = "Successful WSAStartup() not yet performed";
break;
}
case WSAEDISCON:{
ret = "WSAEDISCON";
break;
}
case WSAENOMORE:{
ret = "WSAENOMORE";
break;
}
case WSAECANCELLED:{
ret = "WSAECANCELLED";
break;
}
case WSAEINVALIDPROCTABLE:{
ret = "WSAEINVALIDPROCTABLE";
break;
}
case WSAEINVALIDPROVIDER:{
ret = "WSAEINVALIDPROVIDER";
break;
}
case WSAEPROVIDERFAILEDINIT:{
ret = "WSAEPROVIDERFAILEDINIT";
break;
}
case WSASYSCALLFAILURE:{
ret = "WSASYSCALLFAILURE";
break;
}
case WSASERVICE_NOT_FOUND:{
ret = "WSASERVICE_NOT_FOUND";
break;
}
case WSATYPE_NOT_FOUND:{
ret = "WSATYPE_NOT_FOUND";
break;
}
case WSA_E_NO_MORE:{
ret = "WSA_E_NO_MORE";
break;
}
case WSA_E_CANCELLED:{
ret = "WSA_E_CANCELLED";
break;
}
case WSAEREFUSED:{
ret = "WSAEREFUSED";
break;
}
case WSAHOST_NOT_FOUND:{
ret = "Host not found";
break;
}
case WSATRY_AGAIN:{
ret = "Non-authoritative host not found";
break;
}
case WSANO_RECOVERY:{
ret = "This is a non-recoverable error";
break;
}
case WSANO_DATA:{
ret = "Valid name, no data record of requested type";
break;
}
}
return ret;
}
The function accept third parameter addrlen is a pointer. It must point to a valid memory location. Additionally it's an in/out pointer, i.e. a variable. So you must create a variable with a value the is the size of your adrIncomming variable. After return from accept the variable will have the actual length of the address.
This code is more appropriate.
int adrLength = sizeof(adrIncomming);
wsocket = accept(wserver, (struct sockaddr*)&adrIncomming, &adrLength);
Edit: The link in this answer and the example is specific for the WinSock API since the question is tagged with Windows. Other implementations of a socket API might have different function signatures.
I just started learning C language and I'm trying to get deal with Windows Sockets.
The problem is the server can accept and send message only once.
I used debug mode and saw that work stops in select() from the server part. It seems fine in client (but I'm not sure) and don't see the problem in my code. But I have such a result. What's wrong?
I noticed that my tv.tv_sec isn't defined and I did that just before select, nothing was changed.
And just to be sure: as I need to receive and send message, I don't need write descriptor in accept(), right?
Client uses CreateThread function where I try to send message. Send is in while(1) cycle in main()
Server part:
int main(int argc, char* argv[])
{
/* definitions, WSAStartup(), socket(), bind(), listen()
Listening socket is a returned value of listen() function*/
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
while (1)
{
// SELECT (LISTENING SOCKET)
FD_ZERO(&readSet);
FD_SET(listeningSocket, &readSet);
tv.tv_sec = 5;
printf("Listening: Read FD: %d; Write FD : %d;\n", FD_ISSET(listeningSocket, &readSet), FD_ISSET(listeningSocket, &writeSet));
if ((retVal = select(listeningSocket + 1, &readSet, NULL, NULL, 0)) == SOCKET_ERROR)
{
printf("Select error ");
break;
}
else if (retVal == 0)
{
printf(". . .\n");
continue;
}
else
{
// READ SD
if ((FD_ISSET(listeningSocket, &readSet)) != SOCKET_ERROR)
{
if ((newSocketDescriptor = accept(listeningSocket, (struct sockaddr *)&clientAddr, &clientAddrSize)) == SOCKET_ERROR)
{
printf("Accept error ");
break;
}
FD_ZERO(&readSet);
FD_SET(newSocketDescriptor, &readSet);
HOSTENT *hst = gethostbyaddr((const char *)&serverAddr.sin_addr.s_addr, 4, AF_INET);
printf("Welcome %s (%s:%d) new connected\n", hst->h_name, inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
printf("Read FD: %d; Write FD : %d;\n", FD_ISSET(newSocketDescriptor, &readSet), FD_ISSET(newSocketDescriptor, &writeSet));
// READ
if (FD_ISSET(newSocketDescriptor, &readSet) != 0)
{
if ((numBytes = recv(newSocketDescriptor, &bufferData[0], sizeof(bufferData), 0)) == SOCKET_ERROR)
{
printf("Recv failed \n");
freeSocketInformation(newSocketDescriptor);
break;
}
bufferData[numBytes] = '\0';
printf("Client -> Server: %s\n", &bufferData[0]);
}
// WRITE
FD_ZERO(&writeSet);
FD_SET(newSocketDescriptor, &writeSet);
printf("Read FD: %d; Write FD : %d;\n", FD_ISSET(newSocketDescriptor, &readSet), FD_ISSET(newSocketDescriptor, &writeSet));
if (FD_ISSET(newSocketDescriptor, &writeSet) != 0)
{
//fgets(&bufferData[0], sizeof(bufferData), stdin);
if (send(newSocketDescriptor, &bufferData[0], strlen(&bufferData[0]), 0) == SOCKET_ERROR)
{
printf("Send error ");
freeSocketInformation(newSocketDescriptor);
break;
}
bufferData[numBytes] = '\0';
printf("Server -> Client: %s\n", &bufferData[0]);
}
printf("Read FD: %d; Write FD : %d;\n", FD_ISSET(newSocketDescriptor, &readSet), FD_ISSET(newSocketDescriptor, &writeSet));
FD_SET(newSocketDescriptor, &readSet);
}
}
}
//FD_CLR(listeningSocket, &readSet);
closesocket(newSocketDescriptor);
} while (FALSE);
printf("- Error code: %d\n", WSAGetLastError());
closesocket(listeningSocket);
WSACleanup();
return 0;
}
Client part (it uses CreateThread function which is in the end of the code):
/* definitions, socket(), connect()*/
if (ioctlsocket(socketDescriptor, FIONBIO, (unsigned long *)&nb) != 0)
{
printf("ioctlsocket error ");
break;
}
FD_ZERO(&writeSet);
FD_SET(socketDescriptor, &writeSet);
if ((retVal = select(socketDescriptor + 1, NULL, &writeSet, NULL, &tv)) == SOCKET_ERROR)
{
printf("Send non-blocking error ");
break;
}
else if (retVal == 0)
{
printf("Non-blocking connect time limit is expired");
break;
}
}
printf("Connection with %s\n", SERVERADDR);
DWORD thID;
printf("Socket Desciptor: %d\n", socketDescriptor);
HANDLE hThread = CreateThread(NULL, NULL, HandleReadThread, (LPVOID)socketDescriptor, NULL, &thID);
printf("Thread ID: %d\n", thID);
while (1)
{
// WRITE
printf("Client -> Server: ");
fgets(&bufferData[0], sizeof(bufferData), stdin);
FD_ZERO(&writeSet);
FD_SET(socketDescriptor, &writeSet);
tv.tv_sec = 5;
if ((retVal = select(socketDescriptor + 1, NULL, &writeSet, NULL, &tv)) == SOCKET_ERROR)
{
printf("Send non-blocking error ");
break;
}
if (FD_ISSET(socketDescriptor, &writeSet) != 0)
{
if (send(socketDescriptor, bufferData, strlen(&bufferData[0]), 0) == SOCKET_ERROR)
{
printf("Send error ");
break;
}
}
}
} while (FALSE);
printf("- Error code: %d\n", WSAGetLastError());
closesocket(socketDescriptor);
WSACleanup();
return 0;
}
DWORD WINAPI HandleReadThread(LPVOID serverSocket)
{
SOCKET socketDescriptor;
socketDescriptor = (SOCKET)serverSocket;
char bufferData[MAXDATASIZE] = { 0 };
int retVal;
fd_set readSet;
timeval tv = { 0 };
tv.tv_sec = 5;
int numBytes;
int nclients = 0;
while (1)
{
FD_ZERO(&readSet);
FD_SET(socketDescriptor, &readSet);
if ((retVal = select(socketDescriptor + 1, &readSet, NULL, NULL, &tv)) == SOCKET_ERROR)
{
printf("Select error. Error code: %d", WSAGetLastError());
break;
}
else if (retVal == 0)
{
//printf(". . .\n");
continue;
}
else
{
//FD_ZERO(socketDescriptor, &readSet);
//FD_SET(socketDescriptor, &readSet);
// READ
if (FD_ISSET(socketDescriptor, &readSet) != 0)
{
if ((numBytes = recv(socketDescriptor, &bufferData[0], sizeof(bufferData), 0)) == SOCKET_ERROR)
{
printf("Recv error in Thread. Error code: %d\n", WSAGetLastError());
break;
}
printf("\nSocket Desciptor: %d\n", socketDescriptor);
bufferData[numBytes] = '\0';
printf("Server -> Client: %s\n", &bufferData[0]);
}
}
}
closesocket(socketDescriptor);
return 0;
}
Well, I solved it by myself. The condition
if ((FD_ISSET(listeningSocket, &readSet)) != 0)
worked for all while(1) cycle, it should has been finished after accept() function.
Also I added these lines in the beginning of while(1) after FD_SET for readSet:
if (newSocketDescriptor)
{
FD_SET(newSocketDescriptor, &readSet);
}
The chat started working but there are a lot of stuffs to work at :)
After sending "wrong" username - client won't start loop from beginning, actually, there is no server asks:?
Dunno how to handle 3 way client-server message sender for such auth. I must understand this to continue such message receiving in further.
client.c:
int is_authenticated = 0;
size_t sendline_s;
while (!is_authenticated) {
recv(sockfd, recvline, MAXLINE, 0);
printf("%s", "server asks:");
fputs(recvline, stdout);
printf("?> ");
fflush(stdout);
while (fgets(sendline, MAXLINE, stdin) != NULL) {
sendline_s = strlen(sendline);
if (sendline[sendline_s-1] == '\n') {
sendline[sendline_s-1] = '\0';
send(sockfd, sendline, sendline_s+1, 0);
puts("username sended");
break;
}
// handling ^Z (EOF) here
//
}
recv(sockfd, recvline, MAXLINE, 0);
printf("\nawaiting for server ACK\n");
puts(recvline);
if (strcmp(recvline, "ACCEPTED_AUTH") == 0) {
puts("authentication complete successful");
is_authenticated = 1;
}
else {
puts("authentication declined");
}
}
server.c
int is_authenticated = 0;
char *accepted = "ACCEPTED_AUTH";
char *name = "kaldown";
char *wrong = "wrong";
size_t name_s = strlen(name);
size_t accepted_s = strlen(accepted);
size_t wrong_s = strlen(wrong);
while (!is_authenticated) {
send(connfd, name, name_s+1, 0);
puts("authentication request was send");
recv(connfd, buf, MAXLINE, 0);
printf("username was recieved: ");
puts(buf);
if (strcmp(buf, name) == 0) {
puts("hurray");
send(connfd, accepted, accepted_s+1, 0);
is_authenticated = 1;
//break;
}
else {
puts("WRONG NAME");
send(connfd, wrong, wrong_s+1, 0);
}
}
But, If i send right username - it passes the block and everything goes well.
server:
while (!is_authenticated) {
if ((n = recv(connfd, buf, MAXLINE-1,0))== -1) {
perror("recv");
exit(1);
}
else if (n == 0) {
printf("Connection closed\n");
//So I can now wait for another client
break;
}
printf("\n%d truely recieved", n);
buf[n] = '\0';
printf("Server:Msg Received %s\n", buf);
if (strcmp(buf, "DONE") == 0) {
strcpy(buf, "DONE");
if ((send(connfd,buf, strlen(buf),0))== -1)
{
fprintf(stderr, "Failure Sending Message\n");
close(connfd);
break;
}
puts("KONEC");
is_authenticated = 1;
}
else {
if ((send(connfd,buf, strlen(buf),0))== -1)
{
fprintf(stderr, "Failure Sending Message\n");
close(connfd);
break;
}
}
printf("Server:Msg being sent: %s\nNumber of bytes sent: %d\n", buf, strlen(buf));
}
client:
while (!is_authenticated) {
printf("Client: Enter Data for Server:\n");
if (fgets(sendline, MAXLINE-1, stdin) != NULL) {
if (sendline[(strlen(sendline)-1)] == '\n') {
sendline[strlen(sendline)-1] = '\0';
if ((send(sockfd,sendline, strlen(sendline),0))== -1) {
fprintf(stderr, "Failure Sending Message\n");
close(sockfd);
exit(1);
}
else {
printf("Client:Message being sent: %s\n",sendline);
n = recv(sockfd, sendline, sizeof(sendline),0);
if ( n <= 0 )
{
printf("Either Connection Closed or Error\n");
//Break from the While
break;
}
sendline[n] = '\0';
if (strcmp(sendline, "DONE") == 0) {
puts("AUTH PASSED");
is_authenticated = 1;
}
printf("Client:Message Received From Server - %s\n",sendline);
}
}
//EOF
}
}
I have this server app where clients can connect to. Now i want that when the clients are connected i can send data to all of them. I manage to do it when i connect my two clients.. what i send is received by my two clients. but my problem is when i connect client1 then send data to server, client1 can receive data then i connect client2 and i send data to the server. now when i send data from server to my clients only client1 can receive data but when i disconnect client 1 then client2 can receive data from the server.
How can i make them work at the same time?.. Also how can i make my server accept messages at the same time from my client?..
Here's the part of the code im having trouble.
for(j=0;j<MAX_CLIENTS; j++)
Clients[j].connected_sock = -1;
do
{
fduse = fdin;
printf("Waiting for Connection\n");
err = select(sMax + 1, &fduse, NULL, NULL, NULL);
if (err < 0)
{
perror(" select() failed");
break;
}
DescRead = err;
for (SockStorage=0; SockStorage <= sMax && DescRead > 0; ++SockStorage)
{
if (FD_ISSET(SockStorage, &fduse))
{
DescRead -= 1;
if (SockStorage == socketFd)
{
printf(" Listening socket is readable\n");
do
{
NewSFD = accept(socketFd,(struct sockaddr *) &cli_addr, &clilen);
if (NewSFD < 0)
{
if (errno != EWOULDBLOCK)
{
perror(" accept() failed");
DCSERVER = TRUE;
}
break;
}
if(ClientCount < MAX_CLIENTS){
for(loop = 0; loop <MAX_CLIENTS; loop++){
if(Clients[loop].connected_sock<0){
Clients[loop].connected_sock = NewSFD;
break;
}
}
ClientCount++;
}
else
{
printf("Maximum Client Reached.\n");
char *sendtoclient = "Server full. ";
send(NewSFD, sendtoclient, strlen(sendtoclient),0);
close(NewSFD);
break;
}
ip = ntohl(cli_addr.sin_addr.s_addr);
printf(" Connection from %d.%d.%d.%d\n",
(int)(ip>>24)&0xff,
(int)(ip>>16)&0xff,
(int)(ip>>8)&0xff,
(int)(ip>>0)&0xff);
dlogs(ip);
FD_SET(NewSFD, &fdin);
if (NewSFD > sMax)
sMax = NewSFD;
} while (NewSFD != -1);
}
else
{
int d;
for(d=0; d<MAX_CLIENTS; d++){
printf("Descriptor ID: %d\n", Clients[d].connected_sock);
}
pfds[0].fd = fd;
pfds[0].events = POLLIN;
pfds[1].fd = SockStorage;
pfds[1].events = POLLIN;
state = FALSE;
do
{
rc = poll(pfds, 2, -1);
if (pfds[0].revents & POLLIN)
{
while ((nbytes = read(fd, buf, sizeof(buf)-1)) > 0)
{
buf[nbytes] = '\0';
printf("%s\n", buf);
}
pfds[0].events = 0;
pfds[1].events = POLLIN | POLLOUT;
}
if (pfds[1].revents & POLLIN)
{
err = recv(SockStorage, strbuf, sizeof(strbuf), 0);
if (err < 0)
{
if (errno != EWOULDBLOCK)
{
perror(" recv() failed");
state = TRUE;
}
break;
}
if (err == 0)
{
printf(" Connection closed\n");
state = TRUE;
break;
}
dSize = err;
printf(" %d bytes received\n", dSize);
}
if (pfds[1].revents & POLLOUT)
{
int s;
for(s=0; s<MAX_CLIENTS; s++){
if(Clients[s].connected_sock>0){
err = send(Clients[s].connected_sock, buf, strlen(buf), 0);
if (err < 0)
{
perror(" send() failed");
state = TRUE;
break;
}
}
}
pfds[0].events = POLLIN;
pfds[1].events = POLLIN;
}
} while (TRUE);
Here's how im sending data to my clients.
int s;
for(s=0; s<MAX_CLIENTS; s++){
if(Clients[s].connected_sock>0){
err = send(Clients[s].connected_sock, buf, strlen(buf), 0);
if (err < 0)
{
perror(" send() failed");
state = TRUE;
break;
}
}
}
Thanks,
There are generally couple of possible solutions:
You can use separate threads for each client connection;
You can use select;
You can use poll;
You can use epoll;
In Windows environment there are also some other possibilities like IOCP for example.
Each of mentioned solutions have some pros and cons (you need generally distinguish between simplicity and performance).
You can read some network programming tutorial for more details, e.g. this.
I'm connected to certain client then we're communicating. Another client connects to my server which will instantly output that there's a new connection.. e.g Accepting while listening to other clients?.. How can i do that?..
I have already a working one but it only does one thing at a time. When i'm already communicating with my client i can't accept incoming connection or data to other clients. I can only accept new connection or data from other clients when the client i'm communicating is disconnected. how can i do so that it can process both accept and listen to client at the same time. I don't want to use threads.
Here's a part of my code.
do
{
fduse = fdin;
printf("Waiting for Connection\n");
err = select(sMax + 1, &fduse, NULL, NULL, NULL);
if (err < 0)
{
perror(" select() failed");
break;
}
DescRead = err;
for (SockStorage = 0; SockStorage <= sMax && DescRead > 0; ++SockStorage)
{
if (FD_ISSET(SockStorage, &fduse))
{
DescRead -= 1;
if (SockStorage == socketFd)
{
printf(" Listening socket is readable\n");
do
{
NewSFD =
accept(socketFd, (struct sockaddr *)&cli_addr, &clilen);
if (NewSFD < 0)
{
if (errno != EWOULDBLOCK)
{
perror(" accept() failed");
DCSERVER = TRUE;
}
break;
}
if (ClientCount < MAX_CLIENTS)
{
for (loop = 0; loop < MAX_CLIENTS; loop++)
{
if (Clients[loop].connected_sock < 0)
{
Clients[loop].connected_sock = NewSFD;
break;
}
}
ClientCount++;
}
else
{
printf("Maximum Client Reached.\n");
char *sendtoclient = "Server full. ";
send(NewSFD, sendtoclient, strlen(sendtoclient), 0);
close(NewSFD);
break;
}
ip = ntohl(cli_addr.sin_addr.s_addr);
printf(" Connection from %d.%d.%d.%d\n",
(int)(ip >> 24) & 0xff,
(int)(ip >> 16) & 0xff,
(int)(ip >> 8) & 0xff, (int)(ip >> 0) & 0xff);
dlogs(ip);
FD_SET(NewSFD, &fdin);
if (NewSFD > sMax)
sMax = NewSFD;
}
while (NewSFD != -1);
}
else
{
int d;
for (d = 0; d < MAX_CLIENTS; d++)
{
printf("Descriptor ID: %d\n", Clients[d].connected_sock);
}
pfds[0].fd = fd;
pfds[0].events = POLLIN;
pfds[1].fd = SockStorage;
pfds[1].events = POLLIN;
state = FALSE;
do
{
rc = poll(pfds, 2, -1);
if (pfds[0].revents & POLLIN)
{
while ((nbytes = read(fd, buf, sizeof (buf) - 1)) > 0)
{
buf[nbytes] = '\0';
printf("%s\n", buf);
}
pfds[0].events = 0;
pfds[1].events = POLLIN | POLLOUT;
}
if (pfds[1].revents & POLLIN)
{
err = recv(SockStorage, strbuf, sizeof (strbuf), 0);
if (err < 0)
{
if (errno != EWOULDBLOCK)
{
perror(" recv() failed");
state = TRUE;
}
break;
}
if (err == 0)
{
printf(" Connection closed\n");
state = TRUE;
break;
}
dSize = err;
printf(" %d bytes received\n", dSize);
}
if (pfds[1].revents & POLLOUT)
{
int s;
for (s = 0; s < MAX_CLIENTS; s++)
{
if (Clients[s].connected_sock > 0)
{
err =
send(Clients[s].connected_sock, buf,
strlen(buf), 0);
if (err < 0)
{
perror(" send() failed");
track = s;
state = TRUE;
break;
}
}
}
pfds[0].events = POLLIN;
pfds[1].events = POLLIN;
}
}
while (TRUE);
fopen("/sockF.txt", "w");
if (state)
{
ClientCount--;
close(SockStorage);
FD_CLR(SockStorage, &fdin);
if (SockStorage == sMax)
{
while (FD_ISSET(sMax, &fdin) == FALSE)
sMax -= 1;
}
}
}
}
}
} while (DCSERVER == FALSE);
cleanUP(SockStorage, sMax);
}
I've been working on it for two days and still i can't get it. thanks..
Well, to give you an example of a working multi-connection daemon, have a look at this example
If you need a further explanation to go with it, shoot.
Note, the code in that example has been stripped of things like daemon_init(), etc, so just copy-pasting blindly and trying to compile it won't work.
Added setnonblocking() as per request.
void setnonblocking(sock)
int sock;
{
int opts;
opts = fcntl(sock, F_GETFL);
if (opts < 0)
{
syslog(LOG_ERR, "fcntl(F_GETFL) failed");
exit(EXIT_FAILURE);
}
opts = (opts | O_NONBLOCK);
if (fcntl(sock, F_SETFL, opts) < 0)
{
syslog(LOG_ERR, "fcntl(F_SETFL) failed");
exit(EXIT_FAILURE);
}
return;
}
And daemon_init()
int daemon_init(void)
{
pid_t pid;
int i;
if ((pid = fork()) < 0)
{
return -1;
}
else if (pid != 0)
{
exit(0);
}
for (i=getdtablesize();i>=0;--i) close(i);
i = open("/dev/null", O_RDWR); /* open stdin */
dup(i); /* stdout */
dup(i); /* stderr */
setsid();
return 0;
}