printf("ho_tari\n");

C언어(8) 본문

(Telechips) AI 시스템 반도체 SW 개발자 교육/C

C언어(8)

호타리 2025. 2. 27. 16:49

2025.02.27

 

큐(QUEUE)

- 먼저 들어온 데이터가 먼저 나가는 자료구조

- 선입선출 (FIFO, First In First Out)

- 예) 매표소의 대기열

 

큐의 응용

직접적인 응용

- 시뮬레이션의 대기열

- 통신에서의 데이터 패킷들의 모델링에 이용

- 프린터와 컴퓨터 사이의 버퍼링

 

간접적인 응용

- 스택과 마찬가지로 프로그래머의 도구

- 많은 알고리즘에서 사용됨

 

선형큐

- 배열을 선형으로 사용하여 큐를 구현

- 삽입을 계속하기 위해서는 요소들을 이동시켜야 함

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define MAX_QUEUE_SIZE 5

typedef int element;
typedef struct {
	int front;
	int rear;
	element data[MAX_QUEUE_SIZE];
} QueueType;

void error(char* message) {
	fprintf(stderr, "%s\n", message);
	exit(1);
}

void init_queue(QueueType* q) {
	q->rear = -1;
	q->front = -1;
}

void queue_print(QueueType* q) {
	for (int i = 0; i < MAX_QUEUE_SIZE; i++)
	{
		if (i <= q->front || i > q->rear)
		{
			printf("   | ");
		}
		else
		{
			printf("%d | ", q->data[i]);
		}
	}
	printf("\n");
}

int is_full(QueueType* q) {
	if (q->rear == MAX_QUEUE_SIZE - 1)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

int is_empty(QueueType* q) {
	if (q->front == q->rear)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

void enqueue(QueueType* q, int item) {
	if (is_full(q))
	{
		error("큐가 포화상태입니다.");
		return;
	}
	q->data[++(q->rear)] = item;
}

int dequeue(QueueType* q) {
	if (is_empty(q))
	{
		error("큐가 공백상태입니다.");
		return -1;
	}
	int item = q->data[++(q->front)];
	return item;
}

int main(void) {
	int item = 0;
	QueueType q;

	init_queue(&q);

	enqueue(&q, 10); queue_print(&q);
	enqueue(&q, 20); queue_print(&q);
	enqueue(&q, 30); queue_print(&q);

	item = dequeue(&q); queue_print(&q);
	item = dequeue(&q); queue_print(&q);
	item = dequeue(&q); queue_print(&q);

	return 0;

}

 

원형큐(Circular Buffer, Ring Buffer)

 

공백상태 : front == rear

포화상태 : front % M == (rear + 1) % M

공백상태와 포화상태를 구별하기 위하여 하나의 공간은 항상 비워둔다

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define MAX_QUEUE_SIZE 5

typedef int element;
typedef struct {
	int front;
	int rear;
	element data[MAX_QUEUE_SIZE];
} QueueType;

void error(char* message) {
	fprintf(stderr, "%s\n", message);
	exit(1);
}

void init_queue(QueueType* q) {
	q->rear = q->front = 0;
}

void queue_print(QueueType* q) {
	printf("QUEUE(front=%d rear=%d) = ", q->front, q->rear);
	if (!is_empty(q))
	{
		int i = q->front;
		do {
			i = (i + 1) % MAX_QUEUE_SIZE;
			printf("%d | ", q->data[i]);
			if (i == q->rear) break;
		} while (i != q->front);
	}
	printf("\n");
}

int is_full(QueueType* q) {
	return ((q->rear + 1) % MAX_QUEUE_SIZE == q->front);
}

int is_empty(QueueType* q) {
	return (q->rear == q->front);
}

void enqueue(QueueType* q, int item) {
	if (is_full(q))
	{
		error("큐가 포화상태입니다.");
		return;
	}
	q->rear = (q->rear + 1) % MAX_QUEUE_SIZE;
	q->data[q->rear] = item;
}

element dequeue(QueueType* q) {
	if (is_empty(q))
	{
		error("큐가 공백상태입니다.");
		return -1;
	}
	q->front = (q->front + 1) % MAX_QUEUE_SIZE;
	return q->data[q->front];
}

element peek(QueueType* q) {
	if (is_empty(q))
	{
		error("큐가 공백상태입니다.");
		return -1;
	}
	return q->data[(q->front + 1) % MAX_QUEUE_SIZE];
}

int main(void) {
	QueueType queue;
	int item;

	init_queue(&queue);

	printf("--데이터 추가 단계--\n");
	while (!is_full(&queue))
	{
		printf("정수를 입력하세요 : ");
		scanf("%d", &item);
		enqueue(&queue, item);
		queue_print(&queue);
	}
	printf("큐는 포화상태입니다.\n\n");

	printf("--데이터 꺼냄 단계--\n");
	while (!is_empty(&queue))
	{
		item = dequeue(&queue);
		printf("꺼내온 정수 : %d \n", item);
		queue_print(&queue);
	}
	printf("큐는 공백상태입니다.\n");

	return 0;

}

 

queue.h 헤더 파일

#pragma once

#ifndef __queue_h__
#define __queue_h__

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define MAX_QUEUE_SIZE 5

typedef int element;
typedef struct {
	int front;
	int rear;
	element data[MAX_QUEUE_SIZE];
} QueueType;

void init_queue(QueueType* q);
void queue_print(QueueType* q);
void enqueue(QueueType* q, int item);
element dequeue(QueueType* q);
element peek(QueueType* q);

#endif

 

main.c 파일

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"

int main(void) {
	QueueType queue;
	int element;

	init_queue(&queue);
	srand(time(NULL)); // 랜덤 함수 초기화(seed 설정)
	for (int i = 0; i < 100; i++)
	{
		if (rand() % 5 == 0)
		{
			enqueue(&queue, rand() % 100);
		}
		queue_print(&queue);
		if (rand() % 10 == 0)
		{
			int data = dequeue(&queue);
		}
		queue_print(&queue);
	}
	return 0;
}

 

queue.c 파일

#include "queue.h"

void error(char* message) {
	fprintf(stderr, "%s\n", message);
	exit(1);
}

void init_queue(QueueType* q) {
	q->rear = q->front = 0;
}

void queue_print(QueueType* q) {
	printf("QUEUE(front=%d rear=%d) = ", q->front, q->rear);
	if (!is_empty(q))
	{
		int i = q->front;
		do {
			i = (i + 1) % MAX_QUEUE_SIZE;
			printf("%d | ", q->data[i]);
			if (i == q->rear) break;
		} while (i != q->front);
	}
	printf("\n");
}

int is_full(QueueType* q) {
	return ((q->rear + 1) % MAX_QUEUE_SIZE == q->front);
}

int is_empty(QueueType* q) {
	return (q->rear == q->front);
}

void enqueue(QueueType* q, int item) {
	if (is_full(q))
	{
		error("큐가 포화상태입니다.");
		return;
	}
	q->rear = (q->rear + 1) % MAX_QUEUE_SIZE;
	q->data[q->rear] = item;
}

element dequeue(QueueType* q) {
	if (is_empty(q))
	{
		error("큐가 공백상태입니다.");
		return -1;
	}
	q->front = (q->front + 1) % MAX_QUEUE_SIZE;
	return q->data[q->front];
}

element peek(QueueType* q) {
	if (is_empty(q))
	{
		error("큐가 공백상태입니다.");
		return -1;
	}
	return q->data[(q->front + 1) % MAX_QUEUE_SIZE];
}

 

실행 결과

 

덱(deque)

- 덱은 double ended queue의 줄임말

- 큐의 전단(front)과 후단(rear)에서 모두 삽입과 삭제가 가능한 큐

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define MAX_QUEUE_SIZE 5

typedef int element;
typedef struct {
	int front;
	int rear;
	element data[MAX_QUEUE_SIZE];
} DequeType;

void error(char* message) {
	fprintf(stderr, "%s\n", message);
	exit(1);
}

void init_deque(DequeType* q) {
	q->rear = q->front = 0;
}

void deque_print(DequeType* q) {
	printf("Deque(front=%d rear=%d) = ", q->front, q->rear);
	if (!is_empty(q))
	{
		int i = q->front;
		do {
			i = (i + 1) % MAX_QUEUE_SIZE;
			printf("%d | ", q->data[i]);
			if (i == q->rear) break;
		} while (i != q->front);
	}
	printf("\n");
}

int is_full(DequeType* q) {
	return ((q->rear + 1) % MAX_QUEUE_SIZE == q->front);
}

int is_empty(DequeType* q) {
	return (q->rear == q->front);
}

void add_rear(DequeType* q, int item) {
	if (is_full(q))
	{
		error("큐가 포화상태입니다.");
		return;
	}
	q->rear = (q->rear + 1) % MAX_QUEUE_SIZE;
	q->data[q->rear] = item;
}

element delete_front(DequeType* q) {
	if (is_empty(q))
	{
		error("큐가 공백상태입니다.");
		return -1;
	}
	q->front = (q->front + 1) % MAX_QUEUE_SIZE;
	return q->data[q->front];
}

element get_front(DequeType* q) {
	if (is_empty(q))
	{
		error("큐가 공백상태입니다.");
		return -1;
	}
	return q->data[(q->front + 1) % MAX_QUEUE_SIZE];
}

void add_front(DequeType* q, element val) {
	if (is_full(q))
	{
		error("큐가 포화상태입니다.");
	}
	q->data[q->front] = val;
	q->front = (q->front - 1 + MAX_QUEUE_SIZE) % MAX_QUEUE_SIZE;
}

element delete_rear(DequeType* q) {
	int prev = q->rear;
	if (is_empty(q))
	{
		error("큐가 공백상태입니다.");
	}
	q->rear = (q->rear - 1 + MAX_QUEUE_SIZE) % MAX_QUEUE_SIZE;
	return q->data[prev];
}

element get_rear(DequeType* q) {
	if (is_empty(q))
	{
		error("큐가 공백상태입니다.");
	}
	return q->data[q->rear];
}

int main(void) {
	DequeType queue;

	init_deque(&queue);
	for (int i = 0; i < 3; i++)
	{
		add_front(&queue, i);
		deque_print(&queue);
	}
	for (int i = 0; i < 3; i++)
	{
		delete_rear(&queue);
		deque_print(&queue);
	}

	return 0;
}

 

queue.h 헤더파일

#pragma once

#ifndef __queue_h__
#define __queue_h__

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define MAX_QUEUE_SIZE	5

//typedef int element;
typedef struct {
	int id;
	int arrival_time;
	int service_time;
} element;

typedef struct {
	int front;
	int rear;
	element data[MAX_QUEUE_SIZE];
} QueueType;

void init_queue(QueueType* q);
void queue_print(QueueType* q);
void enqueue(QueueType* q, element item);
element dequeue(QueueType* q);
element peek(QueueType* q);

#endif

 

queue.c 파일

#include "queue.h"

void error(char* message) {
	fprintf(stderr, "%s\n", message);
	exit(1);
}

void init_queue(QueueType* q) {
	q->rear = q->front = 0;
}

void queue_print(QueueType* q) {
	printf("QUEUE(front=%d rear=%d) = ", q->front, q->rear);
	if (!is_empty(q)) {
		int i = q->front;
		do {
			i = (i + 1) % MAX_QUEUE_SIZE;
			printf("%d | ", q->data[i]);
			if (i == q->rear) break;
		} while (i != q->front);
	}
	printf("\n");
}

int is_full(QueueType* q) {
	return ((q->rear + 1) % MAX_QUEUE_SIZE == q->front);
}

int is_empty(QueueType* q) {
	return (q->rear == q->front);
}

void enqueue(QueueType* q, element item) {
	if (is_full(q)) {
		error("큐가 포화상태입니다.");
		return;
	}
	q->rear = (q->rear + 1) % MAX_QUEUE_SIZE;
	q->data[q->rear] = item;
}

element dequeue(QueueType* q) {
	if (is_empty(q)) {
		error("큐가 공백상태입니다.");
		return;
	}
	q->front = (q->front + 1) % MAX_QUEUE_SIZE;
	return q->data[q->front];
}

element peek(QueueType* q) {
	if (is_empty(q)) {
		error("큐가 공백상태입니다.");
		return;
	}
	return q->data[(q->front + 1) % MAX_QUEUE_SIZE];
}

 

main.c 파일

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#include "queue.h"

int main(void) {
	int minutes = 60;
	int total_wait = 0;
	int total_customers = 0;
	int service_time = 0;
	int service_customer;
	QueueType queue;
	init_queue(&queue);

	srand(time(NULL));
	for (int clock = 0; clock < minutes; clock++)
	{
		printf("현재시각 = %d\n", clock);
		if ((rand() % 10) < 3)
		{
			element customer;
			customer.id = total_customers++;
			customer.arrival_time = clock;
			customer.service_time = rand() % 3 + 1;
			enqueue(&queue, customer);
			printf("고객 %d이 %d분에 들어옵니다. 업무처리시간 = %d분\n", customer.id, customer.arrival_time, customer.service_time);
		}
		if (service_time > 0)
		{
			printf("고객 %d 업무처리중입니다. \n", service_customer);
			service_time--;
		}
		else
		{
			if (!is_empty(&queue))
			{
				element customer = dequeue(&queue);
				service_customer = customer.id;
				service_time = customer.service_time;
				printf("고객 %d이 %d분에 업무를 시작합니다. 대기시간은 %d분이었습니다.\n", customer.id, clock, clock - customer.arrival_time);
				total_wait += clock - customer.arrival_time;
			}
		}
	}
	printf("전체 대기 시간 = %d분 \n", total_wait);
	return 0;
}

 

실행 결과

 

배열 리스트

 

기본 연산

- 리스트에 새로운 항목 추가 (삽입 연산)

- 리스트에서 항목을 삭제 (삭제 연산)

- 리스트에서 특정한 항목을 찾음 (탐색 연산)

 

배열을 이용하여 리스트를 구현하면 순차적인 메모리 공간이 할당되므로 이것을 리스트의 순차적 연결이라고 함

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

// 배열을 이용한 리스트의 구성

#define MAX_LIST_SIZE 100

typedef int element;
typedef struct {
	element array[MAX_LIST_SIZE]; // 배열 정의
	int size; // 현재 리스트에 저장된 항목의 개수
} ArrayListType;

void error(char* message) {
	fprintf(stderr, "%s\n", message);
	exit(1);
}

void init(ArrayListType* L) {
	L->size = 0;
}

int is_empty(ArrayListType* L) {
	return L->size == 0;
}

int is_full(ArrayListType* L) {
	return L->size == MAX_LIST_SIZE;
}

element get_entry(ArrayListType* L, int pos) {
	if (pos < 0 || pos >= L->size)
	{
		error("위치 오류");
	}
	return L->array[pos];
}

void print_list(ArrayListType* L) {
	int i;
	for (i = 0; i < L->size; i++)
	{
		printf("%d->", L->array[i]);
	}
	printf("\n");
}

void insert_last(ArrayListType* L, element item) {
	if (L->size >= MAX_LIST_SIZE)
	{
		error("리스트 오버플로우");
	}
	L->array[L->size++] = item;
}

void insert(ArrayListType* L, int pos, element item) {
	if (!is_full(L) && (pos >= 0) && (pos <= L->size))
	{
		// 뒤에서부터 데이터를 하나씩 뒤로 복사한다.
		for (int i = (L->size - 1); i >= pos; i--)
		{
			L->array[i + 1] = L->array[i];
		}
		L->array[pos] = item;
		L->size++;
	}
}

element delete(ArrayListType* L, int pos) {
	element item;
	if (pos < 0 || pos >= L->size)
	{
		error("위치 오류");
	}
	item = L->array[pos];
	// 앞에서부터 남은 데이터를 하나씩 앞으로 복사한다.
	for (int i = pos; i < (L->size - 1); i++)
	{
		L->array[i] = L->array[i + 1];
	}
	L->size--;
	return item;
}
	
int main(void) {
	ArrayListType list;

	init(&list);
	insert(&list, 0, 10); print_list(&list);
	insert(&list, 0, 20); print_list(&list);
	insert(&list, 0, 30); print_list(&list);
	insert_last(&list, 40); print_list(&list);
	delete(&list, 0); print_list(&list);
	return 0;
}

 

for문이 아닌 메모리를 이용하여 shift 시키는 방법

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

// 배열을 이용한 리스트의 구성

#define MAX_LIST_SIZE 100

typedef int element;
typedef struct {
	element array[MAX_LIST_SIZE]; // 배열 정의
	int size; // 현재 리스트에 저장된 항목의 개수
} ArrayListType;

void error(char* message) {
	fprintf(stderr, "%s\n", message);
	exit(1);
}

void init(ArrayListType* L) {
	L->size = 0;
}

int is_empty(ArrayListType* L) {
	return L->size == 0;
}

int is_full(ArrayListType* L) {
	return L->size == MAX_LIST_SIZE;
}

element get_entry(ArrayListType* L, int pos) {
	if (pos < 0 || pos >= L->size)
	{
		error("위치 오류");
	}
	return L->array[pos];
}

void print_list(ArrayListType* L) {
	int i;
	for (i = 0; i < L->size; i++)
	{
		printf("%d->", L->array[i]);
	}
	printf("\n");
}

void insert_last(ArrayListType* L, element item) {
	if (L->size >= MAX_LIST_SIZE)
	{
		error("리스트 오버플로우");
	}
	L->array[L->size++] = item;
}

void insert(ArrayListType* L, int pos, element item) {
	if (!is_full(L) && (pos >= 0) && (pos <= L->size))
	{
		// 뒤에서부터 데이터를 하나씩 뒤로 복사한다.
		/*for (int i = (L->size - 1); i >= pos; i--)
		{
			L->array[i + 1] = L->array[i];
		}*/
		element tmp[MAX_LIST_SIZE];
		memcpy(tmp, L->array, L->size * sizeof(element));
		memcpy(&(L->array[pos + 1]), &tmp[pos], (L->size - pos)*sizeof(element));
		L->array[pos] = item;
		L->size++;
	}
}

element delete(ArrayListType* L, int pos) {
	element item;
	if (pos < 0 || pos >= L->size)
	{
		error("위치 오류");
	}
	item = L->array[pos];
	// 앞에서부터 남은 데이터를 하나씩 앞으로 복사한다.
	/*for (int i = pos; i < (L->size - 1); i++)
	{
		L->array[i] = L->array[i + 1];
	}*/
	memcpy(&(L->array[pos]), &(L->array[pos + 1]), (L->size - pos)*sizeof(element));
	L->size--;
	return item;
}
	
int main(void) {
	ArrayListType list;

	init(&list);
	insert(&list, 0, 10); print_list(&list);
	insert(&list, 0, 20); print_list(&list);
	insert(&list, 0, 30); print_list(&list);
	insert_last(&list, 40); print_list(&list);
	delete(&list, 0); print_list(&list);
	return 0;
}

 

연결 리스트

- 리스트의 항목들을 노드라고 하는 곳에 분산하여 저장

- 노드는 데이터 필드와 링크 필드로 구성

데이터 필드 : 리스트의 원소, 즉 데이터 값을 저장하는 곳

링크 필드 : 다른 노드의 주소값을 저장하는 장소(포인터)

 

장점

- 삽입, 삭제가 보다 용이하다

- 연속된 메모리 공간이 필요없다

- 크기 제한이 없다

 

단점

- 구현이 어렵다

- 오류가 발생하기 쉽다

 

노드 = 데이터 필드 + 링크 필드

 

단순 연결 리스트

- 하나의 링크 필드를 이용하여 연결

 

단순 연결 리스트의 연산

- insert_first() : 리스트의 시작 부분에 항목을 삽입하는 함수

- insert() : 리스트의 중간 부분에 항목을 삽입하는 함수

- delete_first() : 리스트의 첫 번째 항목을 삭제하는 함수

- delete() : 리스트의 중간 항목을 삭제하는 함수

- print_list() : 리스트를 방문하여 모든 항목을 출력하는 함수

 

단순 연결 리스트 삽입 연산 (숫자)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef int element;
typedef struct ListNode {
	element data;
	struct ListNode* link;
} ListNode;

// 오류 메시지를 출력하는 함수
void error(char* message) {
	fprintf(stderr, "%d\n", message); // 메시지 출력
	exit(1); // 프로그램 종료
}

// 단일 연결 리스트의 첫 번째 위치에 새 노드를 삽입하는 함수
ListNode* insert_first(ListNode* head, int value) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode)); // 새 노드 공간의 동적할당
	p->data = value; // 데이터를 저장
	p->link = head; // 기존 리스트의 첫 노드를 새 노드의 link로 성정
	head = p; // head를 새 노드로 변경
	return head;
}

// 특정 위치 (이전 노드 pre 다음)에 새 노드를 삽입하는 함수
ListNode* insert(ListNode* head, ListNode* pre, element value) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode)); // 새 노드 공간의 동적할당
	p->data = value; // 데이터를 저장
	p->link = pre->link; // 새 노드가 이전 노드의 다음 노드를 가리키도록 설정
	pre->link = p; // 이전 노드의 link를 새 노드로 변경
	return head;
}

// 리스트의 첫 번째 노드를 삭제하는 함수
ListNode* delete_first(ListNode* head) {
	ListNode* removed; // 삭제될 노드
	if (head == NULL) return NULL; // 리스트가 비어 있으면 NULL 반환 
	removed = head; // 삭제할 노드 저장
	head = removed->link; // head를 다음 노드로 변경
	free(removed); // 삭제된 노드 메모리 해제
	return head;
}

// 특정 위치(이전 노드 pre 다음)의 노드를 삭제하는 함수
ListNode* delete(ListNode* head, ListNode* pre) {
	ListNode* removed; // 삭제된 노드
	removed = pre->link; // 삭제할 노드 지정
	pre->link = removed->link; // 이전 노드의 link를 삭제할 노드의 다음 노드로 변경
	free(removed); // 삭제된 노드 메모리 해제
	return head;
}

// 리스트를 출력하는 함수
void print_list(ListNode* head) {
	// head부터 link에 NULL이 보관된 노드까지 반복
	for (ListNode* p = head; p != NULL; p = p->link) 
	{
		printf("%d->", p->data); // 노드 데이터 출력
	}
	printf("NULL\n"); // 노드 리스트의 끝을 표시
}

int main(void) {
	ListNode* head = NULL;

	for (int i = 0; i < 5; i++)
	{
		head = insert_first(head, i);
		print_list(head);
	}
	// 2 다음에 99를 삽입
	ListNode* pre;
	pre = head;
	while (pre != NULL && pre->data != 2)
	{
		pre = pre->link;
	}
	// 99를 삽입
	if (pre != NULL)
	{
		head = insert(head, pre, 99);
		print_list(head);
	}
	for (int i = 0; i < 5; i++)
	{
		head = delete_first(head);
		print_list(head);
	}
	
	return 0;
}

 

단순 연결 리스트 삽입 연산 (문자열)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// typedef int element; // 정수 저장용 형식 정의
// 문자열 저장용 형식 정의
typedef struct {
	char name[100]; // 문자열의 길이가 최대 99
} element;

typedef struct ListNode {
	element data;
	struct ListNode* link;
} ListNode;

// 오류 메시지를 출력하는 함수
void error(char* message) {
	fprintf(stderr, "%d\n", message); // 메시지 출력
	exit(1); // 프로그램 종료
}

// 단일 연결 리스트의 첫 번째 위치에 새 노드를 삽입하는 함수
ListNode* insert_first(ListNode* head, element value) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode)); // 새 노드 공간의 동적할당
	p->data = value; // 데이터를 저장
	p->link = head; // 기존 리스트의 첫 노드를 새 노드의 link로 성정
	head = p; // head를 새 노드로 변경
	return head;
}

// 특정 위치 (이전 노드 pre 다음)에 새 노드를 삽입하는 함수
ListNode* insert(ListNode* head, ListNode* pre, element value) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode)); // 새 노드 공간의 동적할당
	p->data = value; // 데이터를 저장
	p->link = pre->link; // 새 노드가 이전 노드의 다음 노드를 가리키도록 설정
	pre->link = p; // 이전 노드의 link를 새 노드로 변경
	return head;
}

// 리스트의 첫 번째 노드를 삭제하는 함수
ListNode* delete_first(ListNode* head) {
	ListNode* removed; // 삭제될 노드
	if (head == NULL) return NULL; // 리스트가 비어 있으면 NULL 반환 
	removed = head; // 삭제할 노드 저장
	head = removed->link; // head를 다음 노드로 변경
	free(removed); // 삭제된 노드 메모리 해제
	return head;
}

// 특정 위치(이전 노드 pre 다음)의 노드를 삭제하는 함수
ListNode* delete(ListNode* head, ListNode* pre) {
	ListNode* removed; // 삭제된 노드
	removed = pre->link; // 삭제할 노드 지정
	pre->link = removed->link; // 이전 노드의 link를 삭제할 노드의 다음 노드로 변경
	free(removed); // 삭제된 노드 메모리 해제
	return head;
}

// 리스트를 출력하는 함수
void print_list(ListNode* head) {
	// head부터 link에 NULL이 보관된 노드까지 반복
	for (ListNode* p = head; p != NULL; p = p->link) 
	{
		// printf("%d->", p->data); // 노드 데이터 출력(정수)
		printf("%s->", p->data.name); // 노드 데이터 출력(문자열)
	}
	printf("NULL\n"); // 노드 리스트의 끝을 표시
}

int main(void) {
	ListNode* head = NULL;

	element data;

	strcpy(data.name, "apple");
	head = insert_first(head, data);
	print_list(head);

	strcpy(data.name, "kiwi");
	head = insert_first(head, data);
	print_list(head);

	strcpy(data.name, "banana");
	head = insert_first(head, data);
	print_list(head);
	
	return 0;
}

 

단일 연결 리스트 search 알고리즘

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// typedef int element; // 정수 저장용 형식 정의
// 문자열 저장용 형식 정의
typedef struct {
	char name[100]; // 문자열의 길이가 최대 99
} element;

typedef struct ListNode {
	element data;
	struct ListNode* link;
} ListNode;

// 오류 메시지를 출력하는 함수
void error(char* message) {
	fprintf(stderr, "%d\n", message); // 메시지 출력
	exit(1); // 프로그램 종료
}

// 단일 연결 리스트의 첫 번째 위치에 새 노드를 삽입하는 함수
ListNode* insert_first(ListNode* head, element value) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode)); // 새 노드 공간의 동적할당
	p->data = value; // 데이터를 저장
	p->link = head; // 기존 리스트의 첫 노드를 새 노드의 link로 성정
	head = p; // head를 새 노드로 변경
	return head;
}

// 특정 위치 (이전 노드 pre 다음)에 새 노드를 삽입하는 함수
ListNode* insert(ListNode* head, ListNode* pre, element value) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode)); // 새 노드 공간의 동적할당
	p->data = value; // 데이터를 저장
	p->link = pre->link; // 새 노드가 이전 노드의 다음 노드를 가리키도록 설정
	pre->link = p; // 이전 노드의 link를 새 노드로 변경
	return head;
}

// 리스트의 첫 번째 노드를 삭제하는 함수
ListNode* delete_first(ListNode* head) {
	ListNode* removed; // 삭제될 노드
	if (head == NULL) return NULL; // 리스트가 비어 있으면 NULL 반환 
	removed = head; // 삭제할 노드 저장
	head = removed->link; // head를 다음 노드로 변경
	free(removed); // 삭제된 노드 메모리 해제
	return head;
}

// 특정 위치(이전 노드 pre 다음)의 노드를 삭제하는 함수
ListNode* delete(ListNode* head, ListNode* pre) {
	ListNode* removed; // 삭제된 노드
	removed = pre->link; // 삭제할 노드 지정
	pre->link = removed->link; // 이전 노드의 link를 삭제할 노드의 다음 노드로 변경
	free(removed); // 삭제된 노드 메모리 해제
	return head;
}

// 리스트를 출력하는 함수
void print_list(ListNode* head) {
	// head부터 link에 NULL이 보관된 노드까지 반복
	for (ListNode* p = head; p != NULL; p = p->link) 
	{
		// printf("%d->", p->data); // 노드 데이터 출력(정수)
		printf("%s->", p->data.name); // 노드 데이터 출력(문자열)
	}
	printf("NULL\n"); // 노드 리스트의 끝을 표시
}

// 노드값을 탐색하는 함수
ListNode* search_list(ListNode* head, element x) {
	ListNode* p = head;
	while (p != NULL) // 노드의 끝까지 반복
	{
		if (strcmp(p->data.name, x.name) == 0) // x와 일치되는 노드 발견
			return p; // 발견된 노드 반환
		p = p->link; // 다음 노드로 이동
	}
	return NULL; // 탐색 실패
}

int main(void) {
	ListNode* head = NULL;
	ListNode* p = NULL;

	element data;
	element data2;

	strcpy(data.name, "apple");
	head = insert_first(head, data);
	print_list(head);

	strcpy(data.name, "kiwi");
	head = insert_first(head, data);
	print_list(head);

	strcpy(data.name, "banana");
	head = insert_first(head, data);
	print_list(head);
	
	// liwi가 존재하는 노드 찾기
	strcpy(data.name, "kiwi");
	p = search_list(head, data);
	if (p != NULL)
	{
		strcpy(data2.name, "melon");
		insert(head, p, data2);
		print_list(head);
	}
	
	return 0;
}

 

두 개의 노드를 하나로 결합

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define USE_NUM
#ifdef USE_NUM
// 정수 저장용 형식 정의
typedef int element; 
#else
// 문자열 저장용 형식 정의
typedef struct {
	char name[100]; // 문자열의 길이가 최대 99
} element;
#endif

typedef struct ListNode {
	element data;
	struct ListNode* link;
} ListNode;

// 오류 메시지를 출력하는 함수
void error(char* message) {
	fprintf(stderr, "%d\n", message); // 메시지 출력
	exit(1); // 프로그램 종료
}

// 단일 연결 리스트의 첫 번째 위치에 새 노드를 삽입하는 함수
ListNode* insert_first(ListNode* head, element value) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode)); // 새 노드 공간의 동적할당
	p->data = value; // 데이터를 저장
	p->link = head; // 기존 리스트의 첫 노드를 새 노드의 link로 성정
	head = p; // head를 새 노드로 변경
	return head;
}

// 특정 위치 (이전 노드 pre 다음)에 새 노드를 삽입하는 함수
ListNode* insert(ListNode* head, ListNode* pre, element value) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode)); // 새 노드 공간의 동적할당
	p->data = value; // 데이터를 저장
	p->link = pre->link; // 새 노드가 이전 노드의 다음 노드를 가리키도록 설정
	pre->link = p; // 이전 노드의 link를 새 노드로 변경
	return head;
}

// 리스트의 첫 번째 노드를 삭제하는 함수
ListNode* delete_first(ListNode* head) {
	ListNode* removed; // 삭제될 노드
	if (head == NULL) return NULL; // 리스트가 비어 있으면 NULL 반환 
	removed = head; // 삭제할 노드 저장
	head = removed->link; // head를 다음 노드로 변경
	free(removed); // 삭제된 노드 메모리 해제
	return head;
}

// 특정 위치(이전 노드 pre 다음)의 노드를 삭제하는 함수
ListNode* delete(ListNode* head, ListNode* pre) {
	ListNode* removed; // 삭제된 노드
	removed = pre->link; // 삭제할 노드 지정
	pre->link = removed->link; // 이전 노드의 link를 삭제할 노드의 다음 노드로 변경
	free(removed); // 삭제된 노드 메모리 해제
	return head;
}

// 리스트를 출력하는 함수
void print_list(ListNode* head) {
	// head부터 link에 NULL이 보관된 노드까지 반복
	for (ListNode* p = head; p != NULL; p = p->link) 
	{
#ifdef USE_NUM
		printf("%d->", p->data); // 노드 데이터 출력(정수)
#else
		printf("%s->", p->data.name); // 노드 데이터 출력(문자열)
#endif
	}
	printf("NULL\n"); // 노드 리스트의 끝을 표시
}

// 노드값을 탐색하는 함수
ListNode* search_list(ListNode* head, element x) {
	ListNode* p = head;
	while (p != NULL) // 노드의 끝까지 반복
	{
#ifdef USE_NUM
		if (p->data == x)
#else
		if (strcmp(p->data.name, x.name) == 0) // x와 일치되는 노드 발견
#endif
			return p; // 발견된 노드 반환
		p = p->link; // 다음 노드로 이동
	}
	return NULL; // 탐색 실패
}

// 2개의 노드를 하나로 결합하는 함수 head1 -> head2
ListNode* concat_list(ListNode* head1, ListNode* head2) {
	if (head1 == NULL) return head2; // head1이 없으면 head2를 반환
	else if (head2 == NULL) return head1; // head1이 있고 head2가 없으면 head1을 반환
	else // head1과 head2가 모두 존재하는 경우
	{
		ListNode* p;
		p = head1;
		while (p->link != NULL) // head1을 복사한 p가 끝까지 이동(마지막 노드 찾기)
		{
			p = p->link;
		}
		p->link = head2;
		return head1;
	}
}

int main(void) {
	ListNode* head1 = NULL;
	ListNode* head2 = NULL;

	head1 = insert_first(head1, 10);
	head1 = insert_first(head1, 20);
	head1 = insert_first(head1, 30);
	print_list(head1);

	head2 = insert_first(head2, 40);
	head2 = insert_first(head2, 50);
	head2 = insert_first(head2, 60);
	print_list(head2);

	ListNode* total = concat_list(head1, head2);
	print_list(total);
	
	return 0;
}

리스트를 역순으로 변환

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define USE_NUM
#ifdef USE_NUM
// 정수 저장용 형식 정의
typedef int element; 
#else
// 문자열 저장용 형식 정의
typedef struct {
	char name[100]; // 문자열의 길이가 최대 99
} element;
#endif

typedef struct ListNode {
	element data;
	struct ListNode* link;
} ListNode;

// 오류 메시지를 출력하는 함수
void error(char* message) {
	fprintf(stderr, "%d\n", message); // 메시지 출력
	exit(1); // 프로그램 종료
}

// 단일 연결 리스트의 첫 번째 위치에 새 노드를 삽입하는 함수
ListNode* insert_first(ListNode* head, element value) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode)); // 새 노드 공간의 동적할당
	p->data = value; // 데이터를 저장
	p->link = head; // 기존 리스트의 첫 노드를 새 노드의 link로 성정
	head = p; // head를 새 노드로 변경
	return head;
}

// 특정 위치 (이전 노드 pre 다음)에 새 노드를 삽입하는 함수
ListNode* insert(ListNode* head, ListNode* pre, element value) {
	ListNode* p = (ListNode*)malloc(sizeof(ListNode)); // 새 노드 공간의 동적할당
	p->data = value; // 데이터를 저장
	p->link = pre->link; // 새 노드가 이전 노드의 다음 노드를 가리키도록 설정
	pre->link = p; // 이전 노드의 link를 새 노드로 변경
	return head;
}

// 리스트의 첫 번째 노드를 삭제하는 함수
ListNode* delete_first(ListNode* head) {
	ListNode* removed; // 삭제될 노드
	if (head == NULL) return NULL; // 리스트가 비어 있으면 NULL 반환 
	removed = head; // 삭제할 노드 저장
	head = removed->link; // head를 다음 노드로 변경
	free(removed); // 삭제된 노드 메모리 해제
	return head;
}

// 특정 위치(이전 노드 pre 다음)의 노드를 삭제하는 함수
ListNode* delete(ListNode* head, ListNode* pre) {
	ListNode* removed; // 삭제된 노드
	removed = pre->link; // 삭제할 노드 지정
	pre->link = removed->link; // 이전 노드의 link를 삭제할 노드의 다음 노드로 변경
	free(removed); // 삭제된 노드 메모리 해제
	return head;
}

// 리스트를 출력하는 함수
void print_list(ListNode* head) {
	// head부터 link에 NULL이 보관된 노드까지 반복
	for (ListNode* p = head; p != NULL; p = p->link) 
	{
#ifdef USE_NUM
		printf("%d->", p->data); // 노드 데이터 출력(정수)
#else
		printf("%s->", p->data.name); // 노드 데이터 출력(문자열)
#endif
	}
	printf("NULL\n"); // 노드 리스트의 끝을 표시
}

// 노드값을 탐색하는 함수
ListNode* search_list(ListNode* head, element x) {
	ListNode* p = head;
	while (p != NULL) // 노드의 끝까지 반복
	{
#ifdef USE_NUM
		if (p->data == x)
#else
		if (strcmp(p->data.name, x.name) == 0) // x와 일치되는 노드 발견
#endif
			return p; // 발견된 노드 반환
		p = p->link; // 다음 노드로 이동
	}
	return NULL; // 탐색 실패
}

// 2개의 노드를 하나로 결합하는 함수 head1 -> head2
ListNode* concat_list(ListNode* head1, ListNode* head2) {
	if (head1 == NULL) return head2; // head1이 없으면 head2를 반환
	else if (head2 == NULL) return head1; // head1이 있고 head2가 없으면 head1을 반환
	else // head1과 head2가 모두 존재하는 경우
	{
		ListNode* p;
		p = head1;
		while (p->link != NULL) // head1을 복사한 p가 끝까지 이동(마지막 노드 찾기)
		{
			p = p->link;
		}
		p->link = head2;
		return head1;
	}
}
// 리스트를 역순으로 변환
ListNode* reverse(ListNode* head) {
	ListNode* p, * q, * r; // 순회 포인터
	p = head; // 원래 리스트에서 순차적으로 노드를 탐색하는 포인터
	q = NULL; // 현재 처리 중인 노드 (새로운 역순 리스트의 head가 될 노드)
		      // r은 q의 이전 노드를 저장하는 포인터 (역순 리스트의 이전 head)
	while (p != NULL) // 리스트 끝까지 반복
	{
		r = q; // r은 역순으로 된 리스트
		q = p;
		p = p->link;
		q->link = r; // q링크의 방향 전환
	}
	return q;
}

int main(void) {
	ListNode* head1 = NULL;
	ListNode* head2 = NULL;

	head1 = insert_first(head1, 10);
	head1 = insert_first(head1, 20);
	head1 = insert_first(head1, 30);
	print_list(head1);

	head2 = insert_first(head2, 40);
	head2 = insert_first(head2, 50);
	head2 = insert_first(head2, 60);
	print_list(head2);

	ListNode* total = concat_list(head1, head2);
	print_list(total);

	ListNode* rev = reverse(total);
	print_list(rev);
	
	return 0;
}

 

ethernet 통신

 

스레드(Thread)

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <windows.h>

#pragma comment(lib, "ws2_32.lib")

#define PORT 8080
#define BUFFER_SIZE 1024

DWORD WINAPI ThreadFunction(LPVOID param);

int main(void) {
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);

	SOCKET server_fd, new_socket;
	struct sockaddr_in address;
	int addrlen = sizeof(address);
	char buffer[BUFFER_SIZE] = { 0 };
	char* message = "Hello from server";

	// 소켓 설정
	server_fd = socket(AF_INET, SOCK_STREAM, 0);
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(PORT);

	// 소켓 연결
	bind(server_fd, (struct sockaddr*)&address, sizeof(address));
	listen(server_fd, 3);

	printf("서버 대기 중... 포트 : %d\n", PORT);

	// 새로운 스레드 생성
	HANDLE threads[2];
	int threadIDs[2] = { 1, 2 };
	for (int i = 0; i < 2; i++)
	{
		threads[i] = CreateThread(NULL, 0, ThreadFunction, &threadIDs[i], 0, NULL);
		if (threads[i] == NULL)
		{
			printf("스레드 생성 실패\n");
			return 1;
		}
	}

	// 스레드 종료 대기
	WaitForMultipleObjects(2, threads, TRUE, INFINITE);
	// 스레드 핸들 닫기
	for (int i = 0; i < 2; i++)
	{
		CloseHandle(threads[i]);
	}

	// 접속 대기
	new_socket = accept(server_fd, (struct sockaddr*)&address, &addrlen);

	// 데이터 수신
	while (1)
	{
		memset(buffer, 0, sizeof(buffer));
		recv(new_socket, buffer, BUFFER_SIZE, 0);
		printf("클라이언트 : %s\n", buffer);
		if (strcmp(buffer, "stop") == 0) break;
	}

	// 접속 종료
	closesocket(new_socket);
	closesocket(server_fd);
}

// 송신 스레드
DWORD WINAPI ThreadFunction(LPVOID param) {
	int id = *(int*)param;
	for (int i = 0; i < 5; i++)
	{
		printf("Thread %d 실행 중...%d\n", id, i);
		Sleep(1000);
	}
	return 0;
}

 

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <Windows.h>

#pragma comment(lib, "ws2_32.lib")

#define PORT 8080
#define BUFFER_SIZE 1024

DWORD WINAPI thread_tx(LPVOID param);
DWORD WINAPI thread_rx(LPVOID param);

int main(void) {
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);

	SOCKET server_fd, new_socket;
	struct sockaddr_in address;
	int addrlen = sizeof(address);
	char buffer[BUFFER_SIZE] = { 0 };
	char* message = "Hello from server";

	// 소켓 설정
	server_fd = socket(AF_INET, SOCK_STREAM, 0);
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(PORT);

	// 소켓 연결
	bind(server_fd, (struct sockaddr*)&address, sizeof(address));
	listen(server_fd, 3);

	printf("서버 대기 중... 포트 : %d\n", PORT);

	// 새로운 스레드 생성
	HANDLE thread_transmit, thread_receive;
	thread_transmit = CreateThread(NULL, 0, thread_tx, NULL, 0, NULL);
	thread_receive = CreateThread(NULL, 0, thread_rx, NULL, 0, NULL);
	if (thread_transmit == NULL) printf("transmit 스레드 생성 실패\n");
	if (thread_receive == NULL) printf("receive 스레드 생성 실패\n");


	// 접속 대기
	new_socket = accept(server_fd, (struct sockaddr*)&address, &addrlen);

	// 데이터 수신
	while (1)
	{
		memset(buffer, 0, sizeof(buffer));
		recv(new_socket, buffer, BUFFER_SIZE, 0);
		printf("클라이언트 : %s\n", buffer);
		if (strcmp(buffer, "stop") == 0) break;
	}

	// 접속 종료
	closesocket(new_socket);
	closesocket(server_fd);

	// 스레드 종료 대기
	WaitForMultipleObjects(2, thread_transmit, TRUE, INFINITE);
	WaitForMultipleObjects(2, thread_receive, TRUE, INFINITE);

	// 스레드 핸들 닫기
	CloseHandle(thread_transmit);
	CloseHandle(thread_receive);
}

// 송신 스레드
DWORD WINAPI thread_tx(LPVOID param) {
	for (int i = 0; i < 5; i++)
	{
		printf("송신 스레드 실행 중...%d\n", i);
		Sleep(1000);
	}
	return 0;
}

// 수신 스레드
DWORD WINAPI thread_rx(LPVOID param) {
	for (int i = 0; i < 5; i++)
	{
		printf("수신 스레드 실행 중...%d\n", i);
		Sleep(800);
	}
	return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <Windows.h>

#pragma comment(lib, "ws2_32.lib")

#define PORT 8080
#define BUFFER_SIZE 1024

DWORD WINAPI thread_tx(LPVOID param);
DWORD WINAPI thread_rx(LPVOID param);

SOCKET new_socket;
typedef enum {
	NONE = 0,
	RUN,
	STOP,
	EXIT
} STATE_APP;

STATE_APP stateApp;

int main(void) {
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);
	SOCKET server_fd;
	struct sockaddr_in address;
	int addrlen = sizeof(address);
	
	char* message = "Hello from server";

	// 소켓 설정
	server_fd = socket(AF_INET, SOCK_STREAM, 0);
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(PORT);

	// 소켓 연결
	bind(server_fd, (struct sockaddr*)&address, sizeof(address));
	listen(server_fd, 3);

	printf("서버 대기 중... 포트 : %d\n", PORT);

	// 접속 대기
	new_socket = accept(server_fd, (struct sockaddr*)&address, &addrlen);

	// 새로운 스레드 생성
	HANDLE thread_transmit, thread_receive;
	thread_transmit = CreateThread(NULL, 0, thread_tx, NULL, 0, NULL);
	if (thread_transmit == NULL) printf("transmit 스레드 생성 실패\n");
	thread_receive = CreateThread(NULL, 0, thread_rx, NULL, 0, NULL);
	if (thread_receive == NULL) printf("receive 스레드 생성 실패\n");

	while (stateApp != EXIT);

	// 접속 종료
	closesocket(new_socket);
	closesocket(server_fd);

	// 스레드 종료 대기
	WaitForMultipleObjects(1, thread_transmit, TRUE, INFINITE);
	WaitForMultipleObjects(1, thread_receive, TRUE, INFINITE);

	// 스레드 핸들 닫기
	CloseHandle(thread_transmit);
	CloseHandle(thread_receive);
}

// 송신 스레드
DWORD WINAPI thread_tx(LPVOID param) {
	for (int i = 0; i < 10; i++)
	{
		printf("송신 스레드 실행 중...%d\n", i);
		Sleep(1000);
	}
	stateApp = EXIT;
	return 0;
}

// 수신 스레드
DWORD WINAPI thread_rx(LPVOID param) {
	char buffer[BUFFER_SIZE] = { 0 };

	// 데이터 수신
	while (1)
	{
		memset(buffer, 0, sizeof(buffer));
		recv(new_socket, buffer, BUFFER_SIZE, 0);
		printf("클라이언트 : %s\n", buffer);
		if (strcmp(buffer, "stop") == 0) break;
	}
	
	return 0;
}

 

서버용

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <Windows.h>

#pragma comment(lib, "ws2_32.lib")

#define PORT 8080
#define BUFFER_SIZE 1024

DWORD WINAPI thread_tx(LPVOID param);
DWORD WINAPI thread_rx(LPVOID param);

SOCKET new_socket;
typedef enum {
	NONE = 0,
	RUN,
	STOP,
	EXIT
} STATE_APP;

STATE_APP stateApp;

int main(void) {
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);
	SOCKET server_fd;
	struct sockaddr_in address;
	int addrlen = sizeof(address);
	
	char* message = "Hello from server";

	// 소켓 설정
	server_fd = socket(AF_INET, SOCK_STREAM, 0);
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(PORT);

	// 소켓 연결
	bind(server_fd, (struct sockaddr*)&address, sizeof(address));
	listen(server_fd, 3);

	printf("서버 대기 중... 포트 : %d\n", PORT);

	// 접속 대기
	new_socket = accept(server_fd, (struct sockaddr*)&address, &addrlen);

	// 새로운 스레드 생성
	HANDLE thread_transmit, thread_receive;
	thread_transmit = CreateThread(NULL, 0, thread_tx, NULL, 0, NULL);
	if (thread_transmit == NULL) printf("transmit 스레드 생성 실패\n");
	thread_receive = CreateThread(NULL, 0, thread_rx, NULL, 0, NULL);
	if (thread_receive == NULL) printf("receive 스레드 생성 실패\n");

	while (stateApp != EXIT);

	// 접속 종료
	closesocket(new_socket);
	closesocket(server_fd);

	// 스레드 종료 대기
	/*WaitForMultipleObjects(1, thread_transmit, TRUE, INFINITE);
	WaitForMultipleObjects(1, thread_receive, TRUE, INFINITE);*/

	// 스레드 핸들 닫기
	CloseHandle(thread_transmit);
	CloseHandle(thread_receive);
}

// 송신 스레드
DWORD WINAPI thread_tx(LPVOID param) {
	char txBuffer[80];
	for (int i = 0; i < 10; i++)
	{
		fgets(txBuffer, sizeof(txBuffer), stdin);
		if (strncmp(txBuffer, "disconnect", 10) == 0) break;
		send(new_socket, txBuffer, strlen(txBuffer), 0);
	}
	stateApp = EXIT;
	return 0;
}

// 수신 스레드
DWORD WINAPI thread_rx(LPVOID param) {
	char buffer[BUFFER_SIZE] = { 0 };

	// 데이터 수신
	while (1)
	{
		memset(buffer, 0, sizeof(buffer));
		recv(new_socket, buffer, BUFFER_SIZE, 0);
		if (stateApp == EXIT) break;
		printf("클라이언트 : %s\n", buffer);
		if (strcmp(buffer, "stop") == 0) break;
	}
	
	return 0;
}

 

클라이언트용

#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#pragma comment(lib, "ws2_32.lib")

#define IS_CLIENT
#define SERVER_IP "127.0.0.1"
#define PORT 8080
#define BUFFER_SIZE 1024

DWORD WINAPI thread_tx(LPVOID param);
DWORD WINAPI thread_rx(LPVOID param);

SOCKET sock, new_socket;
typedef enum {
	NONE = 0,
	RUN,
	STOP,
	EXIT
} STATE_APP;

STATE_APP stateApp;

int main(void) {
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);

	struct sockaddr_in address;
	int addrlen = sizeof(address);

	// 소켓 설정
	sock = socket(AF_INET, SOCK_STREAM, 0);
	address.sin_family = AF_INET;
	address.sin_port = htons(PORT);
#ifdef IS_CLIENT
	//address.sin_addr.s_addr = inet_addr(SERVER_IP);
	if (inet_pton(AF_INET, SERVER_IP, &address.sin_addr) != 1) {
		printf("inet_pton 변환 실패! 잘못된 IP 주소: %s\n", SERVER_IP);
		return 1;
	}
#else
	address.sin_addr.s_addr = INADDR_ANY;
#endif

	// 소켓 연결
#ifdef IS_CLIENT
	connect(sock, (struct sockaddr*)&address, sizeof(address));
#else
	bind(sock, (struct sockaddr*)&address, sizeof(address));
	listen(sock, 3);

	printf("서버 대기 중... 포트 : %d\n", PORT);

	// 접속 대기(waiting...)
	new_socket = accept(sock, (struct sockaddr*)&address, &addrlen);
#endif

	// 새로운 스레드 생성
	HANDLE thread_transmit, thread_receive;
	thread_transmit = CreateThread(NULL, 0, thread_tx, NULL, 0, NULL);
	if (thread_transmit == NULL) printf("transmit 스레드 생성 실패\n");
	thread_receive = CreateThread(NULL, 0, thread_rx, NULL, 0, NULL);
	if (thread_receive == NULL) printf("receive 스레드 생성 실패\n");

	while (stateApp != EXIT);

	// 접속 종료
	closesocket(new_socket);
	closesocket(sock);

	// 스레드 종료 대기
	//WaitForMultipleObjects(1, thread_transmit, TRUE, INFINITE);
	//WaitForMultipleObjects(1, thread_receive, TRUE, INFINITE);
	// 스레드 핸들 닫기
	CloseHandle(thread_transmit);
	CloseHandle(thread_receive);
}

// 송신 스레드
DWORD WINAPI thread_tx(LPVOID param) {
	char txBuffer[80];
	while (1) {
		fgets(txBuffer, sizeof(txBuffer), stdin);
		if (strncmp(txBuffer, "disconnect", 10) == 0)
			break;
#ifdef IS_CLIENT
		send(sock, txBuffer, strlen(txBuffer), 0);
#else
		send(new_socket, txBuffer, strlen(txBuffer), 0);
#endif
	}
	stateApp = EXIT;
	return 0;
}

// 수신 스레드
DWORD WINAPI thread_rx(LPVOID param) {
	char buffer[BUFFER_SIZE] = { 0 };

	// 데이터 수신
	while (1) {
		memset(buffer, 0, sizeof(buffer));
#ifdef IS_CLIENT
		recv(sock, buffer, BUFFER_SIZE, 0);
#else
		recv(new_socket, buffer, BUFFER_SIZE, 0);
#endif
		if (stateApp == EXIT) break;
		printf("클라이언트 : %s\n", buffer);
		if (strcmp(buffer, "stop") == 0) break;
	}

	return 0;
}

 

 

'(Telechips) AI 시스템 반도체 SW 개발자 교육 > C' 카테고리의 다른 글

C언어(9)  (0) 2025.02.28
C언어(7)  (0) 2025.02.26
C언어(6)  (0) 2025.02.25
C언어(5)  (0) 2025.02.24
C언어(4)  (0) 2025.02.21