Skip to content

Instantly share code, notes, and snippets.

@Lhy121125
Last active December 25, 2022 20:05
Show Gist options
  • Save Lhy121125/ad2d22075f935bee7039ec16e98d648f to your computer and use it in GitHub Desktop.
Save Lhy121125/ad2d22075f935bee7039ec16e98d648f to your computer and use it in GitHub Desktop.
Looking up the Keyword From Multiple Machines to the Server
#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