张东轩的博客

合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。

0%

Socket Programming in C/C++

What is socket programming?

Socket programming is a way of connecting two nodes on a network to communicate with each other. One socket(node) listens on a particular port at an IP, while the other socket reaches out to the other to form a connection. The server forms the listener socket while the client reaches out to the server.

State diagram for server and client model

Stages for server

1、socket creation:

int sockfd = socket(domain, type, protocol)]

sockfd: socket descriptor, an integer (like a file-handle)
1. domain: integer, specifies communication domain. We use AF_LOCAL as defined in the POSIX standard for communication between processes on the same host(most infomation see https://blog.csdn.net/frank_jb/article/details/77199834). For communicating between processes on different hosts connected by IPV4, we use AF_INET and AF_I NET 6 for processes connected by IPV6.
2. type: communication type
SOCK_STREAM: TCP(reliable, connection oriented)
SOCK_DGRAM: UDP(unreliable, connectionless)
3. protocol: Protocol value for Internet Protocol(IP), which is 0. This is the same number which appears on protocol field in the IP header of a packet.(man protocols for more details)

2、setsockopt: This helps in manipulating options for the socket referred by the file descriptor sockfd. This is completely optional, but it helps in reuse of address and port. Prevents error such as: “address already in use”.

int setsockopt(int sockfd, int level, int optname,  const void *optval, socklen_t optlen);

3、Bind:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

After creation of the socket, bind function binds the socket to the address and port number specified in addr(custom data structure). In the example code, we bind the server to the localhost, hence we use INADDR_ANY to specify the IP address.

4、Listen:

int listen(int sockfd, int backlog);

It puts the server socket in a passive mode, where it waits for the client to approach the server to make a connection. The backlog, defines the maximum length to which the queue of pending connections for sockfd may grow. If a connection request arrives when the queue is full, the client may receive an error with an indication of ECONNREFUSED.

5、Accept:

int new_socket= accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket. At this point, connection is established between client and server, and they are ready to transfer data.

Stages for Client

1、Socket connection: Exactly same as that of server’s socket creation
Connect:

2、The connect() system call connects the socket referred to by the file descriptor sockfd to the address specified by addr. Server’s address
and port is specified in addr.

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Implementation

Here we are exchanging one hello message between server and client to demonstrate the client/server model.

Server.c

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
//
// Client.c
// ServerTest
//
// Created by disen zhang on 2022/6/8.
//

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

static int g_svfd;

long send_data_to_socket(int socket) {
char buf[1024] = {0};
const size_t buf_size = sizeof(buf) - 1;
snprintf(buf, buf_size, "Server Have Revc Your Connect, Bye.");
long sendLen = send(socket, buf, strlen(buf), 0);
if (sendLen == 0) {
printf("Send Failed\n");
}

return sendLen;
}


void read_data_from_socket(int sockfd) {
char recv_data[1024] = {0};
long rc = recv(sockfd, recv_data, sizeof(recv_data), 0);
if (rc <= 0) {
printf("Server Read Failed cs:%ld\n", rc);
return;
}

printf("Server Revc:%s from sockfds:%d\n", recv_data, sockfd);
}


void exit_server(void) {
shutdown(g_svfd, SHUT_RDWR);
close(g_svfd);
printf("close server socket:%d\n", g_svfd);
}


int start_server(int port) {
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);


// Creating socket file descriptor
if ((g_svfd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
printf("socket failed\n");
return -1;
}

// Forcefully attaching socket to the port 8080
int opt = 1;
int ret = setsockopt(g_svfd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
if (ret != 0) {
perror("setsockopt");
close(g_svfd);
return -2;
}

// Forcefully attaching socket to the port 8080
if (bind(g_svfd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("bind");
close(g_svfd);
return -3;
}

if (listen(g_svfd, SOMAXCONN) < 0) {
perror("listen");
close(g_svfd);
return -4;
}

printf("server start with port:%d fd:%d\n", port, g_svfd);

return g_svfd;
}

int accept_client_conn(void) {
int sockfd = accept(g_svfd, NULL, NULL);
if (sockfd < 0) {
perror("accept");
return -1;
}

read_data_from_socket(sockfd);
send_data_to_socket(sockfd);

shutdown(sockfd, SHUT_RDWR);
close(sockfd);

return sockfd;
}

main.c for Server

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
#include <stdio.h>
#include <stdlib.h>
#include "Server.h"

void on_exit(void)
{
//do something when process exits
exit_server();
printf("on_exit\n");
}

int main(int argc, char * argv[]) {
if (argc != 2) {
printf("please add port\n");
return 0;
}

atexit(on_exit);

int port = atoi(argv[1]);
printf("port:%d\n", port);
int ret = start_server(port);
if (ret < 0) {
printf("server start fail\n");
return 0;
}

accept_client_conn();

return 0;
}

client.c

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
//
// Client.c
// ServerTest
//
// Created by disen zhang on 2022/6/8.
//

#include "Client.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static int g_socket;

void client_read_data_from_socket(int sockfd) {
char recv_data[1024] = {0};
long rc = recv(sockfd, recv_data, sizeof(recv_data), 0);
if (rc <= 0) {
printf("Client Read Failed cs:%ld\n", rc);
return;
}

printf("Client Socket acc:%d revc:%s\n", sockfd, recv_data);
}

long send_data_from_socket(int socket) {
char buf[1024] = {0};
const size_t buf_size = sizeof(buf) - 1;
snprintf(buf, buf_size, "Client Send Data From Socket:%d", socket);
long sendLen = send(socket, buf, strlen(buf), 0);
if (sendLen == 0) {
printf("Send Failed\n");
}

return sendLen;
}


void close_connect() {
shutdown(g_socket, SHUT_RDWR);
close(g_socket);
printf("close server socket:%d\n", g_socket);
}

int start_connect(const char *ip, int port) {
g_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (g_socket < 0) {
printf("Socket:%d Create Error\n", g_socket);
return -1;
}

int r = 1;
setsockopt(g_socket, SOL_SOCKET, SO_NOSIGPIPE, &r, sizeof(int));

// int timeoutMs = 100000;
// __darwin_time_t tv_sec = timeoutMs / 1000;
// __darwin_suseconds_t tv_usec = (timeoutMs % 1000) * 1000;
// struct timeval tv = {tv_sec, tv_usec};
// setsockopt(g_socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
// setsockopt(g_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);

struct in_addr addr;
addr.s_addr = 0;
inet_pton(AF_INET, ip, &addr);

struct sockaddr_in to = {0};
to.sin_addr.s_addr = addr.s_addr;
to.sin_port = htons(port);
to.sin_family = AF_INET;

long rc = connect(g_socket, (struct sockaddr*)&to,sizeof(to));
if (rc < 0) {
printf("Socket:%d Connect Failed\n", g_socket);
return -1;
}


send_data_from_socket(g_socket);
client_read_data_from_socket(g_socket);

return g_socket;
}

the main.c for Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include "Client.h"

void on_exit(void) {
//do something when process exits
close_connect();
}

int main(int argc, char * argv[]) {
if (argc != 2) {
printf("please add port\n");
return 0;
}

atexit(on_exit);
int port = atoi(argv[1]);
printf("port:%d\n", port);

start_connect("127.0.0.1", port);

return 0;
}

Source Code : https://github.com/zhangdongxuan/CodeSlice/tree/master/network/SampleSocket

Server:

cmake CMakeLists.txt 
make 
./SocketServer 8080

Client:

cmake CMakeLists.txt 
make 
./SocketClient 8080

Server Output:

port:8080
server start with port:8080 fd:3
Server Revc:Client Send Data From Socket:3 from sockfds:4
close server socket:3
on_exit

Client Output:

port:8080
Client Socket acc:3 revc:Server Have Revc Your Connect, Bye.
close server socket:3

https://www.ibm.com/docs/en/i/7.4?topic=designs-using-poll-instead-select