본문 바로가기

리눅스 멀티스레드 파일 공유 프로그램 - 6 본문

개인 프로젝트 공부

리눅스 멀티스레드 파일 공유 프로그램 - 6

Seongjun_You 2024. 6. 10. 21:11

멀티스레드를 적용해 본다.

linux에서는 스레드 코드를 디버깅할 때는 인자값을 추가해주어야 한다.

 

tasks.json에 args에 -lpthread 인자값을 추가해 준다.

 

 

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <fstream>
#include <pthread.h>

#define PORT 8080
#define BUFFER_SIZE 1024
using namespace std;


void *clientHandler(void *newsocket)
{
    int new_socket = *((int *)newsocket);
    char buffer[BUFFER_SIZE];
    string str_buffer;
    int valread = recv(new_socket, buffer, BUFFER_SIZE, 0);
    str_buffer = string(buffer, valread);


    if(str_buffer == "1234")
    {
        // read text file
        ifstream file("/home/sj/share_file_project/root_file_dir/secret.txt");
        string line;
        getline(file, line);
        cout << "client socket number is : " << new_socket << endl;
        send(new_socket, line.c_str(), line.length(), 0);
    }

    else
        str_buffer = "Failed";
    send(new_socket, str_buffer.c_str(), str_buffer.length(), 0);

    // 소켓 종료
    close(new_socket);
}


int main() {
    int server_fd, new_socket;
    int opt = 1;
    
    // 소켓 파일 디스크립터 생성
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        std::cerr << "Socket creation error" << std::endl;
        return -1;
    }


    //if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
    //    std::cerr << "setsockopt failed" << std::endl;
    //    close(server_fd);
    //    return -1;
    //}

    // 주소 구조체 설정
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // 소켓을 주소와 바인드
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        std::cerr << "Bind failed" << std::endl;
        return -1;
    }

    // 클라이언트의 연결을 대기
    if (listen(server_fd, 3) < 0) {
        std::cerr << "Listen failed" << std::endl;
        return -1;
    }

    //system("chown root:root server_test");
    //system("chmod 700 server_test");
    // 연결 수락
    while(true)
    {
        struct sockaddr_in client_addr;
        int client_addrlen = sizeof(client_addr);
        if ((new_socket = accept(server_fd, (struct sockaddr *)&client_addr, (socklen_t*)&client_addrlen)) < 0) {
            std::cerr << "Accept failed" << std::endl;
            return -1;
        }
        pthread_t thread;
        int result = pthread_create(&thread, NULL, clientHandler, &new_socket);
        pthread_detach(thread);
    }  
    close(server_fd);

    return 0;
}

연결을 승인하는 부분의 코드가 달라졌다.

 

 

 

 

while(true)
    {
        struct sockaddr_in client_addr;
        int client_addrlen = sizeof(client_addr);
        if ((new_socket = accept(server_fd, (struct sockaddr *)&client_addr, (socklen_t*)&client_addrlen)) < 0) {
            std::cerr << "Accept failed" << std::endl;
            return -1;
        }
        pthread_t thread;
        int result = pthread_create(&thread, NULL, clientHandler, &new_socket);
        pthread_detach(thread);
    }  
    close(server_fd);

client의 정보를 담기 위해 client_addr을 새로 만들어 accept 매개변수에 넣어주었다.

thread의 정보를 담기 위해 thread변수를 만들어주고

스레드를 생성하는 pthread_create함수를 실행한다.

 

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

항수형태를 보면

첫 번째 매개변수는 생성된 스레드의 식별자를 저장할 변수의 포인터이다.

두 번째는 스레드의 속성을 지정하는 데 사용되며 디폴트는 NULL이다.

세 번째는 새로 생성된 스레드가 실행할 함수이며 void *형태로 인수를 받아 void *로 반환하는 형태를 지켜야 한다.

네 번째는 세 번째에 정의된 함수의 인수값으로 들어간다.

 

 

pthread_detach()와 pthread_join()에 대해서도 차이점이 있다.

detach는 분리된 스레드를 독립적으로 실행하게 하며

종료되면 알아서 리소스를 회수한다.

join은 스레드가 종료될 때까지 기다리며

join함수를 통해 스레드의 종료 상태와 리소스를 회수한다.

 

나는 클라이언트가 서버에 연결되면 파일만 전송하고 종료되는 일회성 작업이기에

detach를 사용하기로 했다.

 

 

 

 

void *clientHandler(void *newsocket)
{
    int new_socket = *((int *)newsocket);
    char buffer[BUFFER_SIZE];
    string str_buffer;
    int valread = recv(new_socket, buffer, BUFFER_SIZE, 0);
    str_buffer = string(buffer, valread);


    if(str_buffer == "1234")
    {
        // read text file
        ifstream file("/home/sj/share_file_project/root_file_dir/secret.txt");
        string line;
        getline(file, line);
        cout << "client socket number is : " << new_socket << endl;
        send(new_socket, line.c_str(), line.length(), 0);
    }

    else
        str_buffer = "Failed";
    send(new_socket, str_buffer.c_str(), str_buffer.length(), 0);

    // 소켓 종료
    close(new_socket);
}

소켓 통신이 진행되면 스레드가 생성되어 해당 함수를 진행한다.

newsocket정보는 항상 void *형태로 받기에 타입을 바꾸어준다.

그 후 파일 내용을 돌려주는 코드를 작성한다.

 

 

 

서버에서는 통신된 소켓 번호를 출력을 해주고 클라이언트들은 파일의 내용을 전달받는다.

Comments