User avatar
woodystanford
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am

Socket Any One?

Fri Apr 07, 2017 9:44 am

I've done sockets before...but not too many of them. I think the reason why we look at them as esoteric is because the conventional wisdom is we don't need that many of them (as in we always work it out when a new web browser is being developed, or an FTP server, or whatever). I personally would love to see an explosion of this particular technique, so I've prepared a pristine version, and DO get back with me with any errors at all and I'll edit the code here. I went over this with a fine tooth comb.

This is a good socket implementation. A claim. You do this, you will create a socket that is (1) binded (with bind() ) and (2) working perfectly. The first code I'm posting here is a version that sends a test string from the client program TO the server program. I didn't set it up the typical microcomm way because I wanted to test everything (and thank goodness I did because there are some tiny, though critical, errors that would have slipped through if I hadn't). What you put into the client socket comes out the server socket. Rigorously tested on my Ubuntu btw.

************************************
The code below represents a minimalist coding of a working bi-directional BOUND socket (blocking). You can cut and paste it to your program and modify from there, saving you the 48 hours of aggravation of just screwing with it. A claim.
************************************

On reflection, I don't want to dirty up the main thread post with code, so I'll leave it at this and then post the client.c and server.c code in the following posts.

THEN I will talk about the "typical" errors I made/came across (for example, did you know that you HAVE switch file descriptors midstream after accept()? I've done socket before and I didn't even remember that.)

THEN I will elucidate on some interesting things that I thought I knew about sockets, that aren't true and are far groovier than you might think. For example, did you know that you can treat them just like file descriptor streams? (not fwrite() type functions, but the write() function) Still I coded the examples with the traditional send() and recv() functions.

User avatar
woodystanford
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am

Re: Socket Any One?

Fri Apr 07, 2017 9:56 am

OK, if you want a great, complete primer on how to do a socket, I recommend the web page that I ended up selecting. You have to understand that UN*X started out in academia and to a certain extent defence (a la DARPAnet) so the best documentation would a lot of times come from an academic source:

http://www.cs.dartmouth.edu/~campbell/c ... mming.html

If you print this out, and copy and paste the example code and compile/link its pristine too.

OK, here is my code for the client and server for testing purposes. This first part is client.c.

Code: Select all

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <memory.h>

main()
{
        struct sockaddr_in myaddr;
        int sfd; 
        int port=3000; 
	socklen_t myaddrlen;

        char s[255]; //string buffer for receiving what comes over socket
        char s2[255]="This is a test string FROM client TO server"; //test string

	//set up the sockaddr structure, myaddr
	memset(&myaddr, 0, sizeof(myaddr));
        myaddr.sin_family = AF_INET;
        inet_pton(AF_INET, "127.0.0.1", &myaddr.sin_addr.s_addr);
	//myaddr.sin_addr.s_addr=inet_addr("127.0.0.1"); //alternat form
        myaddr.sin_port = htons(port);

	//create socket 
	sfd=socket(AF_INET, SOCK_STREAM, 0); //protocol 0 here seems to default to IP on my Ubuntu Box

	//connect to server
	connect(sfd,(struct sockaddr *)&myaddr, sizeof myaddr);

	printf("Sending Test String...");
	fflush(NULL);

	//write the test string
	//write(sfd,s2,strlen(s2)+1); //alternate form
	send(sfd, s2, strlen(s2)+1, 0);

	printf("done.\n");
	fflush(NULL);

	//read the acknowledgement from server
	//read(s,s,255); //alternate form
	recv(sfd, s, 255,0);

	printf("Received as acknowledgement: %s\n\n",s);
	fflush(NULL);

	//close the socket
	close(sfd);

	return;
}
Here is the code for server.c . Cut and paste this as well to a fresh text file.

Code: Select all

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h> 
#include <string.h>
#include <memory.h>

main()
{
	struct sockaddr_in myaddr, cliaddr;
	int sfd,cfd; //notice server-side you need two file descriptors
	int port=3000; 
	socklen_t cliaddrlen; //important beleive it or not

	char s[255]; //string buffer for receiving what comes over socket
	char s2[255]; //ascknowledgement string back to client

	memset(&myaddr, 0, sizeof(myaddr));
	myaddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &myaddr.sin_addr.s_addr);
	//myaddr.sin_addr.s_addr=inet_addr("127.0.0.1"); //alternate form
	myaddr.sin_port = htons(port);

	//...or you can let it automatically select one
	//myaddr.sin_addr.s_addr = INADDR_ANY;

	//create socket
	sfd = socket(AF_INET, SOCK_STREAM, 0); //protocol 0 here seems to default to IP on my Ubuntu Box
	//if you want to see the list of available protocols [arg#3] pico the /etc/protocols file
	//bind socket
	bind(sfd, (struct sockaddr *)&myaddr, sizeof myaddr);

	//listen on socket for inbound
	//NOTE! Listen does NOT block. It just puts the socket in a listening state
	listen(sfd,1); //just hardcode backlog to 1 for testing....accepts only one connection
		       //expand out to about 8 for light use, or more for heavy use


	//when a request is detected, accept it
	//NOTE! Where we get the blocking behavior we are looking for is on accept()
  	cliaddrlen = sizeof(cliaddr);
	cfd=accept(sfd,(struct sockaddr *)&cliaddr, &cliaddrlen);
	//BIG NOTE! from this point forward we communicate with the stream with file descriptor cfd
	//NOT sfd - might be where a lot of people are making a mistake right here.

	//read from the socket
	//read(cfd,s,255); //alternate form
	recv(cfd,s,255,0);

	printf("Received Test String: %s\n\n",s);
	fflush(NULL);

	//send acknoledgement back to client (tests 2-way communication over same socket!)
	strcpy(s2,"100 Acknoledgement");
        //write(s, s2, strlen(s2)+1); //alternate form
	send(cfd,s2,strlen(s2)+1,0); //note make it strlen(s2)+1 (PLUS ONE!) or you won't send
	//the trailing string-termination 0x00 byte which leads to goobly-gook after the stream

	//read EOF from socket (formal) - handshaking? I guess just ignore result
	//read(s,s,1024); //alternate form
	recv(cfd,s,255,0);

	//close cfd and socket(sfd) streams on this end
	close(cfd);
	close(sfd);

}
Compile/link with:

gcc client.c -o client -lm (don't forget Peter's Wall and extra switches)
gcc server.c -o server -lm

and invoke in separate terminal windows (not tabs) with:

./server (server first lol, and keep it where you can see it when you invoke client)
./client

It transmits a "test string" from the client to the server. It prints the sent string out on the server-side terminal and then sends back an acknowledgement string. That string is then shown on the client's terminal. Tested and works (even getting the stream a little cleaner than usual).
Last edited by woodystanford on Fri Apr 07, 2017 10:07 am, edited 1 time in total.

User avatar
DougieLawson
Posts: 36178
Joined: Sun Jun 16, 2013 11:19 pm
Location: Basingstoke, UK
Contact: Website Twitter

Re: Socket Any One?

Fri Apr 07, 2017 10:03 am

If you're going to write code for beginners please write code that compiles cleanly - else it's useless junk that doesn't teach anyone anything useful.

Code: Select all

woodyjunk.c:9:1: warning: return type defaults to ‘int’ [-Wreturn-type]
 main()
 ^
woodyjunk.c:9:1: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
woodyjunk.c: In function ‘main’:
woodyjunk.c:22:9: warning: implicit declaration of function ‘inet_pton’ [-Wimplicit-function-declaration]
         inet_pton(AF_INET, "127.0.0.1", &myaddr.sin_addr.s_addr);
         ^
woodyjunk.c:22:9: warning: nested extern declaration of ‘inet_pton’ [-Wnested-externs]
woodyjunk.c:24:33: warning: conversion to ‘uint16_t’ from ‘int’ may alter its value [-Wconversion]
         myaddr.sin_port = htons(port);
                                 ^
woodyjunk.c:52:4: warning: ‘return’ with no value, in function returning non-void [-Wreturn-type]
    return;
    ^
woodyjunk.c:14:14: warning: unused variable ‘myaddrlen’ [-Wunused-variable]
    socklen_t myaddrlen;
              ^
Note: Having anything humorous in your signature is completely banned on this forum. Wear a tin-foil hat and you'll get a ban.

Any DMs sent on Twitter will be answered next month.

This is a doctor free zone.

Martin Frezman
Posts: 1020
Joined: Mon Oct 31, 2016 10:05 am

Re: Socket Any One?

Fri Apr 07, 2017 11:56 am

I don't think teaching is his intent.

What his reason for posting is - is anyone's guess. Somebody should do a master's thesis on it.
If this post appears in the wrong forums category, my apologies.

User avatar
woodystanford
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am

Re: Socket Any One?

Fri Apr 07, 2017 12:46 pm

OK, here is the minicomm-like version. What you do is you copy and paste the code to client2.c and server2.c and compile and execute. (remember its like a web server and a browser, you have to start the server2 first THEN execute client2).

It does illustrate bidirectional bound socket communication, however the user interface is so simplistic that the string being sent back from the server is just a ">>" which looks like the new line marker in the client. I'm really happy this works as well as it does because with a little more coding to make it more asynchronous, I think I have achieved my goal: shedding a little light on how to make socket programming more understandable.

Even with the simplicity of the user interface, it illustrates that a bound socket actually stays bound in real time (play with it a bit). And I've linear-ized the main loop to the point that its understandable, I think.

client2.c

Code: Select all

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h> 
#include <string.h>
#include <memory.h>

main()
{
        struct sockaddr_in myaddr;
        int sfd; 
        int port=3000; 
	socklen_t myaddrlen;

        char s[255]; //string buffer comming in
        char s2[255]=""; // string buffer going out
	char c,c2;

	printf("Socket Testing Client V0.88\n");
	printf("by Woody Stanford\n");
	printf("hit CTRL <C> to exit\n\n");

	//set up the sockaddr structure, myaddr
	memset(&myaddr, 0, sizeof(myaddr));
        myaddr.sin_family = AF_INET;
        inet_pton(AF_INET, "127.0.0.1", &myaddr.sin_addr.s_addr);
	//myaddr.sin_addr.s_addr=inet_addr("127.0.0.1"); //alternat form
        myaddr.sin_port = htons(port);

	//create socket 
	sfd=socket(AF_INET, SOCK_STREAM, 0); 

	//connect to server
	connect(sfd,(struct sockaddr *)&myaddr, sizeof myaddr);

	while (1)
	{
		gets(s);
		send(sfd, s, strlen(s)+1, 0);

		s2[0]=0; //clear it first
		recv(sfd, s2, 255, 0);
		printf("%s",s2);
		fflush(NULL);
	}

	//close the socket
	close(sfd);

	return;
}
server2.c

Code: Select all

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h> 
#include <string.h>
#include <memory.h>

main()
{
	struct sockaddr_in myaddr, cliaddr;
	int sfd,cfd; //notice server-side you need two file descriptors
	int port=3000; 
	socklen_t cliaddrlen; //important beleive it or not

	char s[255]; //string buffer for receiving 
	char s2[255]; //string buffer for transmitting
	//char c,c2;

	printf("Socket Testing Server v0.88\n");
	printf("by Woody Stanford\n");
	printf("hit CTRL <C> to exit\n\n");

	memset(&myaddr, 0, sizeof(myaddr));
	myaddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &myaddr.sin_addr.s_addr);
	//myaddr.sin_addr.s_addr=inet_addr("127.0.0.1"); //alternate form
	myaddr.sin_port = htons(port);

	//...or you can let it automatically select one
	//myaddr.sin_addr.s_addr = INADDR_ANY;

	//create socket
	sfd = socket(AF_INET, SOCK_STREAM, 0); 

	//bind socket
	bind(sfd, (struct sockaddr *)&myaddr, sizeof myaddr);

	//listen on socket for inbound
		listen(sfd,1); 

	//when a request is detected, accept it
	//NOTE! Where we get the blocking behavior we are looking for is on accept()
  	cliaddrlen = sizeof(cliaddr);
	cfd=accept(sfd,(struct sockaddr *)&cliaddr, &cliaddrlen);
	
	while (1)
	{
		//read from the socket
		recv(cfd,s,255,0);
		printf("%s\n",s);
		fflush(NULL);

		strcpy(s2,">>"); //return a ">>" string to indicate
		//bidirectional binded transmission
		send(cfd,s2,strlen(s2)+1,0); 

	}

	//close cfd and socket(sfd) streams on this end
	close(cfd);
	close(sfd);

}
I think I'll write a fully asynchronous UI next to illustrate true bidirectional, constant connect through a TCP/IP socket. Wish me luck. Then I'll point out the insertion point in DAEMON1 and then it'll have true high-speed streaming (instead of that slow :( file-based stuff only).

User avatar
DougieLawson
Posts: 36178
Joined: Sun Jun 16, 2013 11:19 pm
Location: Basingstoke, UK
Contact: Website Twitter

Re: Socket Any One?

Fri Apr 07, 2017 2:11 pm

More code that doesn't compile.

Code: Select all

unk2.c:9:1: warning: return type defaults to ‘int’ [-Wreturn-type]
 main()
 ^
junk2.c:9:1: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
junk2.c: In function ‘main’:
junk2.c:27:9: warning: implicit declaration of function ‘inet_pton’ [-Wimplicit-function-declaration]
         inet_pton(AF_INET, "127.0.0.1", &myaddr.sin_addr.s_addr);
         ^
junk2.c:27:9: warning: nested extern declaration of ‘inet_pton’ [-Wnested-externs]
junk2.c:29:33: warning: conversion to ‘uint16_t’ from ‘int’ may alter its value [-Wconversion]
         myaddr.sin_port = htons(port);
                                 ^
junk2.c:39:7: warning: ‘gets’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]
       gets(s);
       ^
junk2.c:51:4: warning: ‘return’ with no value, in function returning non-void [-Wreturn-type]
    return;
    ^
junk2.c:18:11: warning: unused variable ‘c2’ [-Wunused-variable]
    char c,c2;
           ^
junk2.c:18:9: warning: unused variable ‘c’ [-Wunused-variable]
    char c,c2;
         ^
junk2.c:14:14: warning: unused variable ‘myaddrlen’ [-Wunused-variable]
    socklen_t myaddrlen;
              ^
/tmp/cc3Q1hyk.o: In function `main':
junk2.c:(.text+0xc4): warning: the `gets' function is dangerous and should not be used.

Code: Select all

junk3.c:9:1: warning: return type defaults to ‘int’ [-Wreturn-type]
 main()
 ^
junk3.c:9:1: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
junk3.c: In function ‘main’:
junk3.c:26:4: warning: implicit declaration of function ‘inet_pton’ [-Wimplicit-function-declaration]
    inet_pton(AF_INET, "127.0.0.1", &myaddr.sin_addr.s_addr);
    ^
junk3.c:26:4: warning: nested extern declaration of ‘inet_pton’ [-Wnested-externs]
junk3.c:28:28: warning: conversion to ‘uint16_t’ from ‘int’ may alter its value [-Wconversion]
    myaddr.sin_port = htons(port);
                            ^
Note: Having anything humorous in your signature is completely banned on this forum. Wear a tin-foil hat and you'll get a ban.

Any DMs sent on Twitter will be answered next month.

This is a doctor free zone.

User avatar
woodystanford
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am

Re: Socket Any One?

Tue Apr 18, 2017 12:06 am

Written on Ubuntu, a true select-based socket client/server.

client4.c

Code: Select all

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int done=0; //done flag for main program loop

//exloded command line
char exploded[10][255];

//gets replacement
char *gets2(char *s, int buffsize)
{
	//fgets(s, buffsize , stdin);
	return gets(s);
}



//explode-based parser
parse(char *s, char s2[10][255])
{
        char c;
        int cnt=0;
        int cnt2=0;
        int a;

	for (a=0;a<10;a++) s2[a][0]=0; //clear array

        for (a=0;a<strlen(s);a++)
        {
                c=s[a];
                if (c==' ')
                {
                        s2[cnt][cnt2]=0;
                        cnt++;
                        cnt2=0;
                }
      else
                   s2[cnt][cnt2++]=c;
        }

   s2[cnt][cnt2]=0;
}

main()
{
	char s[1024];
	char ss[1024]; //search string

	int a; 
	int fn; //DEMO filenumber

	printf("Demo V0.88 Alpha - File Retreival Protocol - by Zaxxon\n");
	printf("Type ? or help for list of commands...\n");

	while (!done)
	{
		printf(">");
		gets2(s,1024);

		parse(s,exploded);

		//help command
		if ((stricmp("help",exploded[0])==0)||(exploded[0][0]=='?'))
		{
			if (strlen(exploded[1])==0)
			{
				printf("SEARCH <string> - returns all files pertaining to seach string on DEMO network.\n");
				printf("GET <filenumber> - fetches the file identified by <filenumber> and places in downloads.\n");				
				printf("DESC <filenumber> - displays the description of the file identified by <filenumber>.\n");
				printf("DIR <pattern> - shows all files in downloads.\n");
				printf("STATUS - shows all downloads in progress.\n");
				printf("CANCEL <filenumber> - cancels the download of file <filenumer> if in progress.\n");
				printf("CONFIG - opens DEMO configurtion screen.\n");
				printf("SCAN - performs port scan reconsiliation of Peers DB in case of network shutdown.\n");
				printf("HELP - HELP <command>, DHELP.\n");
				printf("EXIT or QUIT - exits program immediately stopping all downloads (no recovery in this version).\n\n");
			}
			else
			{
				//detailed help by command

				if (strcmp("search",exploded[1])==0)
				{
					printf("\nDetailed SEARCH help: the search string entered must exactly match (case ignored) a portion of ");
					printf("the returned filenames. No wildcards supported in this version.\n\n");
					printf(" IMPORTANT: all filenumbers returned are invalidated upon a new SEARCH Use the current filenumber ");
					printf(" from the current search on GET.\n\n");
				}

				if (strcmp("get",exploded[1])==0)
				{
					printf("\nDetailed GET <filenumber> help: the file number returned from SEARCH is unique and identifiable ");
					printf("only during the current SEARCH and is not unique across the network and only reflects the order ");
					printf("in which a matching file was found on searched peers.\n\n");

					printf("Alternate Version: GET <filenumber> <copy to filespec> when arg 2 is specified, the downloaded ");
					printf("file is copied to the filespec provided. Very useful!\n\n");
				}

				if (strcmp("desc",exploded[1])==0)
				{
					printf("\nDetailed DESC <filenumber> help: it is possible to show a peer a description of a download file ");
					printf("by making a text file with the same name as the original but with the .txt extension. It just ");
					printf("shows the contents of this file (when available) to the user.\n\n");
				}

				if (strcmp("dir",exploded[1])==0)
				{
					printf("\nDetailed DIR help: the current contents of the DEMO downloads directory is shown.\n\n");
				}

				if (strcmp("status",exploded[1])==0)
				{
					printf("\nDetailed STATUS help: all downloads currently in progress are shown. If you are expecting a file ");
					printf("that is not, it might be already download, so type DIR and check in the downloads directory\n\n");
				}

				if (strcmp("cancel",exploded[1])==0)
				{
					printf("\nDetailed CANCEL <filenumber> help: If the file identified by <filenumber> is in progress of being ");
					printf("downloaded, you can cancel it with this command. If it is already downloaded, it does nothing.\n\n");
				}

				if (strcmp("config",exploded[1])==0)
				{
					printf("\nDetailed CONFIG help: You can view/edit all DEMO configuration settings by entering this command. ");
					printf("Things like throttling, server status and the like are fully setable from this screen.\n\n");
				}

				if (strcmp("scan",exploded[1])==0)
				{
					printf("\nDetailed SCAN help: IMPORANT! This command should almost never be used. It means that all of the IP addresses ");
					printf("in your Peer DB file are BAD (ie. they are all disconnected or, if dynamically allocated, shifted on the same ");
					printf("subnet).\n\n");

					printf("Note that it works like a sniffer, so only use when ABSOLUTELY necessary to restore contact with your DEMO peers.\n\n");
				}

				if ((strcmp("exit",exploded[1])==0)||(strcmp("quit",exploded[1])==0))
				{
					printf("\nDetailed QUIT/EXIT help: immediately concels all downloads and exits program. You can also use just ");
					printf("\"q\" or \"x\" to exit DEMO as well\n\n");
				}


			}		

		}


		if ((stricmp("quit",exploded[0])==0)||(stricmp("exit",exploded[0])==0)||(stricmp("x",exploded[0])==0)||(stricmp("q",exploded[0])==0))
		{
			printf("\nExitting...\n\n");
			done=1;
		}

		if (stricmp("search",exploded[0])==0)
		{
			//reassmeble exploded information to one search string
			ss[0]=0;
			for (a=1;a<10;a++)
			{
				if (strlen(exploded[a])>0)
				{
					strcat(ss,exploded[a]);
					strcat(ss," ");
				}
			}
			
			if (strlen(ss)>0)
			{
				printf("\nSearching DEMO for files matching \"%s\". This might take a few minutes. Also, if you find the file you are looking for ",ss);
				printf("you can terminate the search with a <ctrl>Z\n\n");
			}
			else
				printf("\nYou need to put in a search string to use SEARCH. Try again...\n\n");

		}

		if (stricmp("dir",exploded[0])==0)
		{
			//show contents of DOWNLOADS directory to user
			printf("\nContents of DOWNLOADS directory...\n\n"); //DEBUG: code later	
		}

		if (stricmp("status",exploded[0])==0)
		{
			//show curretn download STATUS
			printf("\nDownloads in progress...\n\n"); //DEBUG: code later	
		}

		if (stricmp("cancel",exploded[0])==0)
		{
			fn=atoi(exploded[1]);

			if (fn>0)
			{
				//cancel download of file <filenumber>
				printf("\nCancelling download %d...\n\n",fn); //DEBUG: code later
			}
			else
				printf("Invalid download filenumber entered. Try again...\n\n");	
		}






	}

}
server4.c

Code: Select all

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <sys/select.h> 
#include <sys/time.h> 
#include <netdb.h>
#include <unistd.h> 
#include <string.h>
#include <memory.h>

#define MAX_CLIENTS 32

/* According to POSIX.1-2001 */
/* According to earlier standards */
main()
{
	struct sockaddr_in myaddr, cliaddr[MAX_CLIENTS];
	int sfd,cfd[MAX_CLIENTS]; //notice server-side you need two file descriptors

	int port=3000; //port number to listen on
	char ip="127.0.0.1"; //ip address of server

	socklen_t cliaddrlen[MAX_CLIENTS]; //important beleive it or not

	int cactive[MAX_CLIENTS]; //mask of active clients, element nonzero if active
	int cca=0; //cactive ordinal of CURRENTLY servicing cactive

	fd_set read_fd_set; //fd set for select()
	struct timeval timeout; //set the timeout on select to 0 milliseconds

	char s[255]; //string buffer for receiving what comes over socket
	char s2[255]; //string buffer for transmitting back to client
	int a,i; //loop variables

	int done; //loop variable, set to nonzero to exit main loop
	
	//Initialization
	//clear cactive - means all "slots" are inactive
	for (a=0;a<MAX_CLIENTS;a++) cactive[a]=0;

	printf("Socket Testing Server v0.88\n");
	printf("by Woody Stanford\n");
	printf("hit CTRL <C> to exit\n\n");

	memset(&myaddr, 0, sizeof(myaddr));
	myaddr.sin_family = AF_INET;
	inet_pton(AF_INET, ip, &myaddr.sin_addr.s_addr);
	//myaddr.sin_addr.s_addr=inet_addr(ip); //alternate form
	myaddr.sin_port = htons(port);

	//...or you can let it automatically select one
	//myaddr.sin_addr.s_addr = INADDR_ANY;

	//create socket
	sfd = socket(AF_INET, SOCK_STREAM, 0); //protocol 0 [arg#3] is standard inet (default)
	//if you want to see the list of available protocols [arg#3] pico the /etc/protocols file

	//bind socket
	bind(sfd, (struct sockaddr *)&myaddr, sizeof myaddr);

	//listen on socket for inbound
	//NOTE! Listen does NOT block. It just puts the socket in a listening state
	listen(sfd,MAX_CLIENTS);  //expand out to about 8 for light use, or more for heavy use

	//how this works is it loops and tests select() and ONLY runs the inner comm loop when it
	//needs to accept()
	done=0;
	while (!done)
	{
		printf("."); fflush(NULL);//DEBUG

//MAIN SOCKET HANDLING CODE

		//prepare for select() operation
		FD_ZERO(&read_fd_set); //clear fd set to initialize 
		FD_SET(sfd, &read_fd_set);

		//set timeout value for select (optimal would be instantaneous)
		timeout.tv_sec =1; //set for 1 second
		timeout.tv_usec = 100000; //set for 100 miliseconds for now

		select(sfd+1,&read_fd_set,NULL,NULL,&timeout);

		if (FD_ISSET(sfd, &read_fd_set))
		{
//			printf("request1\n"); fflush(NULL);//DEBUG

			//when a request is detected, accept it (if we have slots for it)
			cca=-1;
			for (a=0;a<MAX_CLIENTS;a++)
			{
				if (cactive[a]==0) //find the first empty slot
				{
		 	 		cliaddrlen[a] = sizeof(cliaddr[a]);
					cfd[a]=accept(sfd,(struct sockaddr *)&cliaddr[a], &cliaddrlen[a]);

					cca=a; //curent client active
					cactive[a]=1; //flag this slot as low actively serving client

					a=MAX_CLIENTS+1; //just to exit for loop
				}
			}
			
			if (cca!=-1) 
			{
				printf("\nReceived Connection Request - Handling on cactive[%d]...\n",cca);
				fflush(NULL);
			}
			else  //means we got a connection request but no available slots
			{
				printf("\nConnection Request but at Maximum Clients\n");
				fflush(NULL);
			}
		}

//MAIN SERVER SERVICE CODE

		printf("_"); fflush(NULL); //DEBUG: test how often we are hitting the main program loop

		for (a=0;a<MAX_CLIENTS;a++)
		{
			if (cactive[a]) //remember to only service the active clients
			{

			//************************************
			// PUT YOUR CODE HERE
			// Which client am I processing?
			// simple its client  # a
			// you can send() and recv() wherever
			// you want in here.
			// one proviso: can't write any BLOCKING
			// code.
			//************************************

			//you can tear all this code out if you want
			//its just test code I mean

				s[0]=0;
				recv(cfd[a],s,255,MSG_DONTWAIT); //check if there is anything on this slot

				if (s[0]=='Q')
				{
					//a Q command will cause us to close the client connection
					printf("\nReceived QUIT command on cactive[%d]...\n",a); fflush(NULL);
					cactive[a]=0; //indicate cfd is available for reuse
					close(cfd[a]); //physically close client socket
				}

				//sloppy way of doing but easiest way of getting exit "signal" in for now
				if (s[0]=='X') 
					done=1;
				
				if (s[0]!=0) //means we received a string of some kind
				{
					printf("\nData Received on cactive[%d]: %s...\n",a,s); fflush(NULL);

					strcpy(s2,">>"); //return a ">>" string to indicate
					//bidirectional binded transmission, looks like a newline char
					send(cfd[a],s2,strlen(s2)+1,0); 
				}

			//*************************************
			//  End of Your Code
			//*************************************

			}
		}

	}

	printf("\nExiting server...\n"); fflush(NULL);

	//close all active cfd and [listening] socket(sfd) streams on this end

	for (a=0;a<MAX_CLIENTS;a++)
		if (cactive[a]!=0) close(cfd[a]);

	close(sfd);

}
Compile and link both (on a desktop UN*X box) and port over to Raspberian. Run server4 first and then run multiple shells each with a client4 in them. As long as you put in non-blocking code in the part where its supposed to go, it'll run your stuff.

User avatar
Paeryn
Posts: 2680
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Socket Any One?

Tue Apr 18, 2017 1:14 am

woodystanford wrote:Written on Ubuntu, a true select-based socket client/server.

client4.c

Code: Select all

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int done=0; //done flag for main program loop

//exloded command line
char exploded[10][255];

//gets replacement
char *gets2(char *s, int buffsize)
{
	//fgets(s, buffsize , stdin);
	return gets(s);
}



//explode-based parser
parse(char *s, char s2[10][255])
{
        char c;
        int cnt=0;
        int cnt2=0;
        int a;

	for (a=0;a<10;a++) s2[a][0]=0; //clear array

        for (a=0;a<strlen(s);a++)
        {
                c=s[a];
                if (c==' ')
                {
                        s2[cnt][cnt2]=0;
                        cnt++;
                        cnt2=0;
                }
      else
                   s2[cnt][cnt2++]=c;
        }

   s2[cnt][cnt2]=0;
}

main()
{
	char s[1024];
	char ss[1024]; //search string

	int a; 
	int fn; //DEMO filenumber

	printf("Demo V0.88 Alpha - File Retreival Protocol - by Zaxxon\n");
	printf("Type ? or help for list of commands...\n");

	while (!done)
	{
		printf(">");
		gets2(s,1024);

		parse(s,exploded);

		//help command
		if ((stricmp("help",exploded[0])==0)||(exploded[0][0]=='?'))
		{
			if (strlen(exploded[1])==0)
			{
				printf("SEARCH <string> - returns all files pertaining to seach string on DEMO network.\n");
				printf("GET <filenumber> - fetches the file identified by <filenumber> and places in downloads.\n");				
				printf("DESC <filenumber> - displays the description of the file identified by <filenumber>.\n");
				printf("DIR <pattern> - shows all files in downloads.\n");
				printf("STATUS - shows all downloads in progress.\n");
				printf("CANCEL <filenumber> - cancels the download of file <filenumer> if in progress.\n");
				printf("CONFIG - opens DEMO configurtion screen.\n");
				printf("SCAN - performs port scan reconsiliation of Peers DB in case of network shutdown.\n");
				printf("HELP - HELP <command>, DHELP.\n");
				printf("EXIT or QUIT - exits program immediately stopping all downloads (no recovery in this version).\n\n");
			}
			else
			{
				//detailed help by command

				if (strcmp("search",exploded[1])==0)
				{
					printf("\nDetailed SEARCH help: the search string entered must exactly match (case ignored) a portion of ");
					printf("the returned filenames. No wildcards supported in this version.\n\n");
					printf(" IMPORTANT: all filenumbers returned are invalidated upon a new SEARCH Use the current filenumber ");
					printf(" from the current search on GET.\n\n");
				}

				if (strcmp("get",exploded[1])==0)
				{
					printf("\nDetailed GET <filenumber> help: the file number returned from SEARCH is unique and identifiable ");
					printf("only during the current SEARCH and is not unique across the network and only reflects the order ");
					printf("in which a matching file was found on searched peers.\n\n");

					printf("Alternate Version: GET <filenumber> <copy to filespec> when arg 2 is specified, the downloaded ");
					printf("file is copied to the filespec provided. Very useful!\n\n");
				}

				if (strcmp("desc",exploded[1])==0)
				{
					printf("\nDetailed DESC <filenumber> help: it is possible to show a peer a description of a download file ");
					printf("by making a text file with the same name as the original but with the .txt extension. It just ");
					printf("shows the contents of this file (when available) to the user.\n\n");
				}

				if (strcmp("dir",exploded[1])==0)
				{
					printf("\nDetailed DIR help: the current contents of the DEMO downloads directory is shown.\n\n");
				}

				if (strcmp("status",exploded[1])==0)
				{
					printf("\nDetailed STATUS help: all downloads currently in progress are shown. If you are expecting a file ");
					printf("that is not, it might be already download, so type DIR and check in the downloads directory\n\n");
				}

				if (strcmp("cancel",exploded[1])==0)
				{
					printf("\nDetailed CANCEL <filenumber> help: If the file identified by <filenumber> is in progress of being ");
					printf("downloaded, you can cancel it with this command. If it is already downloaded, it does nothing.\n\n");
				}

				if (strcmp("config",exploded[1])==0)
				{
					printf("\nDetailed CONFIG help: You can view/edit all DEMO configuration settings by entering this command. ");
					printf("Things like throttling, server status and the like are fully setable from this screen.\n\n");
				}

				if (strcmp("scan",exploded[1])==0)
				{
					printf("\nDetailed SCAN help: IMPORANT! This command should almost never be used. It means that all of the IP addresses ");
					printf("in your Peer DB file are BAD (ie. they are all disconnected or, if dynamically allocated, shifted on the same ");
					printf("subnet).\n\n");

					printf("Note that it works like a sniffer, so only use when ABSOLUTELY necessary to restore contact with your DEMO peers.\n\n");
				}

				if ((strcmp("exit",exploded[1])==0)||(strcmp("quit",exploded[1])==0))
				{
					printf("\nDetailed QUIT/EXIT help: immediately concels all downloads and exits program. You can also use just ");
					printf("\"q\" or \"x\" to exit DEMO as well\n\n");
				}


			}		

		}


		if ((stricmp("quit",exploded[0])==0)||(stricmp("exit",exploded[0])==0)||(stricmp("x",exploded[0])==0)||(stricmp("q",exploded[0])==0))
		{
			printf("\nExitting...\n\n");
			done=1;
		}

		if (stricmp("search",exploded[0])==0)
		{
			//reassmeble exploded information to one search string
			ss[0]=0;
			for (a=1;a<10;a++)
			{
				if (strlen(exploded[a])>0)
				{
					strcat(ss,exploded[a]);
					strcat(ss," ");
				}
			}
			
			if (strlen(ss)>0)
			{
				printf("\nSearching DEMO for files matching \"%s\". This might take a few minutes. Also, if you find the file you are looking for ",ss);
				printf("you can terminate the search with a <ctrl>Z\n\n");
			}
			else
				printf("\nYou need to put in a search string to use SEARCH. Try again...\n\n");

		}

		if (stricmp("dir",exploded[0])==0)
		{
			//show contents of DOWNLOADS directory to user
			printf("\nContents of DOWNLOADS directory...\n\n"); //DEBUG: code later	
		}

		if (stricmp("status",exploded[0])==0)
		{
			//show curretn download STATUS
			printf("\nDownloads in progress...\n\n"); //DEBUG: code later	
		}

		if (stricmp("cancel",exploded[0])==0)
		{
			fn=atoi(exploded[1]);

			if (fn>0)
			{
				//cancel download of file <filenumber>
				printf("\nCancelling download %d...\n\n",fn); //DEBUG: code later
			}
			else
				printf("Invalid download filenumber entered. Try again...\n\n");	
		}






	}

}
How are you getting this past the compiler?

stricmp() doesn't exist, strcasecmp() does though.

You create a replacement for gets()... which calls gets() !!! Don't you see the compiler warning about gets() being dangerous?

Code: Select all

client4.c: In function ‘gets2’:
client4.c:14:11: warning: implicit declaration of function ‘gets’ [-Wimplicit-function-declaration]
    return gets(s);
           ^~~~
client4.c:14:11: warning: return makes pointer from integer without a cast [-Wint-conversion]
    return gets(s);
           ^~~~~~~
client4.c: At top level:
client4.c:20:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 parse(char *s, char s2[10][255])
 ^~~~~
client4.c:45:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main()
 ^~~~
client4.c: In function ‘main’:
client4.c:64:12: warning: implicit declaration of function ‘stricmp’ [-Wimplicit-function-declaration]
       if ((stricmp("help",exploded[0])==0)||(exploded[0][0]=='?'))
            ^~~~~~~
/tmp/ccq0yZrF.o: In function `gets2':
client4.c:(.text+0x18): warning: the `gets' function is dangerous and should not be used.
/tmp/ccq0yZrF.o: In function `main':
client4.c:(.text+0x200): undefined reference to `stricmp'
client4.c:(.text+0x504): undefined reference to `stricmp'
client4.c:(.text+0x524): undefined reference to `stricmp'
client4.c:(.text+0x544): undefined reference to `stricmp'
client4.c:(.text+0x564): undefined reference to `stricmp'
/tmp/ccq0yZrF.o:client4.c:(.text+0x5a0): more undefined references to `stricmp' follow
collect2: error: ld returned 1 exit status
And you still insist on creating functions like parse(...) without declaring the return type which gets promoted to returning int. But then your function doesn't return an int. Double error,
woodystanford wrote: server4.c

Code: Select all

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <sys/select.h> 
#include <sys/time.h> 
#include <netdb.h>
#include <unistd.h> 
#include <string.h>
#include <memory.h>

#define MAX_CLIENTS 32

/* According to POSIX.1-2001 */
/* According to earlier standards */
main()
{
	struct sockaddr_in myaddr, cliaddr[MAX_CLIENTS];
	int sfd,cfd[MAX_CLIENTS]; //notice server-side you need two file descriptors

	int port=3000; //port number to listen on
	char ip="127.0.0.1"; //ip address of server

	socklen_t cliaddrlen[MAX_CLIENTS]; //important beleive it or not

	int cactive[MAX_CLIENTS]; //mask of active clients, element nonzero if active
	int cca=0; //cactive ordinal of CURRENTLY servicing cactive

	fd_set read_fd_set; //fd set for select()
	struct timeval timeout; //set the timeout on select to 0 milliseconds

	char s[255]; //string buffer for receiving what comes over socket
	char s2[255]; //string buffer for transmitting back to client
	int a,i; //loop variables

	int done; //loop variable, set to nonzero to exit main loop
	
	//Initialization
	//clear cactive - means all "slots" are inactive
	for (a=0;a<MAX_CLIENTS;a++) cactive[a]=0;

	printf("Socket Testing Server v0.88\n");
	printf("by Woody Stanford\n");
	printf("hit CTRL <C> to exit\n\n");

	memset(&myaddr, 0, sizeof(myaddr));
	myaddr.sin_family = AF_INET;
	inet_pton(AF_INET, ip, &myaddr.sin_addr.s_addr);
	//myaddr.sin_addr.s_addr=inet_addr(ip); //alternate form
	myaddr.sin_port = htons(port);

	//...or you can let it automatically select one
	//myaddr.sin_addr.s_addr = INADDR_ANY;

	//create socket
	sfd = socket(AF_INET, SOCK_STREAM, 0); //protocol 0 [arg#3] is standard inet (default)
	//if you want to see the list of available protocols [arg#3] pico the /etc/protocols file

	//bind socket
	bind(sfd, (struct sockaddr *)&myaddr, sizeof myaddr);

	//listen on socket for inbound
	//NOTE! Listen does NOT block. It just puts the socket in a listening state
	listen(sfd,MAX_CLIENTS);  //expand out to about 8 for light use, or more for heavy use

	//how this works is it loops and tests select() and ONLY runs the inner comm loop when it
	//needs to accept()
	done=0;
	while (!done)
	{
		printf("."); fflush(NULL);//DEBUG

//MAIN SOCKET HANDLING CODE

		//prepare for select() operation
		FD_ZERO(&read_fd_set); //clear fd set to initialize 
		FD_SET(sfd, &read_fd_set);

		//set timeout value for select (optimal would be instantaneous)
		timeout.tv_sec =1; //set for 1 second
		timeout.tv_usec = 100000; //set for 100 miliseconds for now

		select(sfd+1,&read_fd_set,NULL,NULL,&timeout);

		if (FD_ISSET(sfd, &read_fd_set))
		{
//			printf("request1\n"); fflush(NULL);//DEBUG

			//when a request is detected, accept it (if we have slots for it)
			cca=-1;
			for (a=0;a<MAX_CLIENTS;a++)
			{
				if (cactive[a]==0) //find the first empty slot
				{
		 	 		cliaddrlen[a] = sizeof(cliaddr[a]);
					cfd[a]=accept(sfd,(struct sockaddr *)&cliaddr[a], &cliaddrlen[a]);

					cca=a; //curent client active
					cactive[a]=1; //flag this slot as low actively serving client

					a=MAX_CLIENTS+1; //just to exit for loop
				}
			}
			
			if (cca!=-1) 
			{
				printf("\nReceived Connection Request - Handling on cactive[%d]...\n",cca);
				fflush(NULL);
			}
			else  //means we got a connection request but no available slots
			{
				printf("\nConnection Request but at Maximum Clients\n");
				fflush(NULL);
			}
		}

//MAIN SERVER SERVICE CODE

		printf("_"); fflush(NULL); //DEBUG: test how often we are hitting the main program loop

		for (a=0;a<MAX_CLIENTS;a++)
		{
			if (cactive[a]) //remember to only service the active clients
			{

			//************************************
			// PUT YOUR CODE HERE
			// Which client am I processing?
			// simple its client  # a
			// you can send() and recv() wherever
			// you want in here.
			// one proviso: can't write any BLOCKING
			// code.
			//************************************

			//you can tear all this code out if you want
			//its just test code I mean

				s[0]=0;
				recv(cfd[a],s,255,MSG_DONTWAIT); //check if there is anything on this slot

				if (s[0]=='Q')
				{
					//a Q command will cause us to close the client connection
					printf("\nReceived QUIT command on cactive[%d]...\n",a); fflush(NULL);
					cactive[a]=0; //indicate cfd is available for reuse
					close(cfd[a]); //physically close client socket
				}

				//sloppy way of doing but easiest way of getting exit "signal" in for now
				if (s[0]=='X') 
					done=1;
				
				if (s[0]!=0) //means we received a string of some kind
				{
					printf("\nData Received on cactive[%d]: %s...\n",a,s); fflush(NULL);

					strcpy(s2,">>"); //return a ">>" string to indicate
					//bidirectional binded transmission, looks like a newline char
					send(cfd[a],s2,strlen(s2)+1,0); 
				}

			//*************************************
			//  End of Your Code
			//*************************************

			}
		}

	}

	printf("\nExiting server...\n"); fflush(NULL);

	//close all active cfd and [listening] socket(sfd) streams on this end

	for (a=0;a<MAX_CLIENTS;a++)
		if (cactive[a]!=0) close(cfd[a]);

	close(sfd);

}
Compile and link both (on a desktop UN*X box) and port over to Raspberian. Run server4 first and then run multiple shells each with a client4 in them. As long as you put in non-blocking code in the part where its supposed to go, it'll run your stuff.
Lets look at the errors here:-

Code: Select all

pi@rpi3:~/Programming/woody $ gcc server4.c -o server4
server4.c:15:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main()
 ^~~~
server4.c: In function ‘main’:
server4.c:21:12: warning: initialization makes integer from pointer without a cast [-Wint-conversion]
    char ip="127.0.0.1"; //ip address of server
            ^~~~~~~~~~~
server4.c:47:4: warning: implicit declaration of function ‘inet_pton’ [-Wimplicit-function-declaration]
    inet_pton(AF_INET, ip, &myaddr.sin_addr.s_addr);
    ^~~~~~~~~
implicit int when defining main, assigning a char array to a char. Not including the correct header file that declares inet_pton() (the file you need is #include <arpa/inet.h>
She who travels light — forgot something.

Return to “C/C++”