Link to home
Start Free TrialLog in
Avatar of katjas
katjas

asked on

Sockets - read and write

Hi experts,

i am creating a socket server and a socket client.
The server waits for connections on a special port
and the client sents messages to the server.
If the server receives a message, he replys a
message to the client. The length of that message
is not exactly known.

My problem: How can i implement an appropriate read
            on both sides.
            On both sides I don't know the size of
            the messages.

Does anbody have sample code for such a read function ??
Do you have a thin example server ?

Many thanks
katjas
Avatar of kulina
kulina

Hi,

Below is the example that I coded for another member recently. If you have questions let me know.
--------------
Try this code below. First I need you to test it and then I will follow up with any questions you may
have. I tried to make it as simple and (complete at the same time) as possible. As you may notice there
is a lot of error checking (I didn't do it for IP address only). Basically, you start server by supplying
it with a port number to listen on (usually more than 1024). Then in another instance of Win32 console
window you start client by giving it ip address and port of the server. This will be 127.0.0.1 if you
are running server on your local machine, and the port number you started server on. Type in a string
at the client prompt, that will be sent to server and returned in upper case - a simple scenario to
demonstrate to basic functionality.
I await your feedback and/or questions.
Regards.

CODE - START
##################### SERVER ############################
/* server.c */
#include <winsock2.h>
#include <stdio.h>
#include <io.h>
#include <signal.h>

#pragma comment( lib, "WS2_32.LIB" )
#define MAX_BUF 255

int isValidPort( int port );
void DisplayErrorAndExit( DWORD dwError, LPTSTR pszAPI );
void signal_handler( int sig );
int initWinSock2(void);
void to_upper( char *str );

/* Socket descriptors - made global in order to close them if user
  presses CTRL+C */
SOCKET server_sockfd;
SOCKET client_sockfd;

int main(int argc, char *argv[])
{
   struct sockaddr_in server_addr;
   struct sockaddr_in client_addr;
   int status;
   int port;
   int bytes_read = 1;
   int server_len;
   int client_len;
   unsigned long block = 0;
   char recv_buf[MAX_BUF+1];
   
   /* Indicate that we want to catch potential CTRL+C signal */
   (void)signal( SIGINT, signal_handler );

   if( argc < 2 ){
       printf( "Usage: server <port number>\n" );
       exit( EXIT_FAILURE );
   }

   port = atoi( argv[1] );
   if( !isValidPort(port) ){
       printf( "Invalid port. Port must be between 1 and 65535\n");
       exit( EXIT_FAILURE );        
   }    

   /* Begin: Init Winsock2 */
   if( initWinSock2() != NO_ERROR ){
       printf( "Can't initialize Winsock\n");
       exit(-1);
   }

   memset( recv_buf, 0, sizeof(recv_buf) );
   server_len = sizeof(server_addr);
   client_len = sizeof(client_addr);

   /* Create the socket.*/
   server_sockfd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
   if (server_sockfd == INVALID_SOCKET) {
       DisplayErrorAndExit( WSAGetLastError(), "socket" );
   }

   /* Turn on blocking IO */
   status = ioctlsocket( server_sockfd, FIONBIO, &block);
   if( status != NO_ERROR ){
       DisplayErrorAndExit( WSAGetLastError(), "ioctlsocket" );
   }

   /* Set server to listen any interface at port # supplied at cmd line */
   server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons(port);

   /* Bind the socket to the port.*/
   status = bind( server_sockfd, (struct sockaddr *)&server_addr, server_len);
   if( status != NO_ERROR ){
       DisplayErrorAndExit( WSAGetLastError(), "bind" );
   }
   else{
       printf( "Server: Listening at port %d...\n", port );
   }

   /* start listening for incoming connections */
   status = listen( server_sockfd, 1 );
   if( status != NO_ERROR ){
       DisplayErrorAndExit( WSAGetLastError(), "listen" );
   }

   /* Waiting (blocked-mode) for a connection */
   client_sockfd = accept( server_sockfd, (struct sockaddr *)&client_addr,
                           &client_len );
   if( client_sockfd == INVALID_SOCKET ){
       DisplayErrorAndExit( WSAGetLastError(), "accept" );
   }
   else{
       printf( "Server: Accepted client from %s, port %d\n",
                inet_ntoa(client_addr.sin_addr),
                ntohs(client_addr.sin_port) );
   }
   
   while( bytes_read > 0 ){
       /* Receive a message from the client */
       bytes_read = recv( client_sockfd, recv_buf, MAX_BUF, 0 );
       if( bytes_read > 0 ){
           printf( "Server: Received from client: %s\n", recv_buf );
           /* convert received string to upper case and return it to client*/
           to_upper(recv_buf);
           send( client_sockfd, recv_buf, MAX_BUF, 0 );
       }
       else{
           printf( "Server: Client disconnected\n" );
       }
   }

   printf( "Server: Shutting down.\n" );
   closesocket( server_sockfd );
   closesocket( client_sockfd );
   WSACleanup();
   return 0;
}

void DisplayErrorAndExit( DWORD dwError, LPTSTR pszAPI )
{
   void *lpvErrorMsg;

   /* Get error description */
   FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
                  FORMAT_MESSAGE_FROM_SYSTEM,
                  NULL, dwError,
                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                  (char *)&lpvErrorMsg, 0, NULL );

   /* Display error.*/
   printf( "%s: \n", pszAPI );
   printf( "Error Code: %d\n", dwError );
   printf( "Error Desc: %s\n", (char *)lpvErrorMsg );

   /* Free buffer allocated by the system, and exit with error code */
   LocalFree( lpvErrorMsg );
   WSACleanup();
   exit( dwError );
}

/* Handles CTRL+C signal. Free resources and close */
void signal_handler( int sig )
{
   printf( "\nServer shuting down...\n" );
   closesocket( server_sockfd );
   closesocket( client_sockfd );
   WSACleanup();
   exit(0);
}

/* Validate port number */
int isValidPort( int port )
{
   return (port >= 1 && port <= 65535);
}

/* initiates use of Winsock 2 by a process */
int initWinSock2()
{
   WSADATA wsaData;
   WORD    wVersionRequested;

   wVersionRequested = MAKEWORD( 2, 2 );
   WSAStartup( wVersionRequested, &wsaData );
   
   if ( LOBYTE( wsaData.wVersion ) != 2 ||
        HIBYTE( wsaData.wVersion ) != 2 ){
       /* no usable winsock2.dll found*/
       WSACleanup( );
       return -1;
   }

   return 0;
}

/* Convert str to upper case */
void to_upper( char *str )
{
   char *p_str = str - 1;

   while( ++p_str && *p_str ) {
       if( 'a' <= *p_str && *p_str <= 'z' ){
                *p_str -= 'a'-'A';
       }
   }
}



##################### CLIENT ############################

/* client.c */
#include <stdio.h>
#include <winsock2.h>        
#include <io.h>              

#pragma comment( lib, "WS2_32.LIB" )
#define MAX_BUF 255

void usage( void );
int initWinSock2( void );
void DisplayErrorAndExit( DWORD dwError, LPTSTR pszAPI );
int isValidPort( int port );

int main( int argc, char *argv[] )
{
   int sockfd;
   int server_len;
   int port;
   int bytes_read = 1;
   int status;
   unsigned long block = 0;
   struct sockaddr_in server_addr;
   char ip_address[16];
   char recv_buf[MAX_BUF+1];

   if( argc < 3 ){
       usage();
   }

   if( initWinSock2() != NO_ERROR ){
       printf( "Can't initialize Winsock\n");
       exit(-1);
   }
   
   port = atoi( argv[2] );
   strcpy( ip_address, argv[1] );
   server_len = sizeof( server_addr );
   memset( recv_buf, 0, sizeof(recv_buf) );

   sockfd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );        
   if( sockfd < 0 ){
       DisplayErrorAndExit( WSAGetLastError(), "socket" );
   }

   ioctlsocket( sockfd, FIONBIO, &block );      

   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons( port );
   server_addr.sin_addr.s_addr = inet_addr( ip_address );
   
   status = connect( sockfd, (struct sockaddr *)&server_addr, server_len );
   if( status != -1 ){
       printf( "Client: Connected to %s at port %d\n", ip_address, port );
   }
   else{
       DisplayErrorAndExit( WSAGetLastError(), "socket" );
   }

   while( stricmp( recv_buf, "quit") != 0 ){
       printf( "\nEnter string('quit' to exit): " );
       fflush(stdout);
       scanf( "%s", recv_buf );
       send( sockfd, recv_buf, MAX_BUF, 0 );
       bytes_read = recv( sockfd, recv_buf, MAX_BUF, 0 );
       if( bytes_read > 0 ){
           printf( "Client: Received from server: %s\n", recv_buf );
       }
   }

   printf( "Client: Shutting down.\n" );
   closesocket( sockfd );
   WSACleanup();
   
   return 0;
}

/* initiates use of Winsock 2 by a process */
int initWinSock2()
{
   WSADATA wsaData;
   WORD    wVersionRequested;

   wVersionRequested = MAKEWORD( 2, 2 );
   WSAStartup( wVersionRequested, &wsaData );
   
   if ( LOBYTE( wsaData.wVersion ) != 2 ||
        HIBYTE( wsaData.wVersion ) != 2 ){
       /* no usable winsock2.dll found*/
       WSACleanup( );
       return -1;
   }

   return 0;
}

void usage( )
{
   printf( "\nUsage: client <ip address> <port>\n" );
   exit(-1);
}

void DisplayErrorAndExit( DWORD dwError, LPTSTR pszAPI )
{
   void *lpvErrorMsg;

   /* get error description */
   FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
                  FORMAT_MESSAGE_FROM_SYSTEM,
                  NULL, dwError,
                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                  (char *)&lpvErrorMsg, 0, NULL );

   /* display error.*/
   printf( "%s: \n", pszAPI );
   printf( "Error Code: %d\n", dwError );
   printf( "Error Desc: %s\n", (char *)lpvErrorMsg );

   /* Free buffer allocated by the system, and exit with error code */
   LocalFree( lpvErrorMsg );
   WSACleanup();
   exit( dwError );
}

CODE - END


SAMPLE OUTPUT:
**************** CLIENT ************************
c:\temp\working>client 127.0.0.1 3455
Client: Connected to 127.0.0.1 at port 3455

Enter string('quit' to exit): hello
Client: Received from server: HELLO

Enter string('quit' to exit): there
Client: Received from server: THERE

Enter string('quit' to exit): quit
Client: Received from server: QUIT
Client: Shutting down.

****************** SERVER *********************
c:\temp\working>server 3455
Server: Listening at port 3455...
Server: Accepted client from 127.0.0.1, port 2839
Server: Received from client: hello
Server: Received from client: there
Server: Received from client: quit
Server: Client disconnected
Server: Shutting down.
Avatar of katjas

ASKER


My only problem is the read or recv.

I cannot be sure, that the message i receive is
a maximum of 1024 Bytes long.

So how can i implement a function that receives
any messages from the client.

Thanks katjas
Sorry, I as under the impression that you need the server example. I never tried using some sort of dynamic allocation of a receiving buffer since (let's assume) there is no way to know how much data is comming from the other side until the read() returns with the number of bytes read. Every C implementation of this kind, that I came across, have used some MAX_BUFFER_SIZE constant (i.e. 2048 bytes). Maybe it's possible to allocate dynamically, since the receiving buffer is of void* type, and the cast would be possible, but this would have to be C++... I'll try something along these lines.
It would be ugly but you could do this..

1) malloc an area of memory of a given size (say 1k).
2) read from the socket 1 bytes at a time and write each byte into the malloc'ed area keeping track of where you are in the area.
3) If your counter into the malloc'ed area hits the top of the area then realloc the malloc'ed area to increase the size of the area and continue.

Cheers - Gavin
Avatar of katjas

ASKER

Sounds good newmanq,

but do you have an example source of these steps...
i have some problems with reallocing and so on....

Is it necessary that I define a character for
my client-server protocol that means 'end of stream' ???

The client can send any text with spaces, newlines and
so on. Then he sends this text.
Then he receives the reply from the server, that can
be of any size.
How should the server know that 'this is the
end of clients message ' ????


Thanks
katjas
katjas

1) The end of data should be indicated by the read function returning 0.

2) The memory allocation could be like this...

int current_position;
int maximum_position;
const int increment=1000;
unsigned char * ptr_buffer;
unsigned char   received_char;

ptr_buffer=(unsigned char *)malloc(increment);
maximum_position=increment;
current_position=0;
while(add_your_read_function_call_here()!=0)
{
   *(ptr_buffer+current_position)=received_char;
   current_position++;
   if(current_position>maximum_position)
   {
       realloc(ptr_buffer,maximum_position+increment);
       maximum_position+=increment;
   }
}
do_something_with_the_buffer;
free(ptr_buffer);


Obviously you can refine this, for example in a server you would not continually malloc and then free memory, you would initially allocate some memory, let it grow as needed and then eventually free it up when the server shuts down.


Note I have not included error checking to improve clarity - obviously you can add this....

Cheers - Gavin
katjas

Sorry, in my pseudo code the line reading

if(current_position>maximum_position)
 
should read

if(current_position==maximum_position)

as current _position will start at 0 not 1 as it is an offset into the buffer.
 

recv() returns the number of bytes read from the socket.
Then why do u need to do so ..

try this ..

#define MAX_SIZE 1024 // can be whatever u want !!

char buffer[MAX_SIZE];

....

int bytes_read = recv(sockFd,buffer,MAX_SIZE,0);
buffer[bytes_read] = '\0'; //if NULL terminated string is needed !!
Aggarwal

While your code is valid his problem is that he does not know how much data he will be receiving and therefore cannot allocate and use a fixed size buffer.

In your code, if the message size is less than 1024 then all is OK.
If the message size is equal to 1024 then you are going to try and write a null to the 1025th byte of a 1024 byte area (offsets start at zero) thereby clobbering non-related memeory
If the message is greater than 1024 bytes then he won't get the full message in one go (and the situation described above will occur as the read will return 1024!

Hence the desire to have an expandable size memeory area.

Cheers - Gavin
Avatar of katjas

ASKER

Hi newmaq

Could you give an example for your following
pseudo-code ??

  while(add_your_read_function_call_here()!=0)


Does the function than really work or is it a MUST
that i have to define a message-end-character, so
that the server can recognize 'Aha message end' ???

Many thanks
katjas
ASKER CERTIFIED SOLUTION
Avatar of newmang
newmang

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Paul Maker
the easyiest way to do this safley with sockets is first get the client to send to the server the number of bytes the client will send, the server will then know how many to wait for. the best way to do this is write to functions and let the server and client use them

for example

struct header
{
  int to_follow;
};

int send(char *buffer, int len)
{
  struct header h;
  h.to_follow = len;

  send(&h,sizeof(struct header));
  send(buffer,len);

  return a_code_that_signals_success
}

char *recv()
{
  struct header h;
 
  recv(&h,sizeof(struct header));

  char *buffer = (char*)malloc(h.to_follow);

  recv(buffer,h.to_follow);
}


this is more like pseudo code than reall winsock code.
A header as makerp suggested is good. But if you send/recv
with UDP instead of TCP, there may be an error in your
received data. If that error is in the header, all your
packets are wrong then. Luckily, you are using TCP.
Any ideas if we use UDP ?
You have 15 open questions today which need your attention.  ADMINISTRATION WILL BE CONTACTING YOU SHORTLY.  Moderators Computer101, Netminder, Mindphaser or I will return to finalize these as soon as possible if after 7 days no response by the Asker.  EXPERTS-->  please post closing recommendations before that time.

Below are your open questions as of today.  Questions which have been inactive for 21 days or longer are considered to be abandoned and for those, your options are:
1. Accept a Comment As Answer (use the button next to the Expert's name).
2. Close the question if the information was not useful to you, but may help others. You must tell the participants why you wish to do this, and allow for Expert response.  This choice will include a refund to you, and will move this question to our PAQ (Previously Asked Question) database.  If you found information outside this question thread, please add it.
3. Ask Community Support to help split points between participating experts, or just comment here with details and we'll respond with the process.
4. Delete the question (if it has no potential value for others).
   --> Post comments for expert of your intention to delete and why
   --> YOU CANNOT DELETE A QUESTION with comments; special handling by a Moderator is required.

For special handling needs, please post a zero point question in the link below and include the URL (question QID/link) that it regards with details.
https://www.experts-exchange.com/jsp/qList.jsp?ta=commspt
 
Please click this link for Help Desk, Guidelines/Member Agreement and the Question/Answer process.  https://www.experts-exchange.com/jsp/cmtyHelpDesk.jsp

Click you Member Profile to view your question history and please keep them updated. If you are a KnowledgePro user, use the Power Search option to find them.  

Questions which are LOCKED with a Proposed Answer but do not help you, should be rejected with comments added.  When you grade the question less than an A, please comment as to why.  This helps all involved, as well as others who may access this item in the future.  PLEASE DO NOT AWARD POINTS TO ME.

To view your open questions, please click the following link(s) and keep them all current with updates.
https://www.experts-exchange.com/questions/Q.12027399.html
https://www.experts-exchange.com/questions/Q.20009551.html
https://www.experts-exchange.com/questions/Q.20036889.html
https://www.experts-exchange.com/questions/Q.20076318.html
https://www.experts-exchange.com/questions/Q.20079982.html
https://www.experts-exchange.com/questions/Q.20088301.html
https://www.experts-exchange.com/questions/Q.20092671.html
https://www.experts-exchange.com/questions/Q.20095170.html
https://www.experts-exchange.com/questions/Q.20114215.html
https://www.experts-exchange.com/questions/Q.20125382.html
https://www.experts-exchange.com/questions/Q.20149027.html
https://www.experts-exchange.com/questions/Q.20174966.html
https://www.experts-exchange.com/questions/Q.20223721.html

To view your locked questions, please click the following link(s) and evaluate the proposed answer.
https://www.experts-exchange.com/questions/Q.12024660.html
https://www.experts-exchange.com/questions/Q.20234944.html

*****  E X P E R T S    P L E A S E  ******  Leave your closing recommendations.
If you are interested in the cleanup effort, please click this link
https://www.experts-exchange.com/jsp/qManageQuestion.jsp?ta=commspt&qid=20274643 
------> POINTS FOR EXPERTS awaiting comments are listed in the links below
https://www.experts-exchange.com/commspt/Q.20277028.html (Part 1)
https://www.experts-exchange.com/jsp/qShow.jsp?ta=commspt&qid=20295853 (Part 2)
 
Moderators will finalize this question if in @7 days Asker has not responded.  This will be moved to the PAQ (Previously Asked Questions) at zero points, deleted or awarded.
 
Thanks everyone.
Moondancer
Moderator @ Experts Exchange
No comment has been added lately and this question is therefore classified abandoned.

If asker wishes to close the question, then refer to
https://www.experts-exchange.com/help/closing.jsp

Otherwise, I will leave a recommendation in the Cleanup topic area that this question is:
PAQed with A grade to newmang

Please leave any comments here within the next seven days. It is assumed that any participant not responding to this request is no longer interested in its final disposition.

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

Sunny
EE Cleanup Volunteer