Last active
December 25, 2022 20:05
-
-
Save Lhy121125/ad2d22075f935bee7039ec16e98d648f to your computer and use it in GitHub Desktop.
Looking up the Keyword From Multiple Machines to the Server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <mylist.h> | |
#include "mdb.h" | |
//#include "mdb.c" | |
#define KeyMax 5 | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <arpa/inet.h> | |
#include <linux/limits.h> | |
#include <netdb.h> | |
#include <unistd.h> | |
#include <signal.h> | |
#include <sys/stat.h> | |
#include <sys/wait.h> | |
#include <time.h> | |
static void die(const char *message) | |
{ | |
perror(message); | |
exit(1); | |
} | |
int loadmdb(FILE *fp, struct List *dest) | |
{ | |
/* | |
* read all records into memory | |
*/ | |
struct MdbRec r; | |
struct Node *node = NULL; | |
int count = 0; | |
while (fread(&r, sizeof(r), 1, fp) == 1) { | |
// allocate memory for a new record and copy into it the one | |
// that was just read from the database. | |
struct MdbRec *rec = (struct MdbRec *)malloc(sizeof(r)); | |
if (!rec) | |
return -1; | |
memcpy(rec, &r, sizeof(r)); | |
// add the record to the linked list. | |
node = addAfter(dest, node, rec); | |
if (node == NULL) | |
return -1; | |
count++; | |
} | |
// see if fread() produced error | |
if (ferror(fp)) | |
return -1; | |
return count; | |
} | |
void freemdb(struct List *list) | |
{ | |
// free all the records | |
traverseList(list, &free); | |
removeAllNodes(list); | |
} | |
static void sigchld_handler(int sig) | |
{ | |
// Keep reaping dead children until there aren't any to reap. | |
while (waitpid(-1, NULL, WNOHANG) > 0) | |
; | |
} | |
int main(int argc, char **argv) | |
{ | |
/* | |
* Configure signal-handling. | |
*/ | |
struct sigaction sa; | |
memset(&sa, 0, sizeof(sa)); | |
// Ignore SIGPIPE so that we don't terminate when we call | |
// send() on a disconnected socket. | |
sigemptyset(&sa.sa_mask); | |
sa.sa_flags = 0; | |
sa.sa_handler = SIG_IGN; | |
if (sigaction(SIGPIPE, &sa, NULL)) | |
die("sigaction(SIGPIPE)"); | |
// Install a handler for the SIGCHLD signal so that we can reap children | |
// who have finished processing their requests. | |
sigemptyset(&sa.sa_mask); | |
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; | |
sa.sa_handler = &sigchld_handler; | |
if (sigaction(SIGCHLD, &sa, NULL)) | |
die("sigaction(SIGCHLD)"); | |
/* | |
* open the database file specified in the command line | |
*/ | |
if (argc != 3) { | |
fprintf(stderr, "%s\n", "usage: ./mdb-lookup-server <server-port> <database>"); | |
exit(1); | |
} | |
// char *filename = argv[2]; | |
// FILE *fp = fopen(filename, "rb"); | |
// if (fp == NULL) | |
// die(filename); | |
/* | |
* read all records into memory | |
*/ | |
// struct List list; | |
// initList(&list); | |
// int loaded = loadmdb(fp, &list); | |
// if (loaded < 0) | |
// die("loadmdb"); | |
// fclose(fp); | |
char *server_port = argv[1]; | |
struct addrinfo hints; | |
memset(&hints, 0, sizeof(hints)); | |
hints.ai_family = AF_INET; // Only accept IPv4 addresses | |
hints.ai_socktype = SOCK_STREAM; // stream socket for TCP connections | |
hints.ai_protocol = IPPROTO_TCP; // TCP protocol | |
hints.ai_flags = AI_PASSIVE; // Construct socket address for bind()ing | |
// Define where getaddrinfo() will return the information it found. | |
struct addrinfo *info; | |
// Call getaddrinfo(), specifying the server IP address and port as strings. | |
// getaddrinfo() will parse those for us and point info to the result. | |
int addr_err; | |
if ((addr_err = getaddrinfo(NULL, server_port, &hints, &info)) != 0) { | |
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(addr_err)); | |
exit(1); | |
} | |
// Create socket() according to the address family, socket type, and | |
// protocol of the address info. Since we specified AF_INET, SOCK_STREAM, | |
// and IPPROTO_TCP in the hints, this should be equivalent to just calling | |
// socket(AF_INET, SOCK_STREAM, IPPROTO_TCP). | |
int serv_fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol); | |
if (serv_fd < 0) | |
die("socket"); | |
// bind() socket to a port on the server; the port in info->ai_addr should | |
// be the same port that getaddrinfo() parsed from server_port. | |
if (bind(serv_fd, info->ai_addr, info->ai_addrlen) < 0) | |
die("bind"); | |
// BTW: we're done with the info retrieved by getaddrinfo(), so free it. | |
freeaddrinfo(info); | |
// Start listen()ing for connections on this socket, maintaining a queue of | |
// at most 8 pending connections. | |
if (listen(serv_fd, 8) < 0) | |
die("listen"); | |
for(;;){ | |
/* | |
* read all records into memory | |
*/ | |
char *filename = argv[2]; | |
FILE *fp = fopen(filename, "rb"); | |
if (fp == NULL) | |
die(filename); | |
struct List list; | |
initList(&list); | |
int loaded = loadmdb(fp, &list); | |
if (loaded < 0) | |
die("loadmdb"); | |
fclose(fp); | |
/* | |
* accept() connection from client. | |
*/ | |
// Define space to receive client address info. | |
struct sockaddr_in clnt_addr; | |
socklen_t clnt_addr_len = sizeof(clnt_addr); | |
// accept() blocks until a client connects with the server, and returns | |
// a NEW socket file descriptor for interacting with the client. | |
int clnt_fd = accept(serv_fd, (struct sockaddr *) &clnt_addr, | |
&clnt_addr_len); | |
if (clnt_fd < 0) | |
die("accept"); | |
// clnt_addr is now populated with information about the client. | |
fprintf(stderr, "Connection started: %s\n", | |
inet_ntoa(clnt_addr.sin_addr)); | |
pid_t pid = fork(); | |
if (pid < 0) | |
die("fork"); | |
if (pid > 0) { | |
/* | |
* Parent process: | |
* | |
* Close client socket and continue accept()ing connections. | |
*/ | |
close(clnt_fd); | |
continue; | |
} | |
/* | |
* Child process: | |
* | |
* Close server socket, handle the request, and exit. | |
*/ | |
close(serv_fd); | |
/* | |
* Handle client connection. | |
*/ | |
// Keep track of how many bytes we've received. | |
// size_t recv_len = 0; | |
// int len; | |
char line[4096]; | |
char key[KeyMax + 1]; | |
FILE *clnt_r = fdopen(clnt_fd,"rb"); | |
if(clnt_r == NULL) die("fdopen"); | |
// FILE *clnt_w = fdopen(clnt_fd,"wb"); | |
// if(clnt_w == NULL) die("fdopen"); | |
// recv() is like read(), except there's an extra flags argument. | |
// | |
// Since we didn't pass any flags to recv(), this is equivalent to | |
// read(clnt_fd, buf, sizeof(buf)). | |
// if ((len = recv(clnt_fd, line, sizeof(line), 0)) < 0) | |
// die("recv"); | |
//Look UP LOOP | |
// while (recv(clnt_fd,line,sizeof(line),0) > 0) { | |
while(fgets(line,sizeof(line),clnt_r)!=NULL){ | |
/* | |
* clean up user input | |
*/ | |
// must null-terminate the string manually after strncpy(). | |
strncpy(key, line, sizeof(key) - 1); | |
key[sizeof(key) - 1] = '\0'; | |
// if newline is within the first KeyMax characters, remove it. | |
size_t last = strlen(key) - 1; | |
// if (key[last] == '\n') | |
// key[last] = '\0'; | |
if(strstr(key,"\r\n")){ | |
key[last] = '\0'; | |
key[last-1] = '\0'; | |
} | |
if(key[last]=='\n'|| key[last]=='\r') key[last]='\0'; | |
// user might have typed more than sizeof(line) - 1 characters in line; | |
// continue fgets()ing until we encounter a newline. | |
while (line[strlen(line) - 1] != '\n' && recv(clnt_fd,line,sizeof(line),0)) | |
; | |
// fprintf(stderr,"Line 1: %s\n",line); | |
// fprintf(stderr,"Key 1: %s\n",key); | |
/* | |
* search with key | |
*/ | |
// traverse the list, printing out the matching records | |
struct Node *node = list.head; | |
int recNo = 1; | |
while (node) { | |
struct MdbRec *rec = (struct MdbRec *)node->data; | |
if (strstr(rec->name, key) || strstr(rec->msg, key)){ | |
char combined[1024]; | |
int combined_len = sprintf(combined,"%4d: {%s} said {%s}\n", recNo, rec->name, rec->msg); | |
//sprintf("%4d: {%s} said {%s}\n", recNo, rec->name, rec->msg); | |
if(send(clnt_fd,combined,combined_len,0)<0) | |
die("send"); | |
// if(fwrite(combined,combined_len,1,clnt_w)<0)die("fwrite"); | |
} | |
node = node->next; | |
recNo++; | |
} | |
char* space = "\n"; | |
if(send(clnt_fd,space,1,0)<0) | |
die("send"); | |
// fflush(stdout); | |
// if ((len = recv(clnt_fd, line, sizeof(line), 0)) < 0) | |
// die("recv"); | |
// fprintf(stderr,"Line: %s\n",line); | |
// fprintf(stderr,"Key: %s\n------------\n",key); | |
// fprintf(stderr, "Received (and sent) %d bytes in total\n",len); | |
} | |
// see if fgets() produced error | |
if (ferror(stdin)) | |
die("stdin"); | |
/* | |
* clean up and quit | |
*/ | |
freemdb(&list); | |
// fprintf(stderr, "Received (and sent) %d bytes in total\n",len); | |
fclose(clnt_r); | |
// Close client connection. | |
close(clnt_fd); | |
fprintf(stderr, "Connection terminated: %s\n",inet_ntoa(clnt_addr.sin_addr)); | |
exit(0); | |
} | |
/* | |
* UNREACHABLE | |
*/ | |
// Theoretically, if we want our server to handle graceful termination, we | |
// should also close() the server socket here too: | |
close(serv_fd); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment