Solved

Sockets - read and write

Posted on 2001-08-28
15
571 Views
Last Modified: 2008-02-01
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
0
Comment
Question by:katjas
  • 5
  • 3
  • 2
  • +5
15 Comments
 
LVL 3

Expert Comment

by:kulina
ID: 6431851
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.
0
 

Author Comment

by:katjas
ID: 6432103

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
0
 
LVL 3

Expert Comment

by:kulina
ID: 6434383
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.
0
 
LVL 4

Expert Comment

by:newmang
ID: 6434510
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
0
 

Author Comment

by:katjas
ID: 6435357
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
0
 
LVL 4

Expert Comment

by:newmang
ID: 6435414
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
0
 
LVL 4

Expert Comment

by:newmang
ID: 6435469
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.
 
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 1

Expert Comment

by:Aggarwal
ID: 6435601

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 !!
0
 
LVL 4

Expert Comment

by:newmang
ID: 6438882
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
0
 

Author Comment

by:katjas
ID: 6448717
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
0
 
LVL 4

Accepted Solution

by:
newmang earned 40 total points
ID: 6449369
katjas

The read function will return the number of bytes read so if you specify a looping 1 byte read then it will continue to return 1 until there is no more data to be read. Normally this would signify the end of the data from the other party and therefore the end of the message.

The other way you can ensure that the full message has been received is to code either a delimiter or a length in your messages. Assuming that you do not send binary data normally (i.e. all the information exchanged is textual - spaces, punctuation, letters and numbers etc - in other words in the normal ASCII range of 0x20 - 0x7F) you could either end the message with a binary value outside that range, say 0xFF or you could precede the message with a binary integer encoding the length of the message and then have your code check for this.

Cheers - Gavin
0
 
LVL 10

Expert Comment

by:makerp
ID: 6450533
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.
0
 
LVL 1

Expert Comment

by:nhuanvn
ID: 6485306
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 ?
0
 
LVL 1

Expert Comment

by:Moondancer
ID: 7024864
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.
http://www.experts-exchange.com/jsp/qList.jsp?ta=commspt
 
Please click this link for Help Desk, Guidelines/Member Agreement and the Question/Answer process.  http://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.
http://www.experts-exchange.com/questions/Q.12027399.html
http://www.experts-exchange.com/questions/Q.20009551.html
http://www.experts-exchange.com/questions/Q.20036889.html
http://www.experts-exchange.com/questions/Q.20076318.html
http://www.experts-exchange.com/questions/Q.20079982.html
http://www.experts-exchange.com/questions/Q.20088301.html
http://www.experts-exchange.com/questions/Q.20092671.html
http://www.experts-exchange.com/questions/Q.20095170.html
http://www.experts-exchange.com/questions/Q.20114215.html
http://www.experts-exchange.com/questions/Q.20125382.html
http://www.experts-exchange.com/questions/Q.20149027.html
http://www.experts-exchange.com/questions/Q.20174966.html
http://www.experts-exchange.com/questions/Q.20223721.html

To view your locked questions, please click the following link(s) and evaluate the proposed answer.
http://www.experts-exchange.com/questions/Q.12024660.html
http://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
http://www.experts-exchange.com/jsp/qManageQuestion.jsp?ta=commspt&qid=20274643
------> POINTS FOR EXPERTS awaiting comments are listed in the links below
http://www.experts-exchange.com/commspt/Q.20277028.html (Part 1)
http://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
0
 
LVL 45

Expert Comment

by:sunnycoder
ID: 9419276
No comment has been added lately and this question is therefore classified abandoned.

If asker wishes to close the question, then refer to
http://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
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

Suggested Solutions

Have you thought about creating an iPhone application (app), but didn't even know where to get started? Here's how: ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Important pre-programming comments: I’ve never tri…
An Outlet in Cocoa is a persistent reference to a GUI control; it connects a property (a variable) to a control.  For example, it is common to create an Outlet for the text field GUI control and change the text that appears in this field via that Ou…
The goal of this video is to provide viewers with basic examples to understand and use pointers in the C programming language.
The goal of this video is to provide viewers with basic examples to understand and use structures in the C programming language.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now