리눅스데브코스 [9주차 - 1(2)]<네트워크 프로그래밍(2) 실습>

심우열·2023년 5월 29일
0

1. 단체 채팅 서버 구현

1. 구현 방안

1. 구현 조건

  1. 채팅 클라이언트는 nc 로 대신
  2. 서버 프로그램 실행 후 클라이언트 접속
  3. 클라이언트가 접속 시 서버는 Welcome 메시지 전송
  4. 한 클라이언트에서 메시지 전송 시 다른 클라이언트 모두 출력

2. 구현 팁

  1. 서버는 각 클라이언트가 접속 시 해당 정보를 배열에 저장
  2. select() 함수를 사용하여 여러개의 소켓을 감시
  3. 하나의 소켓에서 입력이 들어오면 클라이언트 배열에 저장된 다른 소켓으로 그 내용을 그대로 출력

2. 구현

1. chatsrv.c

#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>

#define BUFSIZE 100

void error_handling(char *message);
void CutTokens(char* str);
void display();
void Forward(char *str, int sender) ;
int GetEmptySlot();

char tokens[3][100];
#define MAXCLIENT (16)
int peertcpSocket[MAXCLIENT];	// max 128 clients


int main(int argc, char **argv) {
	
  int tcpServ_sock;
  int slot;
  
  struct sockaddr_in tcpServer_addr;
  struct sockaddr_in tcpClient_addr;
  struct sockaddr_in newTcp_addr;
  for(int i = 0; i < MAXCLIENT; i++) {
  	peertcpSocket[i] = -1;;	// max 128 clients
    
  }

  socklen_t clnt_len;
    
  fd_set reads, temps;
  int fd_max;

  char *tcpport = NULL;
  
  char str[BUFSIZE];
  int option = 2;

  if(argc != 2) {
    printf("Usage : %s <tcpport> \n", argv[0]);
    exit(1);
  }

  tcpport = argv[1];

  display();
	
  tcpServ_sock = socket(AF_INET, SOCK_STREAM, 0);
  if(tcpServ_sock == -1)
	  error_handling("socket() error");
  
  memset(&tcpServer_addr, 0 , sizeof(tcpServer_addr));
  tcpServer_addr.sin_family = AF_INET;
  tcpServer_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  tcpServer_addr.sin_port = htons(atoi(tcpport));

  setsockopt(tcpServ_sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&option, sizeof(int));
  setsockopt(tcpServ_sock, SOL_SOCKET, SO_REUSEPORT, (const void *)&option, sizeof(int));
  if( bind(tcpServ_sock, (struct sockaddr *) &tcpServer_addr, sizeof(tcpServer_addr)) == -1 )
	  error_handling("bind() error");

			
  if(listen(tcpServ_sock, 5)==-1)
    error_handling("listen() error");
  
  FD_ZERO(&reads);
  FD_SET(tcpServ_sock, &reads);
  fd_max = tcpServ_sock;

  while(1) {
    int nfound = 0;

    temps = reads;
    
    nfound = select(fd_max+1, &temps, 0, 0, NULL);
	
	if(FD_ISSET(tcpServ_sock, &temps)) {
		// Input from the client for new connection
		// This request will arrive after UDP sent
  		FD_CLR(tcpServ_sock, &temps);

		clnt_len = sizeof(newTcp_addr);
        slot = GetEmptySlot();
		peertcpSocket[slot] = accept(tcpServ_sock, 
			(struct sockaddr *)&newTcp_addr, &clnt_len);

		printf("connection from host %s, port %d, socket %d slot %d\n",
                        inet_ntoa(newTcp_addr.sin_addr),  
                        ntohs(newTcp_addr.sin_port), peertcpSocket[slot], slot);

		sprintf(str, "Welcome: host %s, port %d\n", 
				inet_ntoa(newTcp_addr.sin_addr),  ntohs(newTcp_addr.sin_port));
		write(peertcpSocket[slot], str, strlen(str));

		FD_SET(peertcpSocket[slot], &reads);
		if(fd_max < peertcpSocket[slot])
			fd_max = peertcpSocket[slot];
	} else {
		for(int i = 0; i < MAXCLIENT; i++) {
		  if (FD_ISSET(peertcpSocket[i], &temps)) {
			FD_CLR(peertcpSocket[i], &temps);

			/* process the data */
			int bytesread = read(peertcpSocket[i], str, sizeof str - 1);
			if (bytesread<0) {
				perror("read");
				/* fall through */
			}
			if (bytesread <= 0) {
				printf("Connection Closed %d slot %d\n", 
					peertcpSocket[i], i);
				FD_CLR(peertcpSocket[i], &reads);
				if (close(peertcpSocket[i])) perror("close");
				peertcpSocket[i] = -1;
				continue;
			}
            str[bytesread] = 0;	

           // forward the message to all the other sockets
           Forward(str, i);
          }
		}
	}

  }//while End
}//main End

void display() {
	printf("Student ID : 01048028464 \n");
	printf("Name : w10sim  \n");
}

void error_handling(char *message) {
  fputs(message, stderr);
  fputc('\n', stderr);
  perror("hi");
  exit(1);
}

void CutTokens(char* str) {
	char *pch;
	int cnt = 0;

	pch = strtok(str, " \r\n\t");
	
	while(pch != NULL) {
		strcpy(tokens[cnt++], pch);
		pch = strtok(NULL, " ");
	}
}


void Forward(char *str, int sender) {
  for(int i = 0; i < MAXCLIENT; i++) {
     if((peertcpSocket[i] != -1) && (i != sender)) {
		write(peertcpSocket[i], str, strlen(str));
	 }
  }
}

int GetEmptySlot() {
  for(int i = 0; i < MAXCLIENT; i++) {
     if(peertcpSocket[i] == -1) return i;
  }
  return -1;
}

2. 동작 모습

2. 파일 서버 구현

1. 구현 방안

1. 구현 조건

  1. 파일 클라이언트는 nc로 대신
  2. 서버 프로그램 실행 후 클라이언트 접속

2. 기능

  1. 특정 확장자를 가진 파일 목록 다운로드
  2. 주어진 이름의 파일 다운로드
  3. 파일 업로드

2. 구현

1. filesrv.c

// Student ID :
// Name :

#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 : 01048028464\n");
	printf("Name : w10sim\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");
	}
} /* main - hw2sol.c */

2. 동작 모습

1. filesrv

2. LS

3. GET (not found)

4. GET (found)

5. GET (redirect server to client)

6. PUT (client to server)

profile
Dev Ops, "Git, Linux, Docker, Kubernetes, ansible, " .

0개의 댓글