In a previous example we learnt about the basics of socket programming in C. In this example we shall build a basic ECHO client and server. The server/client shown here use TCP sockets or SOCK_STREAM.
Tcp sockets are connection oriented, means that they have a concept of independant connection on a certain port which one application can use at a time. The concept of connection makes TCP a "reliable" stream such that if errors occur, they can be detected and compensated for by resending the failed packets.
Lets build a very simple web server. The steps to make a webserver are as follows :
1. Create socket
2. Bind to address and port
3. Put in listening mode
4. Accept connections and process there after.
Quick example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
/*
C socket server example
*/
#include
#include
#include
#include
#include
int
main(
int
argc ,
char
*argv[])
{
int
socket_desc , client_sock , c , read_size;
struct
sockaddr_in server , client;
char
client_message[2000];
//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if
(socket_desc == -1)
{
printf
(
"Could not create socket"
);
}
puts
(
"Socket created"
);
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 8888 );
//Bind
if
( bind(socket_desc,(
struct
sockaddr *)&server ,
sizeof
(server)) < 0)
{
//print the error message
perror
(
"bind failed. Error"
);
return
1;
}
puts
(
"bind done"
);
//Listen
listen(socket_desc , 3);
//Accept and incoming connection
puts
(
"Waiting for incoming connections..."
);
c =
sizeof
(
struct
sockaddr_in);
//accept connection from an incoming client
client_sock = accept(socket_desc, (
struct
sockaddr *)&client, (socklen_t*)&c);
if
(client_sock < 0)
{
perror
(
"accept failed"
);
return
1;
}
puts
(
"Connection accepted"
);
//Receive a message from client
while
( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 )
{
//Send the message back to client
write(client_sock , client_message ,
strlen
(client_message));
}
if
(read_size == 0)
{
puts
(
"Client disconnected"
);
fflush
(stdout);
}
else
if
(read_size == -1)
{
perror
(
"recv failed"
);
}
return
0;
}
|
The above code example will start a server on localhost (127.0.0.1) port 8888
Once it receives a connection, it will read some input from the client and reply back with the same message.
To test the server run the server and then connect from another terminal using the telnet command like this
Now instead of using the telnet program as a client, why not write our own client program. Quite simple again
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
/*
C ECHO client example using sockets
*/
#include
#include
#include
#include
int
main(
int
argc ,
char
*argv[])
{
int
sock;
struct
sockaddr_in server;
char
message[1000] , server_reply[2000];
//Create socket
sock = socket(AF_INET , SOCK_STREAM , 0);
if
(sock == -1)
{
printf
(
"Could not create socket"
);
}
puts
(
"Socket created"
);
server.sin_addr.s_addr = inet_addr(
"127.0.0.1"
);
server.sin_family = AF_INET;
server.sin_port = htons( 8888 );
//Connect to remote server
if
(connect(sock , (
struct
sockaddr *)&server ,
sizeof
(server)) < 0)
{
perror
(
"connect failed. Error"
);
return
1;
}
puts
(
"Connected\n"
);
//keep communicating with server
while
(1)
{
printf
(
"Enter message : "
);
scanf
(
"%s"
, message);
//Send some data
if
( send(sock , message ,
strlen
(message) , 0) < 0)
{
puts
(
"Send failed"
);
return
1;
}
//Receive a reply from the server
if
( recv(sock , server_reply , 2000 , 0) < 0)
{
puts
(
"recv failed"
);
break
;
}
puts
(
"Server reply :"
);
puts
(server_reply);
}
close(sock);
return
0;
}
|
The above program will connect to localhost port 8888 and then ask for commands to send. Here is an example, how the output would look
Enter message : hi
Server reply :
hi
Enter message : how are you
The server in the above example has a drawback. It can handle communication with only 1 client. Thats not very useful. One way to work around this is by using threads. A thread can be assigned for each connected client which will handle communication with the client.
Code example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
/*
C socket server example, handles multiple clients using threads
*/
#include
#include
#include
#include
#include
#include
#include
//the thread function
void
*connection_handler(
void
*);
int
main(
int
argc ,
char
*argv[])
{
int
socket_desc , client_sock , c , *new_sock;
struct
sockaddr_in server , client;
//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if
(socket_desc == -1)
{
printf
(
"Could not create socket"
);
}
puts
(
"Socket created"
);
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 8888 );
//Bind
if
( bind(socket_desc,(
struct
sockaddr *)&server ,
sizeof
(server)) < 0)
{
//print the error message
perror
(
"bind failed. Error"
);
return
1;
}
puts
(
"bind done"
);
//Listen
listen(socket_desc , 3);
//Accept and incoming connection
puts
(
"Waiting for incoming connections..."
);
c =
sizeof
(
struct
sockaddr_in);
//Accept and incoming connection
puts
(
"Waiting for incoming connections..."
);
c =
sizeof
(
struct
sockaddr_in);
while
( (client_sock = accept(socket_desc, (
struct
sockaddr *)&client, (socklen_t*)&c)) )
{
puts
(
"Connection accepted"
);
pthread_t sniffer_thread;
new_sock =
malloc
(1);
*new_sock = client_sock;
if
( pthread_create( &sniffer_thread , NULL , connection_handler , (
void
*) new_sock) < 0)
{
perror
(
"could not create thread"
);
return
1;
}
//Now join the thread , so that we dont terminate before the thread
//pthread_join( sniffer_thread , NULL);
puts
(
"Handler assigned"
);
}
if
(client_sock < 0)
{
perror
(
"accept failed"
);
return
1;
}
return
0;
}
/*
* This will handle connection for each client
* */
void
*connection_handler(
void
*socket_desc)
{
//Get the socket descriptor
int
sock = *(
int
*)socket_desc;
int
read_size;
char
*message , client_message[2000];
//Send some messages to the client
message =
"Greetings! I am your connection handler\n"
;
write(sock , message ,
strlen
(message));
message =
"Now type something and i shall repeat what you type \n"
;
write(sock , message ,
strlen
(message));
//Receive a message from client
while
( (read_size = recv(sock , client_message , 2000 , 0)) > 0 )
{
//Send the message back to client
write(sock , client_message ,
strlen
(client_message));
}
if
(read_size == 0)
{
puts
(
"Client disconnected"
);
fflush
(stdout);
}
else
if
(read_size == -1)
{
perror
(
"recv failed"
);
}
//Free the socket pointer
free
(socket_desc);
return
0;
}
|
Run the above server and connect from multiple clients and it will handle all of them. There are other ways to handle multiple clients, like select, poll etc. We shall talk about them in some other article. Till then practise the above code examples and enjoy.
(http://www.binarytides.com/server-client-example-c-sockets-linux/)