Kabanson Kabanson - 4 months ago 12
Linux Question

C Server/Client with Sockets

I try to write a simple server/client in C to send messages over sockets.
It must run under Linux and Windows with MinGW. I found many examples for Linux but way to many arent working with Windows.
It would be really nice if you would help me.

For the server I have something I dont understand.

What I'm doing on server-side?




  1. Need to initialise WSA on Windows, nothing on Linux.

  2. Create a socket for the server on server-side.

  3. Create struct sockaddr_in for the server.

  4. Bind the socket on ANY-IP.

  5. Listen on the socket.

  6. Accept connections, handle connection with the newSocket.

  7. Close the new Socket repeat with 6).



It is only working correctly if I dont close the newSocket, but why? (EBADF error)

Code Update after Edit2:

// IMPORTANT: On linker errors try -lws2_32 as Linkerparameter
#ifdef __WIN32__
# include <winsock2.h> // used for sockets with windows, needs startup / shutdown
# include <ws2tcpip.h> // for MinGW / socklen_t
# define INIT_SOCKET_SYSTEM WSADATA wsaData;if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {printf ("Error initialising WSA.\n");exit(6);}
# define CLEAR_SOCKET_SYSTEM WSACleanup();
# include <windows.h> // for Sleep
# define SLEEP Sleep(10); // sleeping 10ms
# define CLOSE_SOCKET_FUNCTION closesocket
#else
# include <sys/socket.h>
# define INIT_SOCKET_SYSTEM printf("Linux dont need a special init for sockets, so all fine.\n");
# define CLEAR_SOCKET_SYSTEM printf("Linux dont need a special clear for sockets, so all fine.\n");
# include <time.h>
# define SLEEP sleep(1); // sleeping a second :-/
# define CLOSE_SOCKET_FUNCTION close
#endif
#include <stdio.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <strings.h> // used for bzero
//used in the tutorial but not necessary!?
//#include <sys/types.h>
//#include <unistd.h>
//#include <stdlib.h>


/* could be still useful
// polling 10ms...
//#include <time.h>
//#define SLEEP time_t tStart, tEnd;time(&tStart);do {time(&tEnd);} while (difftime(tEnd, tStart) < 0.01);
*/
/* Random Sources
* http://pubs.opengroup.org/onlinepubs/9699919799/
* http://linux.die.net/man/2
* http://stackoverflow.com/questions/31765278/simple-webserver-wont-work
* http://blog.stephencleary.com/2009/05/using-socket-as-server-listening-socket.html
*
* http://cs.baylor.edu/~donahoo/practical/CSockets/WindowsSockets.pdf
*/

// functions
void createListenSocket(int * retListenSocket, const int port, bool * isRunning);
void listenFor(int * listenSocket, bool * isRunning);
void acceptFor(int * listenSocket, socklen_t * addrlen, bool * isRunning);
void handleConnection(int * inSocket, struct sockaddr_in * addClient);

// http://www.gnu.org/software/libc/manual/html_node/Cleanups-on-Exit.html
int * cleanSocket;
void cleanUp() {
CLOSE_SOCKET_FUNCTION(* cleanSocket);
CLEAR_SOCKET_SYSTEM
}


//[todo] WSAGetLastError handling for windows
int main(int argc, char ** argv) {
atexit(cleanUp);
bool isRunning = true;
socklen_t addressLen = sizeof(struct sockaddr_in);

// create listening socket
const int port = 15000;
int listenSocket;
cleanSocket = &listenSocket;

createListenSocket(&listenSocket, port, &isRunning);
listenFor(&listenSocket, &isRunning);
while (isRunning) {
acceptFor(&listenSocket, &addressLen, &isRunning);
SLEEP
}

return 0;
}

void createListenSocket(int * retListenSocket, const int port, bool * isRunning) {
INIT_SOCKET_SYSTEM
struct sockaddr_in addServer;
(* retListenSocket) = socket(AF_INET, SOCK_STREAM, 0);
int tErr = errno;
if ((* retListenSocket) > 0) {
printf("The socket was created (%i)\n", * retListenSocket);
} else {
printf("Couldnt create socket\n- ");
switch (tErr) {
case EACCES:
printf("Permission to create a socket of the specified type and/or protocol is denied.\n");
break;
case EAFNOSUPPORT:
printf("The implementation does not support the specified addServer family.\n");
break;
case EINVAL:
printf("Unknown protocol, or protocol family not available. OR Invalid flags in type.\n");
break;
case EMFILE:
printf("Process file table overflow.\n");
break;
case ENFILE:
printf("The system limit on the total number of open files has been reached.\n");
break;
case ENOBUFS:
printf("Insufficient memory is available. The socket cannot be created until sufficient resources are freed.\n");
break;
case ENOMEM:
printf("Insufficient memory is available. The socket cannot be created until sufficient resources are freed.\n");
break;
case EPROTONOSUPPORT:
printf("The protocol type or the specified protocol is not supported within this domain.\n");
break;
default:
printf("unspecified error %i ... \n", tErr);
break;
}
* isRunning = false;
return;
}

addServer.sin_family = AF_INET;
addServer.sin_addr.s_addr = INADDR_ANY;
addServer.sin_port = htons(port);

if (bind(* retListenSocket, (struct sockaddr * ) &addServer, sizeof(struct sockaddr_in)) == 0) {
printf("Socket bind successfull\n");
} else {
printf("Socket bind failed\n");
* isRunning = false;
return;
}
}

// http://linux.die.net/man/2/listen / http://pubs.opengroup.org/onlinepubs/9699919799/
void listenFor(int * listenSocket, bool * isRunning) {
int t = listen(* listenSocket, 10);
int tErr = errno;
if (t < 0) {
printf("Error while listening\n- ");
//perror("server: listen");
switch (tErr) {
case EADDRINUSE:
printf("Another socket is already listening on the same port.\n");
break;
case EBADF:
printf("The argument sockfd is not a valid descriptor.\n");
break;
case ENOTSOCK:
printf("The argument sockfd is not a socket\n");
break;
case EOPNOTSUPP:
printf("The socket is not of a type that supports the listen() operation\n");
break;
default:
printf("Undefined Error%i\n", tErr);
break;
}
* isRunning = false;
}
}

// http://linux.die.net/man/2/accept / http://pubs.opengroup.org/onlinepubs/9699919799/
void acceptFor(int * listenSocket, socklen_t * addrlen, bool * isRunning) {
struct sockaddr_in addClient;
memset(&addClient, 0, sizeof(addClient));
int NewSocket = accept(* listenSocket, (struct sockaddr *) &addClient, addrlen);
int tErr = errno;

//write(NewSocket, "Hoi\n", 4);
if (tErr != 0) {
printf("Error while accepting\n- ");
switch (tErr) {
case EAGAIN:
printf("The socket is marked nonblocking and no connections are present to be accepted. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.\n");
break;
case EWOULDBLOCK:
printf("The socket is marked nonblocking and no connections are present to be accepted. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.\n");
break;
case EBADF:
printf("The descriptor is invalid\n");
break;
case ECONNABORTED:
printf("A connection has been aborted.\n");
break;
case EFAULT:
printf("The addr argument is not in a writable part of the user addServer space.\n");
break;
case EINTR:
printf("The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7).\n");
break;
case EINVAL:
printf("Socket is not listening for connections, or addrlen is invalid (e.g., is negative). or (accept4()) invalid value in flags\n");
break;
case EMFILE:
printf("The per-process limit of open file descriptors has been reached.\n");
break;
case ENFILE:
printf("The system limit on the total number of open files has been reached.\n");
break;
case ENOBUFS:
printf("Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory.\n");
break;
case ENOMEM:
printf("Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory.\n");
break;
case ENOTSOCK:
printf("The descriptor references a file, not a socket.\n");
break;
case EOPNOTSUPP:
printf("The referenced socket is not of type SOCK_STREAM.\n");
break;
case EPROTO:
printf("Protocol error\n");
break;
default:
printf("Undefined Error %i\n", tErr);
break;
}
* isRunning = false;
} else if (NewSocket != -1) {
handleConnection(&NewSocket, &addClient);
}
}

void handleConnection(int * inSocket, struct sockaddr_in * addClient) {
if (* inSocket > 0){
int bufferSize = 1024;
char * buffer = malloc(bufferSize);
memset(buffer, '\0', bufferSize);

char response[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n\r\n"
"<html><head><title>test</title>"
"<html><body><H1>Hello world</H1></body></html>";
printf("The Client is connected from %s ...\n", inet_ntoa((* addClient).sin_addr));
//[todo] handle full buffer
int received = recv(* inSocket, buffer, bufferSize, 0);
printf("%s\nbuffer size: %i\n", buffer, bufferSize);
send(* inSocket, response, strlen(response), 0);
printf("=> response send\n");
CLOSE_SOCKET_FUNCTION(* inSocket);
}
}


What I'm doing on client-side?




  1. Need to initialise WSA on Windows, nothing on Linux.

  2. Create a socket for the client on client-side.

  3. Create struct sockaddr_in for the server.



[try 1]


  1. Create struct sockaddr_in for the client.

  2. Bind the socket to the struct for client.

  3. Connect with client Socket to server-struct.

  4. Send a message.



[try 2]


  1. Use sendto because I only want to send one message.



Both isnt working, I think my problem is the struct sockaddr_in, but I havent any idea right know why.
What I'm doing wrong here?

View Edit 3 for solutions.

#ifdef __WIN32__
# include <winsock2.h> // used for sockets with windows, needs startup / shutdown
# include <ws2tcpip.h> // for MinGW / socklen_t / InetPtonA
# define INIT_SOCKET_SYSTEM WSADATA wsaData;if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {printf ("Error initialising WSA.\n");exit(6);}
# define CLEAR_SOCKET_SYSTEM WSACleanup();
# include <windows.h> // for Sleep
# define SLEEP Sleep(10); // sleeping 10ms
#else
# include <sys/socket.h>
# define INIT_SOCKET_SYSTEM printf("Linux dont need a special init for sockets, so all fine.\n");
# define CLEAR_SOCKET_SYSTEM printf("Linux dont need a special clear for sockets, so all fine.\n");
# include <time.h>
# define SLEEP sleep(1); // sleeping a second :-/
#endif
#include <stdio.h>
#include <sys/stat.h>
#include <stdbool.h>

// Step 1, create lokal Access point
void createSocket(int * mySocket);
// Step 2, create the target address
struct sockaddr_in getTargetAddress(char * ip, int port);

int * cleanSocket;
void cleanUp() {
close(* cleanSocket);
CLEAR_SOCKET_SYSTEM
}

int main() {
int mySocket;
// Step 1 create you Socket
createSocket(&mySocket);
// Step 2 get target
struct sockaddr_in serverAddress = getTargetAddress("127.0.0.1", 15000);
//struct sockaddr_in myAddress = getTargetAddress("127.0.0.1", 15000);
// Step 3 bind & connect or sendto
//bind(mySocket, (const struct sockaddr *) &myAddress, sizeof(myAddress));
//connect(mySocket, (const struct sockaddr * )&serverAddress, sizeof(serverAddress));
char * question = "Whats up?\n";
printf("sending %s\n", question);
//send(mySocket, question, strlen(question), 0); // try to use protocol?
sendto(mySocket, question, strlen(question), 0, (const struct sockaddr *) &serverAddress, sizeof(serverAddress));
printf("sended!\n");

close(mySocket);
return 0;
}

void createSocket(int * mySocket) {
INIT_SOCKET_SYSTEM
if ((* mySocket = socket(AF_INET, SOCK_STREAM, 0)) > 0) {
printf("Socket creation successful\n");
} else {
printf("Socket creation failed\n");
}
}

struct sockaddr_in getTargetAddress(char * ip, int port) {
struct sockaddr_in ret;
ret.sin_family = AF_INET;
ret.sin_addr.s_addr = inet_addr(ip);
ret.sin_port = htons(15000);
return ret;
}


Edit 1



Commenting includes out:
I dont have any compiler errors, just a warning because
int received
is not in use.
I comment that out because I tried a lot and wanted to clean it up before I post it here, but thought it could be important enough to keep it as a comment.
Maybe its included in another include? I will check that.

I test and write on windows right now, but finally it needs to run on linux too. I test the server above with a small tool in Autoit on windows on the same machine which connects to a server and do a GET request. The server got the GET, printed it in his console and send a reply back which the Autoit-client got and printed, so it worked once.
Without the close Operation I can do it everytime.

Edit 2 - got answer for the server, client still not running



Server is running fine now, got the answer from:
http://cs.baylor.edu/~donahoo/practical/CSockets/WindowsSockets.pdf


Moving from UNIX sockets to Windows sockets is fairly simple. Windows programs require a different set of include files, need initialization and deallocation of WinSock resources, use closesocket( ) instead of close( ), and use a different error reporting facility. However, the meat of the application is identical to UNIX.


Edit 3 - client working, but one minor question



Need to shorten the links because I'm not allowed to post that many links directly.

My mistake in try 1 was to bind the client struct to the same IP as the server.
"127.0.0.1"(clientaddress) => "Pseudo" and its working.

struct sockaddr_in serverAddress = getTargetAddress("127.0.0.1", 15000);
struct sockaddr_in myAddress = getTargetAddress("Pseudo", 15000);
bind(mySocket, (const struct sockaddr *) &myAddress, sizeof(myAddress));
connect(mySocket, (const struct sockaddr * )&serverAddress, sizeof(serverAddress));
send(mySocket, question, strlen(question), 0);





But I dont need to bind here by myself, connect does it if its not done, and it handles that its an unused address.
pubs.opengroup [.] org/onlinepubs/9699919799/functions/connect.html


If the socket has not already been bound to a local address, connect() shall bind it to an address which, unless the socket's address family is AF_UNIX, is an unused local address.


struct sockaddr_in serverAddress = getTargetAddress("127.0.0.1", 15000);
connect(mySocket, (const struct sockaddr * )&serverAddress, sizeof(serverAddress));
send(mySocket, question, strlen(question), 0);





Of course this is working, but in my opinion not correct.

struct sockaddr_in serverAddress = getTargetAddress("127.0.0.1", 15000);
connect(mySocket, (const struct sockaddr * )&serverAddress, sizeof(serverAddress));
sendto(mySocket, question, strlen(question), 0, (const struct sockaddr *) &serverAddress, sizeof(serverAddress));


This here should also work in my opinion, I shouldnt need connect in this structure because it should be built in sendto.

pubs.opengroup [.] org/onlinepubs/9699919799/functions/connect.html


The connect() function shall attempt to make a connection on a connection-mode socket[...]


pubs.opengroup [.] org/onlinepubs/9699919799/functions/sendto.html


If the socket is connection-mode, dest_addr shall be ignored.


Because of the text above I think this should also work, but it doesnt. Maybe someone can say why? (Or maybe it should work without myAddress and bind)

struct sockaddr_in serverAddress = getTargetAddress("127.0.0.1", 15000);
struct sockaddr_in myAddress = getTargetAddress("Pseudo", 15000);
bind(mySocket, (const struct sockaddr *) &myAddress, sizeof(myAddress));
sendto(mySocket, question, strlen(question), 0, (const struct sockaddr *) &serverAddress, sizeof(serverAddress));


And btw the return value for send and sendto isnt clearly.


Successful completion of a call to send() does not guarantee delivery of the message. A return value of -1 indicates only locally-detected errors.

Successful completion of a call to sendto() does not guarantee delivery of the message. A return value of -1 indicates only locally-detected errors.


I think the return value is useless, or not? If its -1 it can be delivered, if 1 it maybe isnt.
Maybe determine another protocol?

minor questions




  1. Why I still need connect for sendto?

  2. Can I have a clear return value from send / sendto with another protocol?



Will search for both and edit it here if I found an answer, will still watch if someone can answer this.
My major-problems are gone so really thanks to all.




Thanks for reading!

x82 x82
Answer

It won't work because you never call connect(). You should check return value from calls like connect() , send () etc.
The solution : You should call connect after you created the socket.

connect(mySocket, (SOCKADDR *)&serverAddress, sizeof(sockaddr_in));  

To use send or sendTo with TCP, the socket has be connected or you will get an error.

send(mySocket, question, strlen(question), 0);
Comments