xref: /aosp_15_r20/external/nanopb-c/examples/network_server/server.c (revision c8d645cafcee3f91213d30caa0fe303887010b9b)
1*c8d645caSAndroid Build Coastguard Worker /* This is a simple TCP server that listens on port 1234 and provides lists
2*c8d645caSAndroid Build Coastguard Worker  * of files to clients, using a protocol defined in file_server.proto.
3*c8d645caSAndroid Build Coastguard Worker  *
4*c8d645caSAndroid Build Coastguard Worker  * It directly deserializes and serializes messages from network, minimizing
5*c8d645caSAndroid Build Coastguard Worker  * memory use.
6*c8d645caSAndroid Build Coastguard Worker  *
7*c8d645caSAndroid Build Coastguard Worker  * For flexibility, this example is implemented using posix api.
8*c8d645caSAndroid Build Coastguard Worker  * In a real embedded system you would typically use some other kind of
9*c8d645caSAndroid Build Coastguard Worker  * a communication and filesystem layer.
10*c8d645caSAndroid Build Coastguard Worker  */
11*c8d645caSAndroid Build Coastguard Worker 
12*c8d645caSAndroid Build Coastguard Worker #include <sys/socket.h>
13*c8d645caSAndroid Build Coastguard Worker #include <sys/types.h>
14*c8d645caSAndroid Build Coastguard Worker #include <netinet/in.h>
15*c8d645caSAndroid Build Coastguard Worker #include <unistd.h>
16*c8d645caSAndroid Build Coastguard Worker #include <dirent.h>
17*c8d645caSAndroid Build Coastguard Worker #include <stdio.h>
18*c8d645caSAndroid Build Coastguard Worker #include <string.h>
19*c8d645caSAndroid Build Coastguard Worker 
20*c8d645caSAndroid Build Coastguard Worker #include <pb_encode.h>
21*c8d645caSAndroid Build Coastguard Worker #include <pb_decode.h>
22*c8d645caSAndroid Build Coastguard Worker 
23*c8d645caSAndroid Build Coastguard Worker #include "fileproto.pb.h"
24*c8d645caSAndroid Build Coastguard Worker #include "common.h"
25*c8d645caSAndroid Build Coastguard Worker 
26*c8d645caSAndroid Build Coastguard Worker /* This callback function will be called once during the encoding.
27*c8d645caSAndroid Build Coastguard Worker  * It will write out any number of FileInfo entries, without consuming unnecessary memory.
28*c8d645caSAndroid Build Coastguard Worker  * This is accomplished by fetching the filenames one at a time and encoding them
29*c8d645caSAndroid Build Coastguard Worker  * immediately.
30*c8d645caSAndroid Build Coastguard Worker  */
listdir_callback(pb_ostream_t * stream,const pb_field_t * field,void * const * arg)31*c8d645caSAndroid Build Coastguard Worker bool listdir_callback(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
32*c8d645caSAndroid Build Coastguard Worker {
33*c8d645caSAndroid Build Coastguard Worker     DIR *dir = (DIR*) *arg;
34*c8d645caSAndroid Build Coastguard Worker     struct dirent *file;
35*c8d645caSAndroid Build Coastguard Worker     FileInfo fileinfo = {};
36*c8d645caSAndroid Build Coastguard Worker 
37*c8d645caSAndroid Build Coastguard Worker     while ((file = readdir(dir)) != NULL)
38*c8d645caSAndroid Build Coastguard Worker     {
39*c8d645caSAndroid Build Coastguard Worker         fileinfo.inode = file->d_ino;
40*c8d645caSAndroid Build Coastguard Worker         strncpy(fileinfo.name, file->d_name, sizeof(fileinfo.name));
41*c8d645caSAndroid Build Coastguard Worker         fileinfo.name[sizeof(fileinfo.name) - 1] = '\0';
42*c8d645caSAndroid Build Coastguard Worker 
43*c8d645caSAndroid Build Coastguard Worker         /* This encodes the header for the field, based on the constant info
44*c8d645caSAndroid Build Coastguard Worker          * from pb_field_t. */
45*c8d645caSAndroid Build Coastguard Worker         if (!pb_encode_tag_for_field(stream, field))
46*c8d645caSAndroid Build Coastguard Worker             return false;
47*c8d645caSAndroid Build Coastguard Worker 
48*c8d645caSAndroid Build Coastguard Worker         /* This encodes the data for the field, based on our FileInfo structure. */
49*c8d645caSAndroid Build Coastguard Worker         if (!pb_encode_submessage(stream, FileInfo_fields, &fileinfo))
50*c8d645caSAndroid Build Coastguard Worker             return false;
51*c8d645caSAndroid Build Coastguard Worker     }
52*c8d645caSAndroid Build Coastguard Worker 
53*c8d645caSAndroid Build Coastguard Worker     /* Because the main program uses pb_encode_delimited(), this callback will be
54*c8d645caSAndroid Build Coastguard Worker      * called twice. Rewind the directory for the next call. */
55*c8d645caSAndroid Build Coastguard Worker     rewinddir(dir);
56*c8d645caSAndroid Build Coastguard Worker 
57*c8d645caSAndroid Build Coastguard Worker     return true;
58*c8d645caSAndroid Build Coastguard Worker }
59*c8d645caSAndroid Build Coastguard Worker 
60*c8d645caSAndroid Build Coastguard Worker /* Handle one arriving client connection.
61*c8d645caSAndroid Build Coastguard Worker  * Clients are expected to send a ListFilesRequest, terminated by a '0'.
62*c8d645caSAndroid Build Coastguard Worker  * Server will respond with a ListFilesResponse message.
63*c8d645caSAndroid Build Coastguard Worker  */
handle_connection(int connfd)64*c8d645caSAndroid Build Coastguard Worker void handle_connection(int connfd)
65*c8d645caSAndroid Build Coastguard Worker {
66*c8d645caSAndroid Build Coastguard Worker     DIR *directory = NULL;
67*c8d645caSAndroid Build Coastguard Worker 
68*c8d645caSAndroid Build Coastguard Worker     /* Decode the message from the client and open the requested directory. */
69*c8d645caSAndroid Build Coastguard Worker     {
70*c8d645caSAndroid Build Coastguard Worker         ListFilesRequest request = {};
71*c8d645caSAndroid Build Coastguard Worker         pb_istream_t input = pb_istream_from_socket(connfd);
72*c8d645caSAndroid Build Coastguard Worker 
73*c8d645caSAndroid Build Coastguard Worker         if (!pb_decode_delimited(&input, ListFilesRequest_fields, &request))
74*c8d645caSAndroid Build Coastguard Worker         {
75*c8d645caSAndroid Build Coastguard Worker             printf("Decode failed: %s\n", PB_GET_ERROR(&input));
76*c8d645caSAndroid Build Coastguard Worker             return;
77*c8d645caSAndroid Build Coastguard Worker         }
78*c8d645caSAndroid Build Coastguard Worker 
79*c8d645caSAndroid Build Coastguard Worker         directory = opendir(request.path);
80*c8d645caSAndroid Build Coastguard Worker         printf("Listing directory: %s\n", request.path);
81*c8d645caSAndroid Build Coastguard Worker     }
82*c8d645caSAndroid Build Coastguard Worker 
83*c8d645caSAndroid Build Coastguard Worker     /* List the files in the directory and transmit the response to client */
84*c8d645caSAndroid Build Coastguard Worker     {
85*c8d645caSAndroid Build Coastguard Worker         ListFilesResponse response = {};
86*c8d645caSAndroid Build Coastguard Worker         pb_ostream_t output = pb_ostream_from_socket(connfd);
87*c8d645caSAndroid Build Coastguard Worker 
88*c8d645caSAndroid Build Coastguard Worker         if (directory == NULL)
89*c8d645caSAndroid Build Coastguard Worker         {
90*c8d645caSAndroid Build Coastguard Worker             perror("opendir");
91*c8d645caSAndroid Build Coastguard Worker 
92*c8d645caSAndroid Build Coastguard Worker             /* Directory was not found, transmit error status */
93*c8d645caSAndroid Build Coastguard Worker             response.has_path_error = true;
94*c8d645caSAndroid Build Coastguard Worker             response.path_error = true;
95*c8d645caSAndroid Build Coastguard Worker             response.file.funcs.encode = NULL;
96*c8d645caSAndroid Build Coastguard Worker         }
97*c8d645caSAndroid Build Coastguard Worker         else
98*c8d645caSAndroid Build Coastguard Worker         {
99*c8d645caSAndroid Build Coastguard Worker             /* Directory was found, transmit filenames */
100*c8d645caSAndroid Build Coastguard Worker             response.has_path_error = false;
101*c8d645caSAndroid Build Coastguard Worker             response.file.funcs.encode = &listdir_callback;
102*c8d645caSAndroid Build Coastguard Worker             response.file.arg = directory;
103*c8d645caSAndroid Build Coastguard Worker         }
104*c8d645caSAndroid Build Coastguard Worker 
105*c8d645caSAndroid Build Coastguard Worker         if (!pb_encode_delimited(&output, ListFilesResponse_fields, &response))
106*c8d645caSAndroid Build Coastguard Worker         {
107*c8d645caSAndroid Build Coastguard Worker             printf("Encoding failed: %s\n", PB_GET_ERROR(&output));
108*c8d645caSAndroid Build Coastguard Worker         }
109*c8d645caSAndroid Build Coastguard Worker     }
110*c8d645caSAndroid Build Coastguard Worker 
111*c8d645caSAndroid Build Coastguard Worker     if (directory != NULL)
112*c8d645caSAndroid Build Coastguard Worker         closedir(directory);
113*c8d645caSAndroid Build Coastguard Worker }
114*c8d645caSAndroid Build Coastguard Worker 
main(int argc,char ** argv)115*c8d645caSAndroid Build Coastguard Worker int main(int argc, char **argv)
116*c8d645caSAndroid Build Coastguard Worker {
117*c8d645caSAndroid Build Coastguard Worker     int listenfd, connfd;
118*c8d645caSAndroid Build Coastguard Worker     struct sockaddr_in servaddr;
119*c8d645caSAndroid Build Coastguard Worker     int reuse = 1;
120*c8d645caSAndroid Build Coastguard Worker 
121*c8d645caSAndroid Build Coastguard Worker     /* Listen on localhost:1234 for TCP connections */
122*c8d645caSAndroid Build Coastguard Worker     listenfd = socket(AF_INET, SOCK_STREAM, 0);
123*c8d645caSAndroid Build Coastguard Worker     setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
124*c8d645caSAndroid Build Coastguard Worker 
125*c8d645caSAndroid Build Coastguard Worker     memset(&servaddr, 0, sizeof(servaddr));
126*c8d645caSAndroid Build Coastguard Worker     servaddr.sin_family = AF_INET;
127*c8d645caSAndroid Build Coastguard Worker     servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
128*c8d645caSAndroid Build Coastguard Worker     servaddr.sin_port = htons(1234);
129*c8d645caSAndroid Build Coastguard Worker     if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0)
130*c8d645caSAndroid Build Coastguard Worker     {
131*c8d645caSAndroid Build Coastguard Worker         perror("bind");
132*c8d645caSAndroid Build Coastguard Worker         return 1;
133*c8d645caSAndroid Build Coastguard Worker     }
134*c8d645caSAndroid Build Coastguard Worker 
135*c8d645caSAndroid Build Coastguard Worker     if (listen(listenfd, 5) != 0)
136*c8d645caSAndroid Build Coastguard Worker     {
137*c8d645caSAndroid Build Coastguard Worker         perror("listen");
138*c8d645caSAndroid Build Coastguard Worker         return 1;
139*c8d645caSAndroid Build Coastguard Worker     }
140*c8d645caSAndroid Build Coastguard Worker 
141*c8d645caSAndroid Build Coastguard Worker     for(;;)
142*c8d645caSAndroid Build Coastguard Worker     {
143*c8d645caSAndroid Build Coastguard Worker         /* Wait for a client */
144*c8d645caSAndroid Build Coastguard Worker         connfd = accept(listenfd, NULL, NULL);
145*c8d645caSAndroid Build Coastguard Worker 
146*c8d645caSAndroid Build Coastguard Worker         if (connfd < 0)
147*c8d645caSAndroid Build Coastguard Worker         {
148*c8d645caSAndroid Build Coastguard Worker             perror("accept");
149*c8d645caSAndroid Build Coastguard Worker             return 1;
150*c8d645caSAndroid Build Coastguard Worker         }
151*c8d645caSAndroid Build Coastguard Worker 
152*c8d645caSAndroid Build Coastguard Worker         printf("Got connection.\n");
153*c8d645caSAndroid Build Coastguard Worker 
154*c8d645caSAndroid Build Coastguard Worker         handle_connection(connfd);
155*c8d645caSAndroid Build Coastguard Worker 
156*c8d645caSAndroid Build Coastguard Worker         printf("Closing connection.\n");
157*c8d645caSAndroid Build Coastguard Worker 
158*c8d645caSAndroid Build Coastguard Worker         close(connfd);
159*c8d645caSAndroid Build Coastguard Worker     }
160*c8d645caSAndroid Build Coastguard Worker 
161*c8d645caSAndroid Build Coastguard Worker     return 0;
162*c8d645caSAndroid Build Coastguard Worker }
163