[Network] Implementing Group Chatting Server & File Server

pos++·2024년 5월 26일

Network

목록 보기
3/3
post-thumbnail

2023.11.20 TIL

Group Chatting Server

  • Client는 nc로 대신
  • Server program 실행 후 client 접속
  • Client가 접속 시 server는 welcome message 전송
  • 한 client에서 message 전송 시 다른 client 모두 출력
  • Server는 각 client가 접속시 해당 정보를 배열에 저장
  • select() 함수를 사용하여 여러개의 socket을 감시
  • 하나의 socket에서 입력이 들어오면 client 배열에 저장된 다른 socket으로 그 내용을 그대로 출력

serverorg.c를 수정해서 구현

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

int main(int argc, char *argv[])
{
	struct sockaddr_in server, remote;
	int request_sock, new_sock;
	int i, nfound, fd, maxfd, bytesread, addrlen;
	fd_set rmask, mask;
	static struct timeval timeout = {5, 0}; /* 5 seconds */

	char buf[BUFSIZ];
	if (argc != 2)
	{
		(void)fprintf(stderr, "usage: %s port\n", argv[0]);
		exit(1);
	}
	if ((request_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
	{
		perror("socket");
		exit(1);
	}

	memset((void *)&server, 0, sizeof server);
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = INADDR_ANY;
	server.sin_port = htons((u_short)atoi(argv[1]));
	if (bind(request_sock, (struct sockaddr *)&server, sizeof server) < 0)
	{
		perror("bind");
		exit(1);
	}
	if (listen(request_sock, SOMAXCONN) < 0)
	{
		perror("listen");
		exit(1);
	}

	FD_ZERO(&mask);
	FD_SET(request_sock, &mask);
	maxfd = request_sock;
	for (;;)
	{
		rmask = mask;
		nfound = select(maxfd + 1, &rmask, (fd_set *)0, (fd_set *)0, &timeout);
		if (nfound < 0)
		{
			if (errno == EINTR)
			{
				printf("interrupted system call\n");
				continue;
			}
			/* something is very wrong! */
			perror("select");
			exit(1);
		}
		if (FD_ISSET(request_sock, &rmask))
		{
			/* a new connection is available on the connetion socket */
			addrlen = sizeof(remote);
			new_sock = accept(request_sock,
							  (struct sockaddr *)&remote, (socklen_t *)&addrlen);
			if (new_sock < 0)
			{
				perror("accept");
				exit(1);
			}
			printf("connection from host %s, port %d, socket %d\n",
				   inet_ntoa(remote.sin_addr), ntohs(remote.sin_port),
				   new_sock);
			FD_SET(new_sock, &mask);
			if (new_sock > maxfd)
				maxfd = new_sock;
			FD_CLR(request_sock, &rmask);
		}

		for (fd = 4; fd <= maxfd; fd++)
		{
			/* look for other sockets that have data available */
			if (FD_ISSET(fd, &rmask))
			{
				/* process the data */
				bytesread = read(fd, buf, sizeof(buf) - 1);
				if (bytesread < 0)
				{
					perror("read");
					/* fall through */
				}

				if (bytesread <= 0)
				{
					printf("server: end of file on %d\n", fd);
					FD_CLR(fd, &mask);
					if (close(fd))
						perror("close");
					continue;
				}
				buf[bytesread] = '\0';

				/* echo it back */
				for(int cli_i = 4; cli_i < maxfd+1; cli_i++) {
					if (fd == cli_i)
						continue;
					if (write(cli_i, buf, bytesread) != bytesread)
						perror("echo");
				}

			}
		}

	}
} /* main - serverorg.c */


File Server

  • File client는 nc로 대신
  • Server program 실행 후 client 접속
  • 특정 확장자를 가진 file list download
    • LS ext: 이 명령이 들어오면 서버는 현재 디렉토리에서 파일 확장자가 ext인 파일을 찾아서 tcp 연결을 통해 전송한다. 전송시에는 한 라인에 한 파일명을 전송한다. 구체적으로는 라인 구분을 “\r\n”으로 해서 각 파일에 대해서 “파일명\r\n”의 형태로 전송하면 된다.
  • 주어진 이름의 파일 다운로드
    • GET filename: 이 명령이 들어오면 서버는 filename에 해당하는 파일을 찾아서 tcp 연결을 통해 전송한다. 만약 존재하지 않으면 "FILE NOT FOUND\r\n" 을 클라이언트로 전송한다.
  • 파일 업로드
    • PUT filename: 이 명령이 들어오면 서버는 filename 이라는 파일을 현재 디렉토리에 생성한 후, 이 tcp 연결을 통해명령라인이후에들어오는모든 내용을 이 filename 파일에 쓴다.

Commands

  • ./fs 100000
  • echo "LS html" | nc localhost 10000
  • echo "GET biga.html" | nc localhost 10000 > biga.html
  • echo "PUT cli.exe" | cat - cli.exe | nc localhost 10000

// Student ID : C035384
// Name : Serin Jeong

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>

int portnum = 0;

void SendFile(int new_sock, char *fname) {
	char buf[BUFSIZ];
	char msg[1024];
	struct stat infobuf;              /* place to store info */

	/* get file info */
	if ( stat(fname, &infobuf) == -1 )  {
		/* file not found */
		printf("Server Error : No such file %s!\n", fname);
		sprintf(msg, "FILE NOT FOUND\r\n");
		infobuf.st_size = -1;
		if (write(new_sock, msg, strlen(msg))!=strlen(msg))
			perror("echo error");

	} else {
		if(infobuf.st_size != -1) {
			int s = 0;
			int num = 0;
			FILE *fp1 = fopen(fname, "r");
			memset(buf, 0, sizeof(buf));
			while((num = fread( buf, sizeof( char ), BUFSIZ - 1, fp1 )) > 0) {
				s += num;
				if (write(new_sock, buf, num)!= num)
					perror("send file error");
			}
			fclose(fp1);
			printf("finish %d %d\n", s, (int)infobuf.st_size);
		}
	}
}

void SendDir(int new_sock, char *ext) {
	char buf[BUFSIZ];
	DIR *folder;
	struct dirent *entry;
	int num = 0;
	int fcnt = 0;

	folder = opendir(".");
	if(folder == NULL) {
		perror("Unable to read directory");
		return;
	}

	while( (entry = readdir(folder)) ) {
		char *p = strrchr(entry->d_name, '.');
		if(p == NULL)
			continue;
		if (strcmp(p + 1, ext) != 0)
			continue;

		sprintf(buf, "%s\n", entry->d_name);
		num = strlen(buf);
		if (write(new_sock, buf, num) != num) {
			perror("send ls error");
		}
		fcnt++;
	}
	if (fcnt == 0) {
		sprintf(buf, "FILE NOT FOUND\r\n");
		num = strlen(buf);
		if (write(new_sock, buf, num) != num) {
			perror("send ls error");
		}
	}
	closedir(folder);
}

void StoreFile(FILE *fp, char *fname) {
	char buf[BUFSIZ];
	int num = 0;

	FILE *fp1 = fopen(fname, "w");
	printf("fopen %s\n", fname);
	if (fp1 == NULL) {
		printf("fopen error for %s\n", fname);
		return;
	}
	while((num = fread( buf, sizeof( char ), BUFSIZ - 1, fp )) > 0) {
		buf[num] = 0;
		printf("%s\n", buf);
		if (fwrite(buf, sizeof(char), num, fp1) != num) {
			printf("write error %d\n", num);
			fclose(fp1);
			return;
		}
	}
	fclose(fp1);
}

int main(int argc, char *argv[]) {
	struct sockaddr_in server, remote;
	int request_sock, new_sock;
	socklen_t addrlen;
	char buf[BUFSIZ];

	if (argc != 2) {
		(void) fprintf(stderr,"usage: %s portnum \n",argv[0]);
		exit(1);
	}

	portnum = atoi(argv[1]);

	if ((request_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		perror("socket create error");
		exit(1);
	}

	printf("Student ID : C035384\n");
	printf("Name : Serin Jeong\n");

	// Create a Server Socket
	memset((void *) &server, 0, sizeof server);
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = INADDR_ANY;
	server.sin_port = htons((u_short)portnum);

	if (bind(request_sock, (struct sockaddr *)&server, sizeof server) < 0) {
		perror("bind error");
		exit(1);
	}
	if (listen(request_sock, SOMAXCONN) < 0) {
		perror("listen error");
		exit(1);
	}

	/* a new connection is available on the connetion socket */
	while(1) {
		addrlen = sizeof(remote);
		fflush(stdout);
		new_sock = accept(request_sock,
			(struct sockaddr *)&remote, &addrlen);
		if (new_sock < 0) {
			perror("accept error");
			exit(1);
		}
		printf("Connection : Host IP %s, Port %d, socket %d\n",
			inet_ntoa(remote.sin_addr), ntohs(remote.sin_port), new_sock);

		FILE *fp = fdopen(new_sock, "r");
		buf[0] = 0;
		if (!fgets(buf, BUFSIZ - 1, fp)) {
			printf("No Data: close connection");
			fclose(fp);
			if (close(new_sock)) perror("close error");
			continue;
		}
		printf("%s", buf);

		// get the request from the client
		// parse the data from the client
		// give the file to the client
		fflush(stdout);
		char *command = strtok(buf, " \t\n\r"); // Command
		char *filename = strtok(NULL, " \t\n\r");

		if (strcmp(command, "GET") == 0) {
			SendFile(new_sock, filename);
		} else if (strcmp(command, "LS") == 0) {
			SendDir(new_sock, filename);
		} else if (strcmp(command, "PUT") == 0) {
			StoreFile(fp, filename);
		} else {
			printf("Unknown Command %s\n", command);
		}
//		fclose(fp);

		if (close(new_sock))
			perror("close error2");
	}
} 

profile
밀린 TIL 업로드 조금씩 정리중...

0개의 댓글