반응형

아래 링크와 같이 WSL에 설치한 vLLM 서버를 실행하고 사용해 보자.

2026.06.19 - [AI, ML, DL] - [vLLM] WSL 에서 vLLM 설치 및 간단한 실행

 

Background에서 서버를 실행하고 프로세스를 확인.

 

■ 서버 실행하기 (Foreground에서 실행된다)

VLLM_USE_FLASHINFER_SAMPLER=0 python3 -m vllhttp://m.entrypoints.openai.api_server --model LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ --trust_remote_code True --gpu_memory_utilization=0.7

 

■ 서버 실행하기 (Background에서 실행되고 vllm.log 파일에 로그가 기록된다)

VLLM_USE_FLASHINFER_SAMPLER=0 python3 -m vllhttp://m.entrypoints.openai.api_server --model LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ --trust_remote_code True --gpu_memory_utilization=0.7 > vllm.log 2>&1 &

 

VLLM_USE_FLASHINFER_SAMPLER=0은 vLLM이 다음 토큰을 선택(샘플링)할 때 FlashInfer 라이브러리의 고속 커널을 사용하지 않고, PyTorch/Triton 기반의 기본 내장 샘플러를 사용하도록 강제하는 환경 변수 설정이다. 내 시스템에선 이 옵션 없이는 실행할 수 없었다.

 

■ 백그라운드에서 동작중인 서버 프로세스 확인 (백그라운드에서 동작하는 프로세스 중 vllm이라는 문자열과 VLLM이라는 문자열을 검색한다)

ps -ef | grep -E "vllm|VLLM"

 

■ 백그라운드 서버 종료

pkill -f vllhttp://m.entrypoints.openai.api_server

 

 

동작중인 서버를 이용해보자. 아래 코드를 작성하고 실행한다.

from openai import OpenAI

# vLLM 서버 주소 설정 (기본 포트 8000)
client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="vllm-token" # api_key는 아무 문자열이나 넣어도 된다.
)

# 단발성 답변 받기 (General Request)
print("=== 일반 답변 요청 ===")
response = client.chat.completions.create(
    model="LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ",  # 동작중인 서버의 모델 이름과 일치해야 한다.
    messages=[
        {"role": "system", "content": "당신은 친절한 AI 도우미입니다."},
        {"role": "user", "content": "인공지능과 거대언어모델(LLM)의 차이점을 한 문장으로 설명해줘."}
    ],
    temperature=0.2,
    top_p=0.95,
    max_tokens=1024
)

print(response.choices[0].message.content)

print()

# 실시간 스트리밍 답변 받기 (Streaming Request)
print("=== 스트리밍 답변 요청 ===")
stream = client.chat.completions.create(
    model="LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ",
    messages=[
        {"role": "system", "content": "당신은 친절한 AI 요리사 도우미 입니다."},
        {"role": "user", "content": "맛있는 김치찌개를 끓이는 비법을 짧게 알려줘."}
    ],
    temperature=0.2,
    top_p=0.95,
    max_tokens=1024,
    stream=True, # 스트리밍 활성화
)

for chunk in stream:
    if chunk.choices[0].delta.content is not None:
        print(chunk.choices[0].delta.content, end="", flush=True)

print()

 

답변 첫 부분에 빈 줄과 </thought> 태그가 따라 나온다.

 

불필요한 태그를 제거해 보자.

from openai import OpenAI

# vLLM 서버 주소 설정 (기본 포트 8000)
client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="vllm-token" # api_key는 아무 문자열이나 넣어도 된다.
)

# 단발성 답변 받기 (General Request)
print("=== 일반 답변 요청 ===")
response = client.chat.completions.create(
    model="LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ",  # 동작중인 서버의 모델 이름과 일치해야 한다.
    messages=[
        {"role": "system", "content": "당신은 친절한 AI 도우미입니다."},
        {"role": "user", "content": "인공지능과 거대언어모델(LLM)의 차이점을 한 문장으로 설명해줘."}
    ],
    temperature=0.2,
    top_p=0.95,
    max_tokens=1024
)

#print(response.choices[0].message.content) # 태그 포함 답변 출력
content = response.choices[0].message.content

# </thought> 태그가 포함되어 있다면 분할 처리
if "</thought>" in content:
    # thought_process: 모델이 생각한 과정 (필요시 사용)
    # final_answer: 사용자가 원하는 최종 답변
    thought_process, final_answer = content.split("</thought>", 1)
    #print("=== [AI의 생각 과정] ===")
    #print(thought_process.replace("<thought>", "").strip()) # 시작 태그가 남아있다면 제거
    print(final_answer.strip())
else:
    # 태그가 없는 일반적인 경우 그대로 출력
    print(content)

print()

# 실시간 스트리밍 답변 받기 (Streaming Request)
print("=== 스트리밍 답변 요청 ===")
stream = client.chat.completions.create(
    model="LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ",
    messages=[
        {"role": "system", "content": "당신은 친절한 AI 요리사 도우미 입니다."},
        {"role": "user", "content": "맛있는 김치찌개를 끓이는 비법을 짧게 알려줘."}
    ],
    temperature=0.2,
    top_p=0.95,
    max_tokens=1024,
    stream=True, # 스트리밍 활성화
)

# 실시간 문자열에서 불필요한 </thought> 태그 처리를 위한 변수 선언
full_text = ""
has_passed_thought = False

for chunk in stream:
    if chunk.choices[0].delta.content is not None:
        #print(chunk.choices[0].delta.content, end="", flush=True) # 태그 포함 답변 출력

        token = chunk.choices[0].delta.content
        full_text += token  # 들어오는 토큰을 전체 버퍼에 누적

        # </thought> 태그가 지나갔는지 확인
        if not has_passed_thought:
            if "</thought>" in full_text:
                # 태그가 끝나는 지점을 찾아 그 이후의 텍스트만 추출
                _, final_start_text = full_text.split("</thought>", 1)
                has_passed_thought = True
                
                # 태그 뒤에 공백이나 줄바꿈이 있다면 깔끔하게 지우고 첫 출력
                first_output = final_start_text.lstrip()
                if first_output:
                    print(first_output, end="", flush=True)
            else:
                # 아직 </thought> 태그가 나오기 전(생각 중)이라면 화면에 출력하지 않고 건너뛴다.
                continue
        else:
            # 만약 앞서 출력된 내용이 아직 아무것도 없다면 (sys.stdout이 비어있음)
            # 다음에 들어오는 토큰들도 첫 글자가 나올 때까지 앞쪽 공백/줄바꿈을 계속 지워준다.
            if not full_text.split("</thought>", 1)[1].lstrip():
                # 여전히 공백이나 줄바꿈만 들어오는 상태이므로 출력하지 않고 스킵.
                continue
                
            # 글자가 들어오기 시작하면, 그 시점의 토큰부터 그대로 출력.
            # (단, 공백이 섞여 들어왔을 수 있으므로 첫 진입 토큰만 lstrip 처리)
            if len(full_text.split("</thought>", 1)[1].lstrip()) == len(token):
                print(token.lstrip(), end="", flush=True)
            else:
                print(token, end="", flush=True)

print()

 

불필요한 태그 없이 깔끔하게 정리되었다.

 

 

또한 WSL에 서버를 실행한 상태에서 네이티브 윈도우에서도 위와 동일한 코드로 LLM을 사용할 수 있다.

아니면 WSL에서 hostname -I 명령으로 확인한 IP 주소값을 base_url에 넣어도 된다.

from openai import OpenAI

# vLLM 서버 주소 설정 (기본 포트 8000)
client = OpenAI(
    base_url="http://localhost:8000/v1",
    #base_url="http://172.21.167.101:8000/v1",
    api_key="vllm-token" # api_key는 아무 문자열이나 넣어도 된다.
)

# 단발성 답변 받기 (General Request)
print("=== 일반 답변 요청 ===")
response = client.chat.completions.create(
    model="LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ",  # 동작중인 서버의 모델 이름과 일치해야 한다.
    messages=[
        {"role": "system", "content": "당신은 친절한 AI 도우미입니다."},
        {"role": "user", "content": "인공지능과 거대언어모델(LLM)의 차이점을 한 문장으로 설명해줘."}
    ],
    temperature=0.2,
    top_p=0.95,
    max_tokens=1024
)

print(response.choices[0].message.content)

print()

# 실시간 스트리밍 답변 받기 (Streaming Request)
print("=== 스트리밍 답변 요청 ===")
stream = client.chat.completions.create(
    model="LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ",
    messages=[
        {"role": "system", "content": "당신은 친절한 AI 요리사 도우미 입니다."},
        {"role": "user", "content": "맛있는 김치찌개를 끓이는 비법을 짧게 알려줘."}
    ],
    temperature=0.2,
    top_p=0.95,
    max_tokens=1024,
    stream=True, # 스트리밍 활성화
)

for chunk in stream:
    if chunk.choices[0].delta.content is not None:
        print(chunk.choices[0].delta.content, end="", flush=True)

print()

 

네이티브 윈도우에서 WSL 서버 사용.

 

※ 참고

LG AI Research의 공식 가이드에 따르면 EXAONE Deep 모델은 시스템 프롬프트를 지원하지 않거나 권장하지 않는다. 시스템 프롬프트를 넣으면 모델이 지시사항을 무시하거나 추론(<thought>)을 시작하지 못하고 엉뚱한 답변을 낼 확률이 높아진다. 요리사 같은 페르소나는 아래와 같이 user 메시지 안에 녹여내자.

response = client.chat.completions.create(
    model="LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ",
    messages=[
        # 시스템 프롬프트를 없애고 유저 메시지에 역할을 녹여낸다.
        {"role": "user", "content": "당신은 친절한 AI 도우미입니다. 인공지능과 거대언어모델(LLM)의 차이점을 한 문장으로 설명해줘."}
    ],
    temperature=0.2,
    top_p=0.95,
    max_tokens=1024
)

 

반응형
Posted by J-sean
:
반응형

MySQL(MariaDB)과 구글 차트를 연동해 보자.

 

아래 내용을 참고해 웹서버와 데이터베이스를 준비한다.

2021.08.25 - [Linux] - Linux(Ubuntu) Build Your Own Web Server - 리눅스(우분투)로 웹서버 만들기

2021.08.28 - [Linux] - Linux(Ubuntu) MariaDB(MySQL) Server Remote Access - 데이터베이스 원격 접속

(데이터베이스는 로컬로 사용하므로 원격 설정을 할 필요는 없다)

 

위와 같은 데이터베이스와 테이블을 생성한다.

 

시간, 온도, 습도 데이터를 적당히 입력한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<html>
<head>
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
    <!--
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
    -->
 
    <?php
        // 에러가 발생하면 내용 표시
        error_reporting(E_ALL);
        ini_set('display_errors''1');
 
        $mysql_host = "localhost";
        $mysql_user = "root";
        $mysql_password = "1234";
        $mysql_db = "test_db";
 
        $conn = mysqli_connect($mysql_host$mysql_user$mysql_password$mysql_db);
        if (!$conn) {
            die("Database Connect Error: " . mysqli_connect_error());
        }
            
        //echo "Database Connected.<br><br>";
            
        $sql = "SELECT * FROM test_tb";
        $result = mysqli_query($conn$sql);
        
        if (mysqli_num_rows($result> 0) {
            while ($row = mysqli_fetch_assoc($result)) {
                $data_array[] = $row;
            }
            $chart = json_encode($data_array);
        } else {
            echo "No Data";
        }
        
        //echo $chart;
 
        mysqli_close($conn);
    ?>
 
    <script type="text/javascript">
        google.charts.load('current', { packages: ['corechart''line'] });
        google.charts.setOnLoadCallback(drawChart);
        
        function drawChart() {
            var chart_array = <?php echo $chart; ?>;
            //console.log(JSON.stringify(chart_array))
            var header = ['dt''temp''humid'];
            var row = "";
            var rows = new Array();
            jQuery.each(chart_array, function(index, item) {
                row = [
                    item.dt,
                    Number(item.temp),
                    Number(item.humid)
                ];
                rows.push(row); 
            });
 
            var jsonData = [header].concat(rows);
            var data = new google.visualization.arrayToDataTable(jsonData);
            var options = {
                title: 'Temperaure & Humid',
                hAxis: {
                    title: 'Time'
                },
                series: {
                    0: { targetAxisIndex: 0 },
                    1: { targetAxisIndex: 1 }
                },
                vAxes: {
                    0: {
                        title: 'Temperature',
                        viewWindow: { min: -30, max: 50 }
                    },
                    1: {
                        title: 'Humid',
                        viewWindow: { min: 30, max: 100 }
                    }
                }
                //,
                //curveType: 'function',
                //legend: { position: 'bottom' }
            };
 
            var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
            chart.draw(data, options);
        }
    </script>
</head>
<body>
    <div id="chart_div" style="width: 900px; height: 500px"></div>
</body>
</html>
 

 

소스를 입력하고 웹서버에 저장한다.(/var/www/html/index.php)

 

웹서버에 접속하면 위와 같은 그래프가 표시된다.

 

 

X축 레이블을 좀 더 보기 편하게 바꾸고 테이블 차트도 추가해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<html>
<head>
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
    <!--
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
    -->
 
    <?php
        // 에러가 발생하면 내용 표시
        error_reporting(E_ALL);
        ini_set('display_errors''1');
 
        $mysql_host = "localhost";
        $mysql_user = "root";
        $mysql_password = "1234";
        $mysql_db = "test_db";
 
        $conn = mysqli_connect($mysql_host$mysql_user$mysql_password$mysql_db);
        if (!$conn) {
            die("Database Connect Error: " . mysqli_connect_error());
        }
            
        //echo "Database Connected.<br><br>";
            
        $sql = "SELECT * FROM test_tb";
        $result = mysqli_query($conn$sql);
        
        if (mysqli_num_rows($result> 0) {
            while ($row = mysqli_fetch_assoc($result)) {
                $data_array[] = $row;
            }
            $chart = json_encode($data_array);
        } else {
            echo "No Data";
        }
        
        //echo $chart;
 
        mysqli_close($conn);
    ?>
 
    <script type="text/javascript">
        google.charts.load('current', { packages: ['corechart''line'] });
        google.charts.load('current', { packages: ['table'] });
        google.charts.setOnLoadCallback(drawChart);
        
        function drawChart() {
            var chart_array = <?php echo $chart; ?>;
            //console.log(JSON.stringify(chart_array))
            var header = ['Date&Time(MM-DD HH:MM)''Temp''Humid'];
            var row = "";
            var rows = new Array();
            jQuery.each(chart_array, function(index, item) {
                row = [
                    item.dt.substr(511),  // 너무 긴 날짜 및 시간을 짧게 추출
                    Number(item.temp),
                    Number(item.humid)
                ];
                rows.push(row); 
            });
 
            var jsonData = [header].concat(rows);
            var data = new google.visualization.arrayToDataTable(jsonData);
 
            var lineChartOptions = {
                title: 'Temperaure & Humid',
                hAxis: {
                    title: 'Time',
                    showTextEvery: 4    // X축 레이블이 너무 많아 보기 힘드므로 4개마다 하나씩 표시
                },
                series: {
                    0: { targetAxisIndex: 0 },
                    1: { targetAxisIndex: 1 }
                },
                vAxes: {
                    0: {
                        title: 'Temperature',
                        viewWindow: { min: -30, max: 50 }
                    },
                    1: {
                        title: 'Humid',
                        viewWindow: { min: 30, max: 100 }
                    }
                }
                //,
                //curveType: 'function',
                //legend: { position: 'bottom' }
            };
 
            var lineChart = new google.visualization.LineChart(document.getElementById('lineChart_div'));
            lineChart.draw(data, lineChartOptions);
 
            // 테이블 차트
            var tableChartOptions = {
                showRowNumber: true,
                width: '40%',
                height: '20%'
            }
 
            var tableChart = new google.visualization.Table(document.getElementById('tableChart_div'));
            tableChart.draw(data, tableChartOptions);
        }
    </script>
</head>
<body>
    <div id="lineChart_div" style="width: 900px; height: 500px"></div>
    <div id="tableChart_div"></div>
</body>
</html>
 

 

소스를 수정하고 웹서버에 저장한다.(/var/www/html/index.php)

 

웹서버에 접속하면 위와 같은 그래프와 차트가 표시된다.

 

※ 참고

2022.05.05 - [Web Development] - Google Chart - 구글 차트 1

반응형
Posted by J-sean
:
반응형

안드로이드 앱을 위한 리눅스(우분투) 데이터베이스(MySQL/MariaDB) 서버를 만들어 보자.

 

app/res/xml/network_security_config.xml 파일을 작성한다.

 

AndroidManifest.xml 에 인터넷 권한과 networkSecurityConfig를 추가한다.

 

리니어 레이아웃에 에디트텍스트, 버튼, 텍스트뷰를 적당히 배치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.example.myapplication;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText1;
    EditText editText2;
    TextView textView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText1 = findViewById(R.id.editText1);
        editText2 = findViewById(R.id.editText2);
        textView = findViewById(R.id.textView);
 
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String id = editText1.getText().toString();
                String name = editText2.getText().toString();
 
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        RequestData(id, name);
                    }
                }).start();
            }
        });
    }
 
    public void RequestData(String id, String name) {
        try {
            BufferedReader bufferedReader;
            StringBuilder stringBuilder = new StringBuilder();
 
            String urlString = "http://192.168.171.200/data_check.php";
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            if (conn != null) {
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);
                conn.setRequestMethod("POST");
                conn.setDoInput(true);
                conn.setDoOutput(true);
                conn.connect();
 
                String parameter = "id=" + id + "&name=" + name;
                OutputStream outputStream = conn.getOutputStream();
                outputStream.write(parameter.getBytes(StandardCharsets.UTF_8));
                outputStream.flush();
                outputStream.close();
 
                int responseCode = conn.getResponseCode();
 
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                } else {
                    bufferedReader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
                }
 
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line + "\n");
                }
 
                textView.setText(stringBuilder);
 
                bufferedReader.close();
                conn.disconnect();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 

 

서버에 데이터를 요청하고 표시하는 소스를 작성한다. (MainActivity.java)

 

 

리눅스(우분투) 서버 데이터베이스에 위와 같은 테이블과 데이터를 생성한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
    $mysql_host = "localhost";
    $mysql_user = "root";
    $mysql_password = "1234";
    $mysql_db = "member_db";
 
    $conn = mysqli_connect($mysql_host$mysql_user$mysql_password$mysql_db);
 
    if (!$conn) {
        die("Database Connect Error: " . mysqli_connect_error());
    }
 
    $android = strpos($_SERVER['HTTP_USER_AGENT'], "Android");
 
    if ((($_SERVER['REQUEST_METHOD'== 'POST'&& isset($_POST['submit'])) || $android)
    {
        $post_id = trim($_POST['id']);
        $post_name = trim($_POST['name']);
 
        $sql = "SELECT * FROM member WHERE id='$post_id' AND name='$post_name'";
        $result = mysqli_query($conn$sql);
 
        if (mysqli_num_rows($result== 1 ) {
            $member = mysqli_fetch_assoc($result);
            echo "ID: " . $member['id'] . " Name: " . $member['name'] . " Age: " . $member['age'];
        } else {
            echo "No Data.";
            mysqli_close($conn);
            exit;
        }
    } else {
        echo "No Android Device Detected.";
    }
 
    mysqli_close($conn);
?>
 

 

아파치 서버 디렉토리에 안드로이드 앱 요청을 처리할 PHP 파일을 작성한다. (/var/www/html/data_check.php)

 

앱을 실행하고 정보를 요청하면 Age 데이터가 표시된다.

 

잘못된 정보 요청.

 

 

 

안드로이드가 아닌 다른 운영체제로 서버에 접속.

※ 참고

SELinux 유효성 검사

 

반응형
Posted by J-sean
:
반응형

Everything에는 http server, ftp server를 만들 수 있는 기능이 있다. http server를 이용해 간단히 원하는 파일이나 폴더를 공유해 보자. (ftp server도 거의 비슷한 방법으로 만들 수 있다)

 

Everything이 설치되어 있다.

 

Options...를 클릭한다.

 

HTTP Server를 선택한다. (ETP/FTP Server도 비슷한 방식으로 만들면 된다)

 

Enable HTTP server를 선택하고 Username과 Password를 입력한다.

 

 

Indexes - Exclude를 선택한다.

 

Enable exclude list를 선택하고 Add Folder... 버튼을 클릭해 공유(검색)에서 제외할 폴더나 드라이브를 선택한다.

 

Apply 버튼과 OK 버튼을 클릭한다.

 

인터넷 브라우저 주소창에 localhost를 입력한다.

 

 

아니면 127.0.0.1을 입력하고 사용자이름과 비밀번호를 입력한다.

 

제외된 C~H 드라이브 빼고 I 드라이브만 표시된다.

 

검색창에 폴더명이나 파일명을 입력해서 찾을 수 도 있다.

 

공유기를 사용한다면 포트포워드 설정으로 외부에서도 파일을 공유할 수 있다.

 

 

Options - Indexes - NTFS - Settings for XXX - Include in database 에서 공유(검색) 드라이브를 추가하거나 제외할 수 도 있다.

 

필요하다면 subst 명령으로 원하는 폴더를 드라이브로 지정하고 그 드라이브만 database에 추가해서 공유한다.

 

반응형
Posted by J-sean
:
반응형

2019.10.08 - [Linux] - Build your own web server with Fedora linux - 페도라 리눅스로 간단한 웹서버 만들기

2021.08.25 - [Linux] - Linux(Ubuntu) Static IP Configuration - 리눅스(우분투) 고정 IP

 

리눅스(우분투)로 간단한 웹서버를 만들어 보자.

 

패키지 리스트를 업데이트 한다.

 

패키지를 업데이트 한다.

 

apache2를 설치 한다.

 

active 상태인지 확인 한다.

 

■ systemctl 명령어
- start: 서비스 시작
- stop: 서비스 중지
- status: 서비스 상태 확인
- restart: 서비스 재시작
- reload: 서비스를 중지하지 않고 설정값을 반영
- enable: 시스템 재부팅시 자동으로 서비스 실행
- disable: enable 해제

 

서버에 접속하기 위해 IP 주소를 확인 한다.

 

 

다른 컴퓨터로 접속했을때 위와 같은 Default Page가 나오면 정상이다.

 

Default Page를 수정해 보자.

 

Default Page의 HTML 코드. 모두 삭제 한다.

 

위와 같은 코드를 입력한다.

 

다시 서버에 접속해 보면 직접 입력한 내용이 출력 된다.

 

 

PHP를 설치 한다.

 

PHP 코드를 입력하기 위한 파일을 만든다.

 

PHP 정보를 표시하는 간단한 코드를 입력 한다.

 

IP 주소/PHP 파일(http://192.168.171.128/index.php) 로 접속하면 PHP 정보를 표시하는 화면이 표시된다.

 

반응형
Posted by J-sean
:
반응형

리눅스(우분투) 서버 같은 콘솔 환경에서도 Ogg, MP3, WAV 등의 음악 파일을 재생 할 수 있다.


cmus(C* Music Player)를 설치한다.


설치가 완료되면 cmus를 실행한다.


cmus가 실행되고 아무것도 표시되지 않는다. 방향키, 엔터등을 눌러도 아무런 반응이 없지만 당황하지 말고 숫자 5를 누른다.


Browser가 표시된다. 음악 파일이 있는 디렉토리로 이동한다.



원하는 파일에서 엔터키를 누르면 플레이 된다. 하지만 선택한 1개의 파일만 플레이 되므로 간단한 사용방법을 알아 보자.


각 파일에서 a키(add to library)를 눌러 준다. (y키를 누르면 playlist로 추가된다)


숫자 1을 누른다. Library 화면이 표시되고 위에서 add한 파일이 모두 추가 되었다. 원하는 곡에서 엔터키를 눌러 플레이 할 수 있다. (Artist/Album과 Track은 Tab키로 이동한다)


숫자 2를 누르면 Sorted 화면이 표시된다.



숫자 3을 누르면 Playlist 화면이 표시된다.


숫자 4를 누르면 Queue 화면이 표시된다. Queue에 추가(e)되는 노래는 순서를 무시하고 지금 재생되는 노래 다음에 재생된다. 재생 후 queue에서 자동 삭제된다.


숫자 5를 누르면 Browser 화면이 표시된다.


숫자 6을 누르면 Filters 화면이 표시된다.



숫자 7을 누르면 Settings 화면이 표시된다. 각 단축키를 확인할 수 있다. 아래는 주요 단축키 목록이다.

  • x: play

  • v: stop

  • c: pause

  • b: next

  • z: prev

  • right: seek +5

  • left: seek -5

  • -: vol -10%

  • +/=: vol +10%

  • C(대문자): toggle continue

  • r: toggle repeat

  • s: toggle shuffle

  • t: toggle show_remaining_time

  • a: add(to library)

  • e: add(to queue)

  • y: add(to playlist)

  • delete: remove

  • q: quit


WAV 파일은 aplay로 간단히 재생할 수 있다.


반응형
Posted by J-sean
:
반응형

NodeMCU(ESP-12E/ESP8266)를 이용해 LED를 제어하는 서버를 만들어 보자.


2020/03/10 - [Raspberry Pi & Arduino] - How to program ESP32-CAM using Arduino UNO - 아두이노로 ESP32-CAM 프로그래밍 하기


NodeMCU를 준비한다.


ESP8266모듈이 내장된 NodeMCU를 사용하기 위해 'Preferences - Additional Boards ManagerURLs:'에 http://arduino.esp8266.com/stable/package_esp8266com_index.json을 입력한다.


Boards Manager에서 esp를 검색하고 esp8266을 설치한다.


Tools - Board에서 NodeMCU 1.0 (ESP-12E Module)을 선택한다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <ESP8266WiFi.h>
 
const char* ssid = "Your ssid";
const char* password = "Your password";
 
WiFiServer server(80);
WiFiClient client;
String request;
int status = LOW;
 
void setup() {
  Serial.begin(9600);
 
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
 
  Serial.print("Connecting to ");
  Serial.println(ssid);
 
  WiFi.begin(ssid, password);
  // Tells the server to begin listening for incoming connections.
 
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected!!");
  server.begin();
  Serial.println("Server started.");
 
  Serial.print("Use this URL to connect to the server: ");
  Serial.print("http://");
  Serial.println(WiFi.localIP());
}
 
void loop() {    
  client = server.available();
  // Gets a client that is connected to the server and has data available for reading.
  // The connection persists when the returned client object goes out of scope;
  // you can close it by calling client.stop().
  if (!client) {
    delay(100);
    
    return;
  }
  
  Serial.println("New client connected.");    
  
  while (!client.available()) {
    // Returns the number of bytes available for reading (that is, the amount of data
    // that has been written to the client by the server it is connected to).
    delay(100);
  }
 
  request = client.readStringUntil('\r');
  Serial.print("Request from the client: ");
  Serial.println(request);
  client.flush();
 
  if (request.indexOf("LED=ON"!= -1) {
    digitalWrite(LED_BUILTIN, HIGH);
    status = HIGH;
  } else if (request.indexOf("LED=OFF"!= -1) {
    digitalWrite(LED_BUILTIN, LOW);
    status = LOW;
  }
 
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println("");
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");
  client.print("LED is turned ");
  if (status)
    client.print("On");
  else
    client.print("Off");
  client.println("<br><br>");
  client.println("<a href=\"/LED=ON\" title=\"Turn the LED on\"><button>Turn on</button></a>");
  client.println("<a href=\"/LED=OFF\" title=\"Turn the LED off\"><button>Turn off</button></a>");
  client.println("</html>");  
 
  //client.stop();
  // client.stop()을 호출하면 제대로 작동하지 않는다.
  Serial.println("Client disconnected!!");
  delay(100);
}


위 소스를 컴파일하고 NodeMCU에 업로드한다.


시리얼 모니터를 확인하면 와이파이에 연결되고 URL이 표시된다.


스마트폰이나 컴퓨터로 URL에 접속하면 위와 같은 화면이 표시된다.


'Turn on'버튼을 클릭하면 서버로 URL/LED=ON 요청을 보내고 서버에서는 LED=ON 문자열을 감지해 NodeMCU의 built-in LED를 켠다. (실제 built-in LED의 동작은 반대로 된다. LED가 꺼진다.)


'Turn off'버튼을 클릭하면 서버로 URL/LED=OFF 요청을 보내고 서버에서는 LED=OFF 문자열을 감지해 NodeMCU의 built-in LED를 끈다. (실제 built-in LED의 동작은 반대로 된다. LED가 켜진다.)


반응형
Posted by J-sean
:
반응형

간단한 웹서버용 HTML 문서 작성 예제


2019/10/08 - [Linux] - Build your own web server with Fedora linux - 페도라 리눅스로 간단한 웹서버 만들기


아래 내용으로 index.html 파일을 작성 하고 /var/www/html에 저장 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!doctype html>
<html lang="ko">
    <head>
        <meta charset="utf-8">
        <title>J.Sean's simple web server</title>
        <style>
            a{
                text-decoration:none;
                color:black;
            }
 
        </style>
    </head>
 
    <body>
        <h1>
        <br>--- J.sean's simple web server ---
        <br>OS: Fedora
        <br>HTTP Server: Apache
        <br>
        <a href="https://s-engineer.tistory.com/" target="_blank">Click me to visit J.Sean's blog</a><br><br>
 
        Alien 3<br>
        <video src="./data/Alien.mp4" width="960" height="520" autoplay controls>
        <track label="Korean" kind="subtitles" srclang="ko" src="./data/Alien.vtt" default></video><br><br>
        
        <a href="https://s-engineer.tistory.com/" target="_blank">
        <img src="./download/animal.jpg">
        </a>
 
        <br>
        <a href="./download/file.zip" download>File (file.zip)</a><br>
 
        </h1>
    </body>
</html>



./data, ./download 디렉토리를 만들고 index.html 내용에 필요한 데이터를 저장 한다.


서버에 접속하면 비디오 플레이어, 웹링크, 파일링크, 사진등이 표시된 웹 페이지가 열린다.


반응형
Posted by J-sean
: