I am writing a simple client program which connects to a the ip address "172.31.1.34" and sends a message. Everything works fine but I am not able to recieve any message from the server. The error says "no route to host".
My code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main()
{
struct sockaddr_in server,client;
int s1,s2,len;
int n;
char buffer[500];
strcpy(buffer,"GET http://172.31.1.34/ HTTP/1.0\n\n");
bzero((char *)&client,sizeof(client));
client.sin_port = htons(80);
client.sin_addr.s_addr = inet_addr("172.31.1.34");
client.sin_family = AF_INET;
s2 = socket(AF_INET,SOCK_DGRAM,0);
if(connect(s2,(struct sockaddr *)&client,sizeof(client)) == -1) {
perror("can't connect\n");
exit(1);
}
n = send(s2,buffer,strlen(buffer),0);
if(n < 0) {
perror("message not sent");
exit(1);
}
while(1) {
memset(buffer,0,sizeof(buffer));
n = recv(s2,buffer,500,0);
if(n < 0) {
perror("coudnot read");
exit(1);
}
buffer[n] = '\0';
printf("%s",buffer);
}
close(s2);
return 0;
}
Why are you using SOCK_DGRAM? That is for UDP packets. HTML uses TCP. You should use SOCK_STREAM
I have only briefly looked at your code, but at first glance it seems OK. However I would start with the obvious - maybe there is no route to the host....
Assuming you are on Linux or other Unix platform (including OSX), I would do the following:
ping 172.31.1.34. Note this does not guarantee the host is not available as ping may be blocked.
telnet 172.31.1.34. This should connect and you can enter your HTTP query directly
tcptraceroute 172.31.1.34 80
If all of these fail, the problem is the network, not your code.
On OSX, you can install tcptraceroute from "homebrew". On Linux use your normal package manager (or ask your system managers).
Try the code below instead:
client.sin_addr.s_addr = inet_addr("172.31.1.34");
inet_pton(AF_INET, "172.31.1.34", &client.sin_addr);
Related
I am having a serious problem transferring my HTTP connection socket program over to HTTPS connection socket code, how do I make only an HTTPS connection in pure C?
I am working on a package manager and am rewriting the connection.c file, the only thing this file contains is the code used to make the initial connection to the server containg packages, it does nothing else. I had this working 100% with an HTTP connection, however I need to move to an HTTPS connection and need to use LibreSSL; at the moment I am trying to use OpenSSL as I can't find anything on LibreSSL. The HTTP code I had is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include "repos.h"
#include "resolv.h"
short connection()
{
short socket_desc;
socket_desc = socket(AF_INET, SOCK_STREAM, 0); /* create socket with IPv4 and TCP protocol */
char host[17];
if (socket_desc == -1)
printf("could not create socket\n");
struct sockaddr_in *serv_addr = calloc(1, sizeof(struct sockaddr_in));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(80);
resolv(DEFAULT_HOST, host); /* set repository to use */
if (inet_pton(AF_INET, host, &serv_addr->sin_addr) <= 0) {
printf("error");
free(serv_addr);
return -1;
}
if (connect(socket_desc, (struct sockaddr *)serv_addr, sizeof(*serv_addr)) < 0) {
printf("connection failed\n");
return 1;
}
else {
printf("connection initialized\n");
return 0;
}
/* close the connection */
free(serv_addr);
close(socket_desc);
return 0;
}
This works 100% and I want to just port this over to HTTPS. After looking at the horribly formatted OpenSSL client.c example (see here: https://wiki.openssl.org/index.php/SSL/TLS_Client) I got that code working on my system (had to make some changes to it), and then went off to port over my HTTP code to HTTPS. I worked on it for a bit and thought I got it working, I have been debugging it but can't figure out why it keeps failing. The code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "repos.h"
#include "resolv.h"
SSL *cSSL;
void initssl()
{
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
}
void destroyssl()
{
ERR_free_strings();
EVP_cleanup();
}
void shutdownssl()
{
SSL_shutdown(cSSL);
SSL_free(cSSL);
}
int main()
{
short socket_desc;
short socket_ssl;
char host[17];
socklen_t sock_size;
SSL_CTX *sslctx;
initssl();
socket_desc = socket(AF_INET, SOCK_STREAM, 0); /* create socket with IPv4 and TCP protocol */
if (socket_desc == -1)
printf("could not create socket\n");
struct sockaddr_in *serv_addr = calloc(1, sizeof(struct sockaddr_in));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(443);
resolv(DEFAULT_HOST, host); /* resolve DEFAULT_HOST and store the ip in host */
if (inet_pton(AF_INET, host, &serv_addr->sin_addr) <= 0) {
printf("error");
free(serv_addr);
return -1;
}
bind(socket_desc, (struct sockaddr *)serv_addr, sizeof(struct sockaddr_in));
listen(socket_desc, 5);
sock_size = sizeof(struct sockaddr_in);
socket_ssl = accept(socket_desc, (struct sockaddr *)serv_addr, &sock_size); /* this is where hang occurs, however I am usnure why. I am reading docs and such and if I figure this out I will post the fix; however I would love some advice/help if anyone sees my error */
sslctx = SSL_CTX_new(SSLv23_server_method());
SSL_CTX_set_options(sslctx, SSL_OP_SINGLE_DH_USE);
short use_cert = SSL_CTX_use_certificate_file(sslctx, "/serverCertificate.pem" , SSL_FILETYPE_PEM);
short use_prv = SSL_CTX_use_PrivateKey_file(sslctx, "/serverCertificate.pem", SSL_FILETYPE_PEM);
cSSL = SSL_new(sslctx);
SSL_set_fd(cSSL, socket_ssl);
char ssl_err = SSL_accept(cSSL);
if(ssl_err <= 0) {
printf("connection failed\n");
shutdownssl();
}
else
printf("connected\n");
return 0;
}
Now I know it is missing some obvious things such as writing my own initssl (I am unsure why that isn't already in the lib, but I am starting to see why OpenBSD decided to fork). I left those out as I am more interested in this working with LibreSSL and don't believe you need them with LibreSSL. I tried using print statements to debug but they never get printed even when given at the top of main(). I am unsure why this isn't working and need some help getting this ported. The other files I wrote, repos.h and resolv.c can be seen below:
/* repos.h */
char DEFAULT_HOST[11] = "gitlab.com";
char DEFAULT_PAGE[24] = "Puffles_the_Dragon/core";
/* resolv.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>
short resolv(char *host, char *ip)
{
struct hostent *hp = calloc(1, sizeof(struct hostent));
hp = gethostbyname(host);
if (hp == NULL) {
fprintf(stderr, "gethostbyname() failed\n");
exit(1);
}
else {
short i = 0;
while (hp->h_addr_list[i] != NULL) {
inet_ntoa(*(struct in_addr *)(hp->h_addr_list[i]));
i++;
}
strlcpy(ip, inet_ntoa(*(struct in_addr *)(hp->h_addr_list[0])), 16);
}
return 0;
}
I know some of these calls are outdated due to IPv6, but I am going to add for IPv6 after I get this all working and port from BSD libc to musl libc.
I expected the HTTPS code to run and connect to the server thus printing connected, but it just runs and doesn't fail or print anything.
So I have a simple TCP echo server program here(IPv4) that seems to disconnect immediately after it receives a connection from a new client before the client sends a FIN packet.
Here's the echo server code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAX_BUFFER 1024
#define PORT 4000
int main()
{
int lsock,csock, ret, in , i;
int yes = 1;
char buffer[MAX_BUFFER];
char* c;
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr; // connector's address information
if((lsock = socket(AF_INET,SOCK_STREAM,0))<0){
perror("socket");
return -1;
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(lsock,(struct sockaddr*)&servaddr,sizeof(servaddr))==-1) {
perror("bind");
close(lsock);
return -1;
}
if(listen(lsock,5)==-1){
perror("listen");
close(lsock);
return -1;
}else{
printf("Server listening on port %i\n",PORT);
system("gnome-terminal");
}
while(1){
int len = sizeof(cliaddr);
bzero(&cliaddr,len);
if(csock = accept(lsock,(struct sockaddr*)&cliaddr,&len)==-1){
perror("accept");
exit(0);
}
printf("New client connected....\n");
in = recv(csock,(void*)&buffer,sizeof(buffer),0);
if(in==-1){
perror("recv");
close(csock);
exit(0);
}else if(in==0){
printf("client disconnected\n");
close(csock);
}else{
if(send(csock,(void*)buffer,sizeof(buffer),0)==-1){
perror("send");
close(csock);
}
}
}
return 0;
}
And for the the echo client application:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <linux/ioctl.h>
#define MAX_BUFFER 1024
void die(char *s)
{
perror(s);
exit(1);
}
int main()
{
int connector,flags,r;
int port;
int set = 1;
struct hostent* host;
struct in_addr in;
struct sockaddr_in rmaddr;
char sendbuffer[MAX_BUFFER];
char recvbuffer[MAX_BUFFER];
char hostname[INET_ADDRSTRLEN];
char* exit = "quit";
if((connector = socket(AF_INET,SOCK_STREAM,0))<0){
perror("socket");
return -1;
}
printf("\n");
printf("Enter the remote hostname(URL/IP4 address): ");
scanf("%s", hostname);
printf("\n");
printf("Enter the port number you wish to connect(on): ");
scanf("%u", &port);
printf("\n");
if(port==0){
printf("ERR0R: Port number must be between 1 & 65,535\n");
printf("\n");
printf("Enter the port number you wish to connect(on): ");
scanf("%u", &port);
printf("\n");
}
host = gethostbyname(hostname);
if(host==NULL){
perror("hostname");
return -1;
}
bzero(&rmaddr,sizeof(rmaddr));
rmaddr.sin_family = AF_INET;
rmaddr.sin_port = htons(port);
bcopy((char*)host->h_addr, (char*)&rmaddr.sin_addr.s_addr, host->h_length);
if(connect(connector,(struct sockaddr*)&rmaddr,sizeof(rmaddr))<0){
perror("connect");
close(connector);
return -1;
}else{
printf("\n");
printf("Connected to host: %s",hostname,"on port %u",port);
printf(" type 'quit' to disconnect\n");
printf("\n");
}
while(1){
int nbr,nbs;
nbr = 0;
printf(">");
scanf("%s",sendbuffer);
printf("\n");
if(sendbuffer==exit){
close(connector);
return 0;
}
nbs = send(connector,(void*)&sendbuffer,strlen(sendbuffer),MSG_NOSIGNAL);
printf("\n");
printf("bytes sent: %i\n",nbs);
printf("\n");
if(nbs < 0){
perror("send() failed");
close(connector);
return -1;
}
while(nbr < nbs){
nbr = recv(connector,(void*)&recvbuffer,strlen(recvbuffer),MSG_NOSIGNAL);
if(nbr < 0){
perror("recv() failed");
close(connector);
return -1;
}else if(nbr==0){
printf("recv(): connection closed prematurely");
close(connector);
return -1;
}else if(nbr > 0){
printf("bytes received: %i\n",nbr);
printf("\n");
printf(">>");
printf("%s",recvbuffer);
printf("\n");
}
}
}
EXIT:
close(connector);
return 0;
}
Now when I compile the code and then execute it, this is the output(after I connect using the echo client):
zermacr0yd#DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7/include $ ./ES
Server listening on port 4000
New client connected....
recvmsg: Socket operation on non-socket
zermacr0yd#DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7/include $
Now I've tried connecting the client to other hosts, like www.google.com on port 80 and the output is the same. Which is:
zermacr0yd#DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7.3/include $ ./EC
Enter the remote hostname(URL/IP4 address): www.google.com
Enter the port number you wish to connect(on): 80
Connected to host: www.google.com type 'quit' to disconnect
>hi
bytes sent: 2
recv(): connection closed prematurelyzermacr0yd#DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7.3/include $
So it's clear that the connection is getting through(that is, the server receives the SYN packet)but immediately afterwards it closes the connection. So it appears to be a problem with the recv() function but it might very well be a connect() issue. But when I try to connect to the loopback address when the server isn't running I get:
zermacr0yd#DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7.3/include $ ./EC
Enter the remote hostname(URL/IP4 address): 127.0.0.1
Enter the port number you wish to connect(on): 5000
connect: Connection refused
zermacr0yd#DALEK /usr/lib/gcc/x86_64-linux-gnu/4.7.3/include $
So I'm confoozed: Is this a server side error or a client side error? I'm thinking that the recv() function on the server side fails, the server closes the connection and shuts down, and then the client doesn't know that the server isn't running until the user on the client side tries to send a message and no bytes are received. It might very well be the client disconnecting but that doesn't look likely.
This line is wrong. It is going to evaluate to TRUE or FALSE (value 0 or 1) every time.
if (csock = accept(lsock, (struct sockaddr*) & cliaddr, &len) == -1)
{
perror("accept");
exit(0);
}
Consequently when you try a socket operation on file descriptor 0 or 1 - normally stdin and stdout - you are going to get the error that you are seeing: recvmsg: Socket operation on non-socket
Change the line to this:
if ((csock = accept(lsock, (struct sockaddr*) & cliaddr, &len)) == -1)
In your orignal server code, you had:
if(csock = accept(lsock,(struct sockaddr*)&cliaddr,&len)<0){
perror("accept");
exit(0);
}
which you have now changed to:
if(csock = accept(lsock,(struct sockaddr*)&cliaddr,&len)==-1){
perror("accept");
exit(0);
}
But the problem is the same. Here, csock is probably getting the value 0, which would correspond to the standard input, and would thus not be a socket. (I assume 0, because if it becomes 1, your server would have exited early with an accept error message from perror().) This is because the < and == operators have higher precedence that =. You can fix this by adding a set of parentheses around the assignment (I stated this clearly before Duck provided his answer), or placing the assignment before the check. Since it seems you did not actually attempt either of my suggestions until you saw Duck's answer, I will illustrate the second suggestion:
csock = accept(lsock,(struct sockaddr*)&cliaddr,&len);
if (csock == -1) {
perror("accept");
exit(0);
}
Your client test is invalid since you are connecting to an HTTP port, expecting ECHO behavior. You cannot draw any conclusions other than web servers do not accept hi as input.
Your server code is not well designed. It is a single threaded iterative server, but it does not properly clean up the existing client connection before iterating to perform another blocking accept() call. Instead, the client should handle the client connection until the connection is terminated before looping back to accept(). There are alternatives (have a separate thread handle the client connection, or use event driven loop with select()/poll()/epoll()). However, given that this seems to be a learning project, just handle one connection at a time is fine. However, you need an inner loop to completely handle the client connection before you accept() again. In pseudo-code:
while (not done)
new_conn = accept()
while (new_conn != -1)
result = read(new_conn, buf, bufsize)
switch (result)
case -1:
perror("read") /* FALLTHROUGH */
case 0:
close(new_conn)
new_conn = -1
break
default:
write(new_conn, buf, result)
break
The illustration above fixes another issue with your server, in that you are writing the complete buffer on the new connection, when instead you should only be writing out the number of bytes read, which in your case is stored in the variable in. So, instead of:
if(send(csock,(void*)buffer,sizeof(buffer),0)==-1){
Do this instead:
if(send(csock,(void*)buffer,in,0)==-1){
You had an operator precedence problem in the line that calls accept(), as noted by Duck. That accounts for 'socket operation on non-socket'.
'Connection refused' occurs at the client when the server cannot be found. It therefore has nothing to do with the server code whatsoever.
In your client you need to call perror() immediately after detecting an error, not three printf()'s later. Otherwise you corrupt errno and print the wrong error.
There is no 'premature disconnect' here. Just a disconnect, and a very misleading error message of your own devising. You're reading in a loop. Somebody has to disconnect some time. If you connect that client to a real echo server, it will echo one response and it may well then close the connection.
So in addition to following Ducks suggestion and adding a receiving loop to the server code, I realized that part of the problem with the echo_client was the following lines:
while(1){
int nbr,nbs;
nbr = 0;
When I removed the line that initialized nbr(number-of bytes received) to zero, the client does not disconnect and it actually works as I intended it to.
I've been approaching network programming these days, and I wrote two simple routines to check if I got it right. So I built the server on the desktop and started it, then I built the client on the laptop and I ran it, and everything went as expected. When I tried to run them the second time and on, the server kept looping and the client after two seconds gave "Error connecting!". If I try again after fifteen minutes it works, but then I have to wait again. Where am I wrong? The computers are both connected to my LAN, 79.13.199.165 is the IP of my modem/router, which forwards every incoming connection on port 53124 to the desktop. This problem doesn't occur when running both server and client on the same PC.
server.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int main () {
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(53124);
sa.sin_addr.s_addr = htonl(INADDR_ANY);
memset(sa.sin_zero, '\0', sizeof(sa.sin_zero));
int mysocket = socket(PF_INET, SOCK_STREAM, 0);
bind(mysocket, (struct sockaddr*)&sa, sizeof(sa));
listen(mysocket, 5);
int inc;
struct sockaddr_in inc_addr;
socklen_t inc_addr_size = sizeof(inc_addr);
inc = accept(mysocket, (struct sockaddr*)&inc_addr, &inc_addr_size);
if (inc != -1) {
printf("accepting client\n");
}
send(inc, "ciao", sizeof("ciao"), 0);
close(inc);
return 0;
}
client.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int main () {
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(53124);
sa.sin_addr.s_addr = inet_addr("79.13.199.165");
memset(sa.sin_zero, '\0', sizeof(sa.sin_zero));
int mysocket = socket(PF_INET, SOCK_STREAM, 0);
if (mysocket == -1) {
printf("Could not create socket!\n");
}
if (connect(mysocket, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
printf("Error connecting!\n");
}
char message[5];
memset(message, '\0', sizeof(message));
recv(mysocket, message, 5, 0);
printf("%s\n", message);
return 0;
}
When the server closes the socket, a couple of ACK packets get sent backwards and forwards across the connection. There's no way to tell if the last ACK gets delivered successfully, so the connection goes into the TIME_WAIT state for a bit. This basically gives the TCP stack time to wait for any lost packets and throw them away.
It's possible to ignore this and reuse the socket straight away by setting SO_REUSEADDR using setsockopt(). There is a small danger that subsequent connects might get data they weren't supposed to but it shouldn't be a problem for your little test application.
Edit
By the way, one reason why you were probably getting confused by this is that you don't do any error checking on socket() bind() or listen(). The bind() call would certainly return an error and set errno, EINVAL on Linux.
I am new to network programming, and have been learning this by writing small programs that make use of the Socket API. Currently, I am writing a simple echo server, that uses fork to create a copy of it, as soon as it gets a connect request, this adds up as in improvement over the previous Iterative server (here). However, after I start the server and fire up the client, and type a message on its console, it quits unexpectedly. Running the program under Gdb shows that SIGPIPE was delivered. But as far as I know as the socket is still valid, a SIGPIPE shouldn't have occured. Any kind of help involved is appreciated.
Here is the client code
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#define MAXCOUNT 1024
int main(int argc, char* argv[])
{
int sfd,i;
struct sockaddr_in saddr;
char buff[MAXCOUNT];
char mesg[MAXCOUNT];
sfd = socket(AF_INET,SOCK_STREAM,0);
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
inet_pton(AF_INET,"127.0.0.1",&saddr.sin_addr);
saddr.sin_port = htons(5008);
connect(sfd,(struct sockaddr*) &saddr,sizeof(saddr));
fgets(buff,MAXCOUNT,stdin);
send(sfd,buff,strlen(buff),0);
if (recv(sfd,mesg,MAXCOUNT,0) == -1) {
perror("Nothing to read\n");
exit(1);
}
printf("%s\n",mesg);
exit(0);
}
Here is the server code
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#define MAXCOUNT 1024
int main(int argc, char* argv[])
{
int sfd,nsfd,cn;
pid_t c;
char buf[MAXCOUNT];
socklen_t clen;
struct sockaddr_in caddr,saddr;
sfd = socket(AF_INET,SOCK_STREAM,0);
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
saddr.sin_port = htons(5008);
bind(sfd,(struct sockaddr*) &saddr,0);
listen(sfd,2);
for (; ;) {
clen = sizeof(caddr);
nsfd = accept(sfd,(struct sockaddr*) &caddr, &clen);
if( (c = fork()) == 0) {
close(sfd);
memset(buf,0,sizeof(buf));
cn = recv(nsfd,buf,sizeof(buf),0);
if ( cn == 0) {
perror("Reading from the client socket failed\n PROGRAM CRASH :\n");
exit(1);
}
buf[cn] = '\0';
send(nsfd,buf,strlen(buf),0);
close(nsfd);
exit(0);
}
}
return 0;
}
send(sfd,buff,strlen(buff),0);
if (recv(sfd,mesg,MAXCOUNT,0) == -1) {
perror("Nothing to read\n");
exit(1);
}
printf("%s\n",mesg);
The %s format specifier is for C-style strings, not arbitrary data. And since you throw away the return value from recv, you have no way to know how many bytes you got.
Your client also doesn't shut down the socket gracefully or make sure it has received all the data the server may send. So it's possible that you're triggering an abnormal termination. The server closes the connection when it's done sending, so the client should keep trying to receive until it detects that the connection has closed.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I would like to know the simplest and most effective way to open and write data to a socket in the C programming language for network programming.
You're right, using sockets in C has a difficult syntax. Later languages like Java and Python make it a snap by comparison. The best tutorial I've found for doing socket programming in C is Beej's Guide to Network Programming. I recommend you start at the beginning to get a good overview, but if you just need to get some code working now, you can skip ahead to the section titled Client-Server Background.
Good luck!
POSIX 7 minimal runnable client server TCP example
Get two computers in a LAN, e.g. your home WiFi network.
Run the server on one computer with:
./server.out
Get the IP of the server computer with ifconfig, e.g. 192.168.0.10.
On the other computer, run:
./client.out 192.168.0.10
Now type lines on the client, and the server will return them incremented by 1 (ROT-1 cypher).
server.c
#define _XOPEN_SOURCE 700
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char **argv) {
char buffer[BUFSIZ];
char protoname[] = "tcp";
struct protoent *protoent;
int enable = 1;
int i;
int newline_found = 0;
int server_sockfd, client_sockfd;
socklen_t client_len;
ssize_t nbytes_read;
struct sockaddr_in client_address, server_address;
unsigned short server_port = 12345u;
if (argc > 1) {
server_port = strtol(argv[1], NULL, 10);
}
protoent = getprotobyname(protoname);
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
server_sockfd = socket(
AF_INET,
SOCK_STREAM,
protoent->p_proto
/* 0 */
);
if (server_sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
exit(EXIT_FAILURE);
}
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(server_port);
if (bind(
server_sockfd,
(struct sockaddr*)&server_address,
sizeof(server_address)
) == -1
) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(server_sockfd, 5) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
fprintf(stderr, "listening on port %d\n", server_port);
while (1) {
client_len = sizeof(client_address);
client_sockfd = accept(
server_sockfd,
(struct sockaddr*)&client_address,
&client_len
);
while ((nbytes_read = read(client_sockfd, buffer, BUFSIZ)) > 0) {
printf("received:\n");
write(STDOUT_FILENO, buffer, nbytes_read);
if (buffer[nbytes_read - 1] == '\n')
newline_found;
for (i = 0; i < nbytes_read - 1; i++)
buffer[i]++;
write(client_sockfd, buffer, nbytes_read);
if (newline_found)
break;
}
close(client_sockfd);
}
return EXIT_SUCCESS;
}
client.c
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char **argv) {
char buffer[BUFSIZ];
char protoname[] = "tcp";
struct protoent *protoent;
char *server_hostname = "127.0.0.1";
char *user_input = NULL;
in_addr_t in_addr;
in_addr_t server_addr;
int sockfd;
size_t getline_buffer = 0;
ssize_t nbytes_read, i, user_input_len;
struct hostent *hostent;
/* This is the struct used by INet addresses. */
struct sockaddr_in sockaddr_in;
unsigned short server_port = 12345;
if (argc > 1) {
server_hostname = argv[1];
if (argc > 2) {
server_port = strtol(argv[2], NULL, 10);
}
}
/* Get socket. */
protoent = getprotobyname(protoname);
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
/* Prepare sockaddr_in. */
hostent = gethostbyname(server_hostname);
if (hostent == NULL) {
fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
exit(EXIT_FAILURE);
}
in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
if (in_addr == (in_addr_t)-1) {
fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
exit(EXIT_FAILURE);
}
sockaddr_in.sin_addr.s_addr = in_addr;
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_port = htons(server_port);
/* Do the actual connection. */
if (connect(sockfd, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
perror("connect");
return EXIT_FAILURE;
}
while (1) {
fprintf(stderr, "enter string (empty to quit):\n");
user_input_len = getline(&user_input, &getline_buffer, stdin);
if (user_input_len == -1) {
perror("getline");
exit(EXIT_FAILURE);
}
if (user_input_len == 1) {
close(sockfd);
break;
}
if (write(sockfd, user_input, user_input_len) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
while ((nbytes_read = read(sockfd, buffer, BUFSIZ)) > 0) {
write(STDOUT_FILENO, buffer, nbytes_read);
if (buffer[nbytes_read - 1] == '\n') {
fflush(stdout);
break;
}
}
}
free(user_input);
exit(EXIT_SUCCESS);
}
On GitHub with a Makefile. Tested on Ubuntu 15.10.
Message length
The read calls on both client and server run inside while loops.
Like when reading from files, the OS may split up messages arbitrarily to make things faster, e.g. one packet may arrive much earlier than the other.
So the protocol must specify a convention of where messages stop. Common methods include:
a header with a length indicator (e.g. HTTP Content-Length)
an unique string that terminates messages. Here we use \n.
the server closes connection: HTTP allows that https://stackoverflow.com/a/25586633/895245. Limited of course since the next message requires a reconnect.
Next steps
This example is limited because:
the server can only handle one client connection at a time
communication is synchronized simply. E.g.: on a P2P chat app, the server (other person) could send messages at any time.
Solving those problems requires threading and possibly other calls like poll.
You don't mention what platform you are on, but a copy of Unix Network Programming by Stevens would be a good addition to your bookshelf. Most operating systems implement Berkley Sockets using socket, bind, connect, etc.
Unless you write a network daemon, most networking in C can be done at a higher level than using directly the sockets, by using appropriate libraries.
For instance, if you just want to retrieve a file with HTTP, use Neon or libcurl. It will be simpler, it will be at a higher level and you will have gratis SSL, IPv6, etc.
Reading and writing from basic sockets is not any harder than reading and writing normal files (just use recv instead of read and send instead if write). Things get a little trickey when you need to open a socket. The reason for that is because there are many different ways to communicate using sockets (TCP, UDP, etc).
I generally write in C++, but you can find some use in a white paper I wrote "How to Avoid the Top Ten Sockets Programming Errors" - ignore the advice to use the ACE toolkit (since it requires C++) but take note of the socket errors in the paper - they're easy to make and hard to find, especially for a beginner.
http://www.riverace.com/sockets10.htm