Your College Professor asked you to prepare a basic client-server program for the networks lab next week or maybe you are yourself starting Socket programming, what's better than writing the first program and learning along that.
The quick theory
what is a socket ?
well, its (an integer and) a file descriptor through which we do our desired communication.
what does communication looks like ? (important)
Server - Bind()s
socket to an address and port, this socket then listen()s
for incoming, then accept()s
the incoming request (from client).
Client - Connect()
s your socket via local address and port for a listen()
ing socket to accept()
.
what happens after connect() and accept() ?
accept()
returns a new socket to be used for communicating to that particular accepted client.
In client side, the same socket used to connect
is used to communicate.
now you can send()
and recv()
from both sides.
What is the first thing that you need? Headers!
We will be using the following
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
Lets build both sides one-by-one
Server.c
int main() {
int sockID1 = socket(PF_INET, SOCK_STREAM, 0);
A stream socket has been created in PF_INET(internet) domain.
Now, we have to bind our socket to an address and port. But, the bind()
function asks for one struct (of sockaddr
type) that has all that info. Instead we pass it sockaddr_in
casted to sockaddr
because it is more specific to our needs.
struct sockaddr_in addrPort1;
addrPort1.sin_family = AF_INET; // sets IPv4
addrPort1.sin_addr.s_addr = htonl(INADDR_ANY);
addrPort1.sin_port = htons(5200);
//htonl - host to network long, htons - host to network short
We filled the structure. Here, INADDR_ANY
automatically fills a default Ip address since we don't want to bind it to particular one.
5200 is the port number that we will be using. You can use your own (make sure it is not reserved) (use the same in client).
if(bind(sockID1, (struct sockaddr *) &addrPort1, sizeof(addrPort1))==-1) {
printf("\nBind Failed to Port 5200\n\n");
}
else printf("\nBind Success to Port 5200\n\n");
Binded the socket, returns -1 if fails.
if(listen(sockID1, 10)) {
printf("Listen Failed\n\n");
}
else printf("Listening...\n\n");
Now we are ready to accept an incoming request (connect()
from client).
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int sockID2 = accept(sockID1, (struct sockaddr *) &client_addr, &len);
printf("Accepted a connection\n");
This is interesting. The only thing accept()
needs from our server is sockID1
. client_addr
will contain info about the client it accepts and will be filled on function call.
This returns a new socket (sockID2
) for us which will be used for sending and recieving from the client.
The last step.
char msg[50];
int countR, countS;
while(strcmp(msg,"Close\n")) {
printf("To Client : ");
fgets(msg, 50, stdin);
countS = send(sockID2, msg, 50, 0);
if(!strcmp(msg,"Close\n")) {
break;
}
printf("From Client : ");
countR = recv(sockID2, msg, 50, 0);
printf("%s", msg);
}
Try to understand this yourself. countR
and countS
are there for error handling (send
and recv
give -1 on error). Sending or recieving the word "Close" is the exit condition.
What are we trying to do? First message to be sent by server. Client then recieves it and sends something. Server then recieves it and sends ...........
Finally
close(sockID1);
close(sockID2);
printf("\nSocket connection Closed\n");
return 0;
}
Without scratching heads, lets move to client.c
Client.c
After the headers
int main() {
int sockID1 = socket(PF_INET, SOCK_STREAM, 0);
struct sockaddr_in addrPort1;
addrPort1.sin_family = AF_INET; //sets to IPv4
addrPort1.sin_addr.s_addr = htonl(INADDR_ANY);
addrPort1.sin_port = htons(5200);
//htonl - host to network long, htons - host to network short
This is same, because connection also requires the specifics of where and via-what to connect.
if(connect(sockID1, (struct sockaddr *) &addrPort1, sizeof(addrPort1))) {
printf("\nConnect Failed\n\n");
}
else printf("\nConnected via Port 5200\n\n");
Nearly identical to bind()
. Right. But remember, it complements the accept()
function in the server side.
char msg[50];
int countR, countS;
while(strcmp(msg,"Close\n")) {
printf("From Server : ");
countR = recv(sockID1, msg, 50, 0);
printf("%s", msg);
if(!strcmp(msg,"Close\n")) {
break;
}
printf("To Server : ");
fgets(msg, 50, stdin);
countS = send(sockID1, msg, 50, 0);
}
I hope its easier to see the logic now.
close(sockID1);
printf("\nSocket connection Closed\n");
return 0;
}
Done.
Move to the directory. Split your terminal. In one, run the server, then on other run the client. Happy connection !
I have tried to keep it as short as possible and only explain the things relevant to a first program.
Questions & Suggestions are welcome.
Top comments (0)