반응형

이번에 우선순위 힙을 사용하여 푸는 알고리즘 문제가 등장하였다.

오늘도 계속해서 달려보자.

*본인은 C++로 코딩할 예정이다.


1. 문제

매운 것을 좋아하는 Leo는 모든 음식의 스코빌 지수를 K 이상으로 만들고 싶습니다.

모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 Leo는 스코빌 지수가 가장 낮은

두 개의 음식을 아래와 같이 특별한 방법으로 섞어 새로운 음식을 만듭니다.

 

섞은 음식의 스코빌 지수 = 가장 맵지 않은 음식의 스코빌 지수 + (두 번째로 맵지 않은 음식의 스코빌 지수 * 2)

 

Leo는 모든 음식의 스코빌 지수가 K 이상이 될 때까지 반복하여 섞습니다.
Leo가 가진 음식의 스코빌 지수를 담은 배열 scoville과 원하는 스코빌 지수 K가 주어질 때,

모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 섞어야 하는

최소 횟수를 return 하도록 solution 함수를 작성해주세요.

 

출저 - 프로그래머스



2. 사고 - 우선순위 힙

1. 하나씩 비교해서 최솟값을 찾아, 계산해나가면 O(n^2) 이상의 복잡도를 가지게 된다. > 효율이 낮다.

2. 우선순위 힙을 선언한다. > greater 기준으로 정렬한다.

3. 힙의 가장 작은 수를 꺼내고, 그 다음 작은 수를 꺼내서 정해진 연산을 수행한다.

4. 결과 값을 다시 힙에 넣는다.

5. 가장 작은 수가 K를 넘어서면 카운트를 반환한다.
6. 길이가 1이거나, 힙이 비어있는 상황 등 다양한 예외 처리를 진행한다.

7. 힙을 이용하므로 시간복잡도는 O(NlogN)을 가지게 된다.


3. 코딩

#include <iostream>
#include <string>
#include <vector>
#include <queue>
using namespace std;

int solution(vector<int> scoville, int K) {
    int answer = 0;
    int min1 = 0, min2=0, newq=1;
    priority_queue<int, vector<int>, greater<int>> pq; //작은 순서대로 정렬하는 우선순위 큐
    for (auto&i : scoville) pq.push(i);
    if (pq.size() == 1) {
        if (pq.top() >= K) return answer;
        return -1;
    }
    while (!pq.empty() && pq.size() > 1 && newq > 0) {
    	//cout << q.top() << endl;
        min1 = pq.top(); //최소 값 저장
        pq.pop();
        min2 = pq.top(); //그 다음 최소 값 저장
        pq.pop();
        newq = min1 + (min2 * 2); //연산
        pq.push(newq); //결과 값 큐에 저장
        answer++; //카운트 증가
        if (pq.top() >= K) return answer; //최소 값이 K 이상이면 answer 반환
    }
    return -1; //나올수 없는 경우엔 -1 반환
}

int main(void) {
    vector<int> scoville;
    int K = 7;
    scoville.push_back(1);
    scoville.push_back(2);
    scoville.push_back(3);
    scoville.push_back(9);
    scoville.push_back(10);
    scoville.push_back(12);
    cout << solution(scoville, K) << endl;
    return 0;  
}

- scoville = [1, 2, 3, 9, 10, 12], K=7

 

- 과정1 : 1 + (2*2) = 5

> [3, 5, 9, 10, 12], answer : 1

 

- 과정2 : 3 + (5*2) = 13

> [9, 10, 12, 13], answer : 2, K보다 9가 크므로 answer 반환

 

- 결과 : 2

 



4. 알게된 것

heap은 유용하다!

위와 같이 heap을 이용해서 최대, 최솟값을 손쉽게 뽑아낼 수 있다.

이러한 점을 이용하여 복잡한 알고리즘을

좀 더 빠른 시간복잡도안에 결과를 도출할 수 있다.

heap과 priority heap을 이용해서 상황에 맞게 잘 구현해보자.

 


맵고 짜고 단거

반응형
반응형

이번에도 탐욕법(그리디)를 이용한 문제를 풀어보고자 한다.

*본인은 C++로 코딩할 예정이다.


1. 문제

어떤 숫자에서 k개의 수를 제거했을 때 얻을 수 있는 가장 큰 숫자를 구하려 합니다.

예를 들어, 숫자 1924에서 수 두 개를 제거하면 [19, 12, 14, 92, 94, 24] 를 만들 수 있습니다.

이 중 가장 큰 숫자는 94 입니다.

문자열 형식으로 숫자 number와 제거할 수의 개수 k가 solution 함수의 매개변수로 주어집니다.

number에서 k 개의 수를 제거했을 때 만들 수 있는 수 중

가장 큰 숫자를 문자열 형태로 return 하도록 solution 함수를 완성하세요.

 

출저 - 프로그래머스



2. 사고 - 탐욕법

1. number의 배열을 순회하며 하나씩 이전 숫자와 비교한다.

2. 큰 숫자가 나타날 경우 그 숫자보다 작은 숫자들을 pop하고, 그 만큼 k를 감소시킨다.

3. k가 0이 되면 남은 숫자들을 자릿수에 맞춰서 반환한다.

4. k가 0이 되지않을때 역시 고려한다.


3. 코딩

#include <iostream>
#include <string>
using namespace std;

string solution(string number, int k) {
	string answer = "";
	int len;
	for (unsigned int i = 0; i < number.size(); i++) {
		len = answer.size();
		for (int j = 0; j < len; j++) {
			if (answer[j] < number[i]) {
				answer.pop_back();
				k--;
			}
			if (k == 0) {
				break;
			}
		}
		if (k == 0) {
			answer += number.substr(i, number.size());
			break;
		}
		answer += number[i];
		//cout << "answer : " << answer << ", k : " << k << endl;
	}
	answer = answer.substr(0, answer.size() - k);
	return answer;
}

int main(void) {
	string number = "4177252841";
	int k = 4;

	cout << solution(number, k) << endl;

	return 0;
}

- 수 : "4177252841"

- 뺄 갯수 : 4개

= 결과 : 775841



4. 알게된 것

string의 substr()함수!

아주 유용한 함수이다. 지정한 위치부터, 지정한 위치까지의 값을 뽑아낼 수 있는데, 유용하게 쓰인다.

파이썬의 슬라이싱과 비슷하다 보면 된다.

해당 함수를 이용해 k가 순환 중에 0이 안나오는 경우까지 고려하여 코딩해야한다.

이러한 과정을 거치면 문제를 해결 가능하다.

 


왜 초기화 코드에 vector가 있었던걸까?

반응형
반응형

오늘도 계속해서 알고리즘 문제를 풀어볼 예정이다.

*본인은 C++로 코딩할 예정이다.

 


1. 문제

0 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 알아내 주세요.

예를 들어, 주어진 정수가 [6, 10, 2]라면 [6102, 6210, 1062, 1026, 2610, 2106]를 만들 수 있고,

이중 가장 큰 수는 6210입니다.

0 또는 양의 정수가 담긴 배열 numbers가 매개변수로 주어질 때,

순서를 재배치하여 만들 수 있는 가장 큰 수를 문자열로 바꾸어 return 하도록 solution 함수를 작성해주세요.

 

출저 - 프로그래머스



2. 사고 - 정렬

1. 매번 가장 크게만들 수 있는 수를 고르면 O(n^2) 시간 복잡도를 가지게 될 것이다.

2. 우선 빈 문자열로 수를 초기화 한다.

3. numbers를 큰 수를 만들 수 있는 순서대로 정렬한다. > O(nlogn)

4. 하나하나씩 answer에 꺼내어 붙인다. > O(n)

5. 시간복잡도는 O(nlogn)을 가지게 된다.

6. 추가적으로 넘어오는 numbers에 0만 여러개 들어있는 경우의 수도 고려한다.

 

- 큰 수 정렬 기준

> 2와 34가 있다고 해보자. 234, 혹은 342가 될 수 있다. 이와 같이 하나씩 붙여보고 대소관계를 파악할 수 있다.


3. 코딩

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

bool cmp(int a, int b) {
	string str_fir;
	string str_sec;
	string s_a = to_string(a);
	string s_b = to_string(b);
	str_fir = s_a + s_b;
	str_sec = s_b + s_a;
	bool res = str_fir > str_sec;
	return res;
}

string solution(vector<int> numbers) {
	string answer = "";
	sort(numbers.begin(), numbers.end(), cmp);
	for (auto &num : numbers) {
		answer += to_string(num);
	}
	
	return answer[0] == '0' ? "0" : answer; //numbers에 0이 여러개면 000... 대비
}

int main(void) {
	vector<int> num;
	num.push_back(6);
	num.push_back(10);
	num.push_back(2);


	cout << solution(num) << endl;

	return 0;
}

- 수 : [6, 10, 2]

- 정렬된 수 : [6, 2, 10]

= 결과 : 6210



4. 알게된 것

예외의 상황을 항상 고려하자.

마지막 테스트케이스에서 삽질을 오래하였는데, 바로 numbers에 0이 여러개 들어온 상황이다.

이런 경우 원래 코드대로라면 000.. 이런식으로 반환할 것이다.

그렇기 때문에 answer의 첫번째 인덱스가 0이면 뒤에 뭐가 오든 0으로 치환시켜버리도록 코딩해야한다.

위와 같이 모든 경우의 수를 고려해야 완벽하게 코딩테스트에서 통과할 수 있을 것이다.

 


"야! C++도 sort 함수 있어!"

반응형
반응형

오늘도 알바를 마치고 어김없이 달린다.

알고리즘 공부해서 미래에 있을 코딩테스트를 뿌시자.

*본인은 C++로 코딩할 예정이다.


1. 문제

점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다.

다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다.

학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다.

예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다.

체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.

 

전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost,

여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때,

체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.

 

출저 - 프로그래머스



2. 사고 - 탐욕법

1. 학생의 수만큼 배열을 만들고, 가지고 있는 체육복 수를 지정한다.

2. 맨앞과 맨뒤에 배열을 추가하여 구현시 편리하게 만든다.

3. 모두 1로 지정하고, reserve 학생들은 +1, lost는 -1을 준다.

4. 순환하면서 체육복을 빌려줄 수 있을때(2일때), 작은번호가 0이면 체육복을 빌려주며 수를 수정한다.

5. 0이 아니면 뒷번호 역시 조사하여 조건에 맞으면 조정한다.

6. 알고리즘 복잡도는 for문이 3번 돌며 총 O(n)의 복잡도를 가지게 될 것이다.



3. 코딩

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int solution(int n, vector<int> lost, vector<int> reserve) {
	int answer = 0;
	//vector<int> v;
	vector<int> v(n+2, 1);
	//for (int i = 0; i <= n+1; i++) {
	//	v.push_back(i);
	//	v[i] = 1;
	//}
	for (auto &j : lost)v[j]--;
	for (auto &k : reserve)v[k]++;
	for (int i = 1; i <= n; i++) {
		if (v[i]==2 && v[i-1]==0) {
			v[i - 1]++;
			v[i]--;
		}
		else if (v[i] == 2 && v[i + 1] == 0) {
			v[i + 1]++;
			v[i]--;
		}
	}
	for (int i = 1; i <= n; i++) {
		if (v[i] >= 1) {
			answer++;
		}
	}
	return answer;
}

int main(void) {
	int num = 5;
	vector<int> lost;
	lost.push_back(2);
	lost.push_back(4);
	vector<int> reserve;
	reserve.push_back(3);

	cout << solution(num, lost, reserve) << endl;

	return 0;
}

- 학생수 : 5명(1번, 2번, 3번, 4번, 5번)

- 여분가져온 착한 친구 : 3번

- 잃어버린 친구 : 2번, 4번

= 수업을 들을 수 있는 학생수 : 4명


4. 알게된 것

시간복잡도만 보고 알고리즘의 좋고, 나쁨을 판단할 수 없다는 것을 알게되었다.

만약 학생수가 10,000,000명이라고 생각하고 여벌을 가져온 학생이 2명이라고 생각해보자.

여기서 위 알고리즘으로 구현하면 10,000,000개의 공간이 필요로 하며, O(n)이지만 여러번의 연산을 필요로한다.

이럴경우 다음과 같이 사고 해 볼 수 있다.

1. 여벌의 체육복을 가져온 학생들의 번호를 정렬한다. > O(nlogn)

2. 잃어버린 학생에 대해 빌려줄 수 있는 학생을 찾아서 처리한다(lost를 해쉬화) > O(1)

 

위의 알고리즘은 O(nlogn)이기 때문에 알고리즘만 보면 O(n)보다는 비효율적으로 보일 수 있으나,

학생수가 매우 많고, 여벌을 챙겨온 학생 수가 이에 비해 매우 적으면 오히려 아래와 같은 방식이

더 나은 알고리즘이 될 수 있다.

 

이와 같이 시간복잡도만 보고 모든 것을 판단할 수 없다는 것을 알게 되었다.


다음엔 체육복 가져간 도둑부터 잡는 걸로 하자.

반응형
반응형

방학 중에 알고리즘 공부를 통해 미래에 있을 코딩테스트에 대비하고자 한다.

그래서 프로그래머스에 있는 문제를 하나하나 풀어보며 포스팅 할 예정이다.

*본인은 C++로 코딩할 예정이다.


1. 문제

수많은 마라톤 선수들이 마라톤에 참여하였다.

단 한명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였다.

 

마라톤에 참여한 선수들의 이름이 담긴 배열 participant와

완주한 선수들의 이름이 담긴 배열 completion이 주어질 때,

완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성하라.

 

 

출저 - 프로그래머스



2. 사고 - 해시

1. 단순히 생각하고자 하면 이중 for문을 이용하여 쉽게 해결할 수 있을 것처럼 보인다.
2. 단, 시간복잡도가 O(n^2)가 되며 효율성 테스트에 통과하지 못할 것이다.

3. Hash Table을 이용하여 이를 해결할 수 있을 것이다. > unordered_map을 사용하였다.

4. participant 길이만큼 순회하며 Key에 선수이름을 저장하고, 이름이 등장한 횟수를 value로 하여 추가한다.

5. 중복이 되면 해당 Key의 value를 1 증가시킨다.

6. completion 길이만큼 순회하며 map에 존재하는 key에 value를 1 감소시킨다.

7. map을 순회하며 value가 0이 아닌 key를 반환한다.

8. for문을 3번 순회하지만 시간복잡도는 O(n)이다.


3. 코딩

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
using namespace std;

string solution(vector<string> participant, vector<string> completion) {
	string answer = "";
	vector<string>::iterator vecit;
	unordered_map<string, unsigned int> ht;
	unordered_map<string, unsigned int>::iterator htit;
	for (vecit = participant.begin(); vecit != participant.end(); vecit++) {
		auto htit = ht.insert(make_pair(*vecit,1)); //key(참가자), value(이름등장횟수) 생성
		if (htit.second == false) { //참가자가 중복이면
			ht[*vecit] += 1; //해당 참가자의 value 값 1 증가
		}
	}
    
	for (vecit = completion.begin(); vecit != completion.end(); vecit++) { //완주자 순회
		ht[*vecit] -= 1; //완주자 value 값 1 감소
	}
    
	for (htit = ht.begin(); htit != ht.end(); htit++) { //unordered map 순회
		if (htit->second != 0) { //0이 아니면
			answer = htit->first; //해당 key > answer 대입
		}
	}
	return answer; //반환
}

int main(void) {
	vector<string> parti;
	vector<string> comple;
	parti.push_back("leo"); //테스트 케이스
	parti.push_back("kiki");
	parti.push_back("eden");

	comple.push_back("eden");
	comple.push_back("kiki");

	string res = solution(parti, comple);

	cout << res << endl;

	return 0;
}

참가자 : leo, eden, kiki

완주자 : eden, kiki

완주하지 못한 사람 : leo

결과창

4. 알게된 것

다른 사람들의 풀이를 보다보니 C++11에 추가된 '범위 기반 for문'을 많이 사용하는 것을 알게되었다.

'범위 기반 for문'을 사용하여 vector와 unordered_map을 순회 시 긴 코드를 짧게 정렬 가능하다.

수정한 solution 함수로 포스팅을 마치겠다.

string solution(vector<string> participant, vector<string> completion) {
	string answer = "";
	//vector<sring>::iterator vecit;
	unordered_map<string, unsigned int> ht;
	//unordered_map<string, unsigned int>::iterator htit;
	for (auto vecit : participant) {
		auto htit = ht.insert(make_pair(vecit,1));
		if (htit.second == false) {
			ht[vecit] += 1;
		}
	}
	for (auto vecit : completion) {
		ht[vecit] -= 1;
	}
	for (auto htit : ht) {
		if (htit.second != 0) {
			answer = htit.first;
		}
	}
	return answer;
}




앞으로 마라톤은 모두 완주하는 걸로하자.

반응형
반응형

본인은 BoB 프로젝트 도중 딥러닝을 이용하는 프로젝트를 진행하였다.

관련 지식을 공부하던 중 AI에 대한 흥미가 생겨 앞으로 관련된 포스팅을 여럿 해보고자 한다.


1. 개요

 다들 아시다 싶이 현재는 바야흐로 AI 전성시대이다.

많은 제품들에서 AI를 사용하고 있으며, 앞으로는 더 많아질 것이다.

일반 제품뿐만아니라 안랩, 이스트시큐리티에서도 AI 스타트 기업을 인수하거나

AI를 적용한 백신을 출시하는 등 관심도가 높아지고 있다.

 

컨퍼런스에서도 이를 알 수 있는데, 최근 기술, 보안 컨퍼런스만 보아도 AI에 대한 다양한 주제를 다루고 있다.

 

왼쪽부터 2019 삼성SDS컨퍼런스, 삼성테크토닉컨퍼런스, 데프콘 컨퍼런스

이처럼 많은 영역에선 인공지능에 대해 다루고, 연구하고 있다.

슬픈 이야기지만 앞으로 우리들이 AI를 공부하지 않으면 조금씩 뒤쳐질 것이라 생각한다.

이제부터 우리는 AI가 무엇이고, 인공지능과 딥러닝이 무엇인지 알고있어야한다.

본 포스팅에선 이를 '간략히' 설명하고자한다.

 

 

2. 인공지능

인공지능 용어 창안자(위키피디아)

"인공지능? 뭔가 먼 미래의 기술같아."

 

인공지능(Artificial Intellignece)은 사실 최근에 등장한 개념이 아니다. 1956년에 존매카시 교수가 개최한 다트머스 회의에서 처음 등장한 개념이며, 지금까지 계속된 연구활동 끝에 최근들어 주목받게 된것이다.

 

인공지능은 다양한 정의가 존재하며 '사람의 지능을 필요로하는 작업을 수행할 수 있는 컴퓨터 시스템'이라고 보면 된다.

이후 연구 활동이 지속되며 다양한 방법론이 등장하였고 이 과정에서 등장한 것이 머신러닝과 딥러닝이다.



3. 머신러닝

머신러닝은 '알고리즘을 이용하여 데이터를 분석하고, 학습하여 이러한 내용을 기반으로 판단, 예측하는 기술'을 뜻한다.

일반적인 프로그램은 직접 과정에 대한 코딩을 진행하여, 결과를 얻어내지만 머신러닝은 그렇지 않다.

 

그림으로 설명하면 다음과 같다.

기존 프로그래밍에선 데이터를 가지고 프로그래밍을 하여 답이란 결과를 얻어낸다.

하지만 머신러닝은 데이터와 답을 가지고 학습하고 교육하여 규칙을 도출해낸다.

그래서 이 규칙을 가지고 향후 새로운 데이터에 대해 규칙을 적용하여 예측하고 판단하는 결과를 낼 수 있다.


4. 딥러닝

딥러닝은 머신러닝연구자들이 만들어낸 하나의 개념이다.

딥러닝은 우리들 '뇌의 뉴련의 형태 정보 입출력 계층을 만들어 데이터를 학습하는 방식'이다.

인공신경망에서 발전한 형태라 보면 된다.

 

물론 처음에 엄청난 데이터량과 연산을 필요로하여 관련된 연구가 활발하지 않았다.

그러나 최근 하드웨어 성능과 알고리즘, 인터넷에 대한 빠른 발전으로 딥러닝 연구가 활기를 찾았다.

이러한 과정에서 CNN(Convolutional neural network), RNN(Recurrent Neural Network) 등 다양한 신경망 알고리즘이 등장하여 지금까지도 연구되고 있다.

 

 

심층적이고 논리적인 구조로 여러번의 과정과 학습을 거치는 기술인 만큼 높은 예측, 판단률(정확도)를 보이고 있다.

AI 기술의 핵심은 정확한 예측, 판단률이다. 그래서 현재 딥러닝이 가장 주목을 받고 있다.

 

5. 마치며

이제 우리는 딥러닝이 머신러닝에서 파생된 것이고, 머신러닝은 미래를 예측하고 판단하기 위한 기술이라는 것을 알았다. 현재 이러한 기술을 통해 글자인식, 목소리 인식이 가능하며 암을 예측하고, 심지어 폐 사진으로 코로나를 진단하고 있다.

 

 

이처럼 우리도 각자 이러한 기술을 어디에 사용할 수 있을지 고민해보는 시간을 가졌으면 좋겠다.


앞으로 계속해서 AI 관련 포스팅을 진행할 것이다.

반응형
반응형

C++을 이용해 소켓프로그래밍을 해보려한다.

소켓에 대한 개념은 너무 방대하여 간단하게 다루고

프로그램을 짜는데 중점을 두고 포스팅하려한다.

함께, 에코 서버와 에코 클라이언트를 프로그래밍해보자.


1. 소켓(Socket) 프로그래밍

소켓 : 네트워크에서 서버와 클라이언트가 서로 특정 포트를 이용하여 양방향 통신하도록 만들어주는 소프트웨어 장치

소켓 프로그래밍 : 소켓을 사용하여 네트워크를 구성해나가는 과정

 

2. 소켓 API 흐름

서버소켓과 클라이언트 소켓의 흐름은 다음과 같다.

 

서버 소켓 >

  1. 소켓 생성 : Create
  2. 서버가 사용할 주소 지정하여 결합(IP, Port) : Bind
  3. 연결 요청 대기 : Listen
  4. 요청 수신시 받기 : Accept
  5. 연결 수립시(Established) 데이터 송수신 : send/recv
  6. 송수신 완료, 소켓 닫기 : Close

클라이언트 소켓 >

  1. 소켓 생성 : Create
  2. 서버 연결요청 : Connect
  3. 연결 요청 받으면 데이터 송수신 : send/recv
  4. 송수신 완료, 소켓 닫기 : Close

그림으로 정리하면 다음과 같다.

3. Echo-Client

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
using namespace std;

void usage(char *argv){
    cout << "Usage : " << argv << " [ip] [port]" << endl;
    cout << "Example) " << argv << " 192.168.0.12 1234" << endl;
}

int main(int argc, char *argv[]){
    if(argc != 3){ //인자가 3개가 아니면 usage를 출력
        usage(argv[0]);
        return -1;
    }

    struct sockaddr_in addr_server = {}; // 주소체계 구조체 선언

    memset(&addr_server, 0, sizeof(addr_server));
    addr_server.sin_family = AF_INET; // IPv4 통신, 기본 인터넷 프로토콜 지정
    addr_server.sin_addr.s_addr = inet_addr(argv[1]); // 첫번째 인자 IP 대입
    addr_server.sin_port = htons(atoi(argv[2])); // 두번째 인자 PORT 대입

    char w_buff[256]; // 쓰기용 버퍼 선언
    char r_buff[256]; // 읽기용 버퍼 선언

    int sock_client = socket(AF_INET, SOCK_STREAM, 0); // 소켓 생성(IPv4, TCP, 기본프로토콜)
    if(sock_client == -1){
        cout << "socket error" << endl;
        close(sock_client);
        exit(1);
    }

    if(connect(sock_client, (struct sockaddr*) &addr_server, sizeof(addr_server)) == -1){ // 연결 요청
        cout << "connect error" << endl;
        close(sock_client);
        exit(1);
    }

    while(1){ // 연결 수락시 반복문
        memset(r_buff, 0, 256); // 읽기 버퍼 초기화
        cin >> w_buff; // 쓰기 버퍼에 문자열 입력
        if(strlen(w_buff)>255) break; // 버퍼 오버플로그 방지
        int write_chk = write(sock_client, w_buff, strlen(w_buff)); // 작성 길이만큼 write(전송)
        if(write_chk == -1){
            cout << "write error" << endl;
            break;
        }
        int read_chk = read(sock_client, r_buff, sizeof(r_buff)-1); // 읽기 버퍼사이즈-1 만큼 read(읽기)
        if(read_chk == -1){
            cout << "read error" << endl;
            break;
        }else{
            r_buff[strlen(r_buff)] = '\n';
            cout << r_buff; // 버퍼 출력
        }
    }
    close(sock_client); // 연결 종료
    return 0;
}

 

 

4. Echo-Server

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
using namespace std;

void usage(char *argv){
    cout << "Usage : " << argv << " [port]" << endl;
    cout << "Example) " << argv << " 1234" << endl;
}

int main(int argc, char *argv[]){
    if(argc != 2){ // 인자가 2개가 아니면 usage 출력
        usage(argv[0]);
        return -1;
    }

    char buff[256]; // 읽기, 쓰기용 버퍼 선언

    struct sockaddr_in addr_server = {}; // 주소체계 구조체 선언
    struct sockaddr_in addr_client = {};
    socklen_t addr_client_len = sizeof(addr_client_len); // 길이 계산

    memset(&addr_server, 0, sizeof(addr_server)); // 초기화
    addr_server.sin_family = AF_INET; // IPv4 인터넷 프로토콜
    addr_server.sin_port = htons(atoi(argv[1])); // 첫번째 인자 PORT 지정
    addr_server.sin_addr.s_addr = htonl(INADDR_ANY); // Anybody
 

    int sock_server = socket(AF_INET, SOCK_STREAM, 0); // 소켓 생성
    if(sock_server == -1){
        cout << "socket error" << endl;
        close(sock_server);
        exit(1);
    }

    if(bind(sock_server, (sockaddr*) &addr_server, sizeof(addr_server)) == -1){ // 주소 지정
        cout << "bind error" << endl;
        close(sock_server);
        exit(1);
    }

    if(listen(sock_server, 3) == -1){ // 연결 대기
        cout << "listen error" << endl;
        close(sock_server);
        exit(1);
    }

    int sock_client = accept(sock_server, (sockaddr*) &addr_client, &addr_client_len); // 연결 수락
    if(sock_client == -1){
        cout << "accept error" << endl;
        close(sock_server);
        exit(1);
    }

    while(1){
        memset(buff, 0, 256); // 버퍼 초기화
        int read_chk = read(sock_client, buff, sizeof(buff)-1); // 버퍼크기-1만큼 read(읽기)
        if(read_chk == -1){
            cout << "read error" << endl;
            break;
        }
        buff[strlen(buff)] = '\n';
        cout << buff; // 버퍼 출력
        int write_chk = write(sock_client, buff, strlen(buff)); // 버퍼 사이즈만큼 write(전송)
        if(write_chk == -1){
            cout << "write error" << endl;
            break;
        }
    }
    close(sock_server); // 연결 종료
    return 0;
}

 

 

5. 실행결과

1234번 포트로 server를 실행시키고 client에서 localhost와 1234번 포트로 접속을 시도했다.

client에서 "echotest"와 "hellworld" 문자열을 입력해보았고 실행결과는 다음과 같다.

 

echo-server-main.cpp

 

echo-client-main.cpp


잘못된 정보나 코드 제보 받으며, 여기까지 Echo-Client, Echo-Server 프로그램 개발 포스팅을 마친다.

 

 

반응형

'Programming > Network' 카테고리의 다른 글

[Network] pypcap 공부, 실습  (0) 2020.08.04
반응형

어셈블리어를 이용하여 파일을 읽고 출력하는 프로그램을 구현해보자.

필자는 Visual Studio 2017과 Irvine 라이브러리를 활용하여 개발할 예정이다.


1. 문제

  • 환경 : Visual Studio 2017, Irvine 라이브러리, x86 assembly
  • 디스크 파일을 읽어 읽은 결과를 메모리 덤프 및 화면 출력하는 프로그램을 작성
  • 디스크 파일은 c:\...\source\...\프로젝트\프로젝트\파일명에 위치

2. 문제 해결 방법

  1. 파일명과, 버퍼, 버퍼크기를 정의해준다(본 문제에선 버퍼 사이즈를 240이라 지정하였다.)
  2. 파일명 offset을 edx에 넣고, OpenInputFile을 call하여 파일 핸들값을 eax에 얻어온다.
  3. 정상적으로 파일이 열린경우 ReadFromFile을 이용하여 문자열을 읽어 버퍼에 넣는다.
  4. 정상적으로 읽었을 경우 버퍼 offset을 edx에 넣어 WriteString으로 문자열을 출력한다.
  5. ebx에 mov해둔 파일 핸들을 eax로 옮겨 CloseFile로 파일을 닫는다.

3. 코딩

TITLE Program asm4fileread(asm4fileread.asm)
; 프로그램 설명문 : 디스크의 파일을 읽고 출력하는 프로그램

INCLUDE c:\Irvine\Irvine32.inc
BUF_SIZE = 240

.data
filename		BYTE "sample.txt", 0
BUF			BYTE BUF_SIZE DUP(? )
good     		BYTE "Good processed !", 0dh, 0ah, 0
Nogood			BYTE "File open or write error !", 0dh, 0ah, 0

.code
main	PROC
	mov		edx, offset filename
	call		OpenInputFile			; 입력파일 열기
	mov		ebx, eax				; 핸들 ebx로 mov
	cmp		eax, 0					; 정상 오픈 확인
	jnz		good_open				; eax != 0 정상open
	call		WriteWindowsMsg			; open 오류
	jmp		terminate

good_open :
	mov		eax, ebx
	mov		edx, offset BUF
	mov		ecx, BUF_SIZE
	call		ReadFromFile			; 파일 내용 읽기
	jnc		good_read				; cf=0이면 jmp
	call		WriteWindowsMsg			; open 오류
	jmp		terminate

good_read :		
	mov		edx, offset BUF			; 버퍼내용 edx로 mov
	call		WriteString				; edx 출력
	call		Crlf
	jmp		good_end

good_end :								; 핸들 닫기
	mov		eax, ebx
	call		CloseFile
	mov		edx, offset good
	call		WriteString
	exit

terminate :
	mov		edx, offset nogood
	call		WriteString
	call		WaitMsg
	exit

main	ENDP
END	main

 

파일 내용과 실행 결과 사진은 다음과 같다.

 

 

 


계속해서 어셈블리를 통해 다양한 문제를 풀어볼 예정이다.

반응형

'Programming > Language' 카테고리의 다른 글

[C++] 클래스(Class) 공부  (0) 2022.06.06
[Assembly] 문자열 입력과 출력  (0) 2020.06.05
[Assembly] 1부터 10까지의 합 계산  (0) 2020.06.04
[C++] STL - multimap 공부  (0) 2020.05.24
[C++] STL - map 공부  (0) 2020.05.14

+ Recent posts