Skip to content
Advertisement

C Server/Client with Sockets

[Just 2 minor questions on the ground of this remaining]

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 socketn- ");
        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 successfulln");
    } else {
        printf("Socket bind failedn");
        * 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 listeningn- ");
        //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 socketn");
                break;
            case EOPNOTSUPP:
                printf("The socket is not of a type that supports the listen() operationn");
                break;
            default:
                printf("Undefined Error%in", 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, "Hoin", 4);
    if (tErr != 0) {
        printf("Error while acceptingn- ");
        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 invalidn");
                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 flagsn");
                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 errorn");
                break;
            default:
                printf("Undefined Error %in", 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, '', bufferSize);
        
        char response[] = "HTTP/1.1 200 OKrn"
                    "Content-Type: text/htmlrnrn"
                    "<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("%snbuffer size: %in", buffer, bufferSize);
        send(* inSocket, response, strlen(response), 0);
        printf("=> response sendn");
        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 %sn", 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 successfuln");
    } else {
        printf("Socket creation failedn");
    }
}

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.

mySocket = socket(AF_INET, SOCK_STREAM, 0)
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.

mySocket = socket(AF_INET, SOCK_STREAM, 0)
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.

mySocket = socket(AF_INET, SOCK_STREAM, 0)
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)

mySocket = socket(AF_INET, SOCK_STREAM, 0)
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!

Advertisement

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);
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement