在 Windows 寫 Linux 程式:Visual Studio

透過 Visual Studio 來開發 Linux 上的程式,主要是透過 SSH 來連到一個 Linux 系統來進行的;而偵錯的部分,則是會使用 gdbserver 來進行,也可以支援中斷點、以及中斷時的變數監控。

本文章將示範在 透過 Visual Studio 透過SSH連線到Ubuntu 18.04 上編譯開發TCP Socket程式碼。

本次教學檔案:

電腦環境:

作業系統:Windows 10

開發軟體:Visual Studio 2022

虛擬機軟體:VirtualBox 7.0.2

一、安裝 Visual Studio 2022

Visual Studio官網下載Community 2022版本安裝器後安裝

安裝 Visual Studio 2022 工作套件

這裡我們要勾選2樣,第一樣在使用傳統與行動裝置中的使用C++的桌面開發及其他工具組中的使用C++進行Linux和內嵌開發,安裝大約10Gb左右。

勾選使用C++的桌面開發
勾選C++進行Linux和內嵌開發
等待安裝完成

二、安裝跟設定Ubuntu 18.04

要使用Visual Studio編譯開發 Linux 上的程式,會需要安裝Windows子系統Ubuntu或是安裝其他的VM來連線開發,本次使用的是 VirtualBox 來安裝 Ubuntu 18.04 並設定WSL 來讓 Visual Studio 可以遠程連線到 Linux 中做編譯。

7.0以前安裝方式請參考以下文章

關於Linux的基本指令可以參考以下文章

安裝完 VirtualBox 後按新增

新增一個虛擬機

設定名稱及選取ISO檔

我這裡命名為UB1804-1
下拉選單選其他
點選我們以下載的ISO檔
勾選跳過簡易安裝不然會有BUG無法正常使用
配置記憶體及CPU數量,這裡依照自己的電腦配備建議記體配4G,CPU分一半給虛擬機,然後點選下一步
硬碟配置建議給到50GB以上,設定好後點選下一步
檢查配置訊息,沒問題後點選Finish
啟動剛剛創建的ub18虛擬機
選取自己想要的語言後點選右邊安裝
選取鍵盤排列方式這裡直接繼續即可
勾選為圖形跟wif硬體安裝額外插件後點繼續
這裡使用預設的就可以了,點選立刻安裝
時區預設因該會抓到台北點選繼續
使用者帳號密碼使用自己習慣的自動登入打勾後點選繼續
等待安裝完成
安裝虛擬機驅動點選裝置插入映像檔
按下執行並輸入密碼來安裝插件
等待安裝完成並重新啟動系統
調整解析度到自己是適合的大小
設定共用剪貼簿為雙向
找到網路設定
設定附加到橋接介面卡,橋接到自己正在使用的網卡上,我是使用wif所以橋接到wif上

安裝完畢後需要來設定WSL

安裝必要的套件


由於實際上會在 WSL 的環境下,進行程式的編譯,所以必須要安裝編譯程式時,所需要使用到的套件;一般來說就是 g++ 、make 等套件了~而根據專案的不同,也會需要安裝必要的函式庫。

官方建議的安裝命令,是直接安裝「build-essential」:

sudo apt install build-essential

如果需要遠端偵錯還需要安裝gdb:

sudo apt-get install g++ gdb make ninja-build rsync zip

開啟終端機貼上指令安裝

安裝完成畫面

不過,由於接下來要使用 Visual Studio 透過 SSH 連線來做遠端編譯的控制,所以還需要安裝「openssh-server」;而如果還要遠端偵錯的話,也還需要安裝「gdbserver」。

所以這邊也可以透過下面的指令,來安裝這兩個套件:

sudo apt install openssh-server gdbserver
安裝完成畫面

設定 SSH Server

在將套件安裝好了之後,接下來則是要針對 OpenSSH Server 做基本的設定。他的設定檔是「/etc/ssh/sshd_config」,只要以系統管理員權限、使用文字編輯器打開就可以了。

這邊官方的範例是使用 nano 這個簡易式的文字編輯器,其指令為:

sudo nano /etc/ssh/sshd_config

總共要修改二個地方才行:

# What ports, IPs and protocols we listen for 
Port 22

# Change to no to disable tunnelled clear text passwords 
PasswordAuthentication yes

在修改好了之後,只要按 Ctrl + X,然後再按 y,就可以儲存修改的內容了。

而在都設定好了之後,接下來還要產生一組 SSH 的金鑰,其指令是:

sudo ssh-keygen -A

啟動 SSH Server

上面的設定步驟,基本上都是只要做一次的設定,當設定好了之後,以後就會存下來,不用每次都重新跑一次。

不過,由於 WSL 的設計並不支援自動啟動的背景服務,所以 SSH Server 不但無法自動啟動、當每次把 bash 視窗關閉時,已經開啟的 SSH Server 也都會被自動關閉。

所以,每次要使用時,都需要手動啟動 OpenSSH Server!其指令是:

sudo service ssh start

三、Visual Studio 的基本使用

來實作一個網路上的教學:

https://www.geeksforgeeks.org/socket-programming-cc/

Visual Studio 安裝好 Visual C++ for Linux Development 後,在建立新專案時,可以在 Visual C++ 下的「跨平台」中,找到「Linux」的分類,這邊會有一些範例專案可以選擇:

選取第二個會印出hello的先驗證環境
設定專案名稱
專案中會有說明如何連線到Linux

我們先去查詢虛擬機的ip地址,打開終端機先安裝net-tools工具

sudo apt install net-tools
安裝完成畫面

然後輸入ifconfig查詢:

ifconfig
這裡可以看到我的是192.168.50.38

接著到 Visual Studio 中設定到工具-跨平台-連線管理員中新增Linux系統連線資訊

新增一個Linux系統
ip打上剛剛查詢到的,使用者跟密碼打Ubuntu上的按下連線
第一次連線點選接受金鑰
新增成功可以點選驗證來看是不是有成功連線

這時候可以對專案右鍵點選建置來看建置的狀況

右鍵點選建置
可以看到左下角有顯示成功

這時候可以到Ubuntu系統資料夾中查到已經編譯的專案

多出一個projects資料夾

資料夾projects/demo/bin/x64/Debug裡面會有一個.out的執行檔可以執行

資料夾右鍵以終端機開啟

打上要執行的檔案

./demo.out 
成功執行

接下來依照https://www.geeksforgeeks.org/socket-programming-cc/的內容來實作

首先雖然專案創建時語言是寫C++,但我們把程式檔名改成.c就可以撰寫c語言了,我們在原有的專案中加入一個新的專案。

在右側方案’Demo’專案中,右鍵-加入-新增專案
選空白專案
專案名稱我們先做Server端所以叫Server
對著 Server 專案右鍵加入-新增項目
這裡手動命名檔案為Server.c

在 Server.c 貼入以下程式碼:

// Server side C/C++ program to demonstrate Socket
// programming
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080
int main(int argc, char const* argv[])
{
	int server_fd, new_socket, valread;
	struct sockaddr_in address;
	int opt = 1;
	int addrlen = sizeof(address);
	char buffer[1024] = { 0 };
	char* hello = "Hello from server";

	// Creating socket file descriptor
	if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket failed");
		exit(EXIT_FAILURE);
	}

	// Forcefully attaching socket to the port 8080
	if (setsockopt(server_fd, SOL_SOCKET,
				SO_REUSEADDR | SO_REUSEPORT, &opt,
				sizeof(opt))) {
		perror("setsockopt");
		exit(EXIT_FAILURE);
	}
	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(PORT);

	// Forcefully attaching socket to the port 8080
	if (bind(server_fd, (struct sockaddr*)&address,
			sizeof(address))
		< 0) {
		perror("bind failed");
		exit(EXIT_FAILURE);
	}
	if (listen(server_fd, 3) < 0) {
		perror("listen");
		exit(EXIT_FAILURE);
	}
	if ((new_socket
		= accept(server_fd, (struct sockaddr*)&address,
				(socklen_t*)&addrlen))
		< 0) {
		perror("accept");
		exit(EXIT_FAILURE);
	}
	valread = read(new_socket, buffer, 1024);
	printf("%s\n", buffer);
	send(new_socket, hello, strlen(hello), 0);
	printf("Hello message sent\n");

	// closing the connected socket
	close(new_socket);
	// closing the listening socket
	shutdown(server_fd, SHUT_RDWR);
	return 0;
}
好了後對 Server 專案右鍵建置
看到左下角編譯成功就ok了
這時候回到Ubuntu會發現多了 一個 Server 資料夾裡面就是我們編譯完成的檔案
這裡我先把 Server 端執行起來

接下來用一樣的方法再創一個client端的專案

右鍵新增一個專案
選取一個空白專案命名為client
右鍵新增項目
建立一個client.c檔

在client.c中貼上以下程式碼:

// Client side C/C++ program to demonstrate Socket
// programming
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080

int main(int argc, char const* argv[])
{
	int sock = 0, valread, client_fd;
	struct sockaddr_in serv_addr;
	char* hello = "Hello from client";
	char buffer[1024] = { 0 };
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		printf("\n Socket creation error \n");
		return -1;
	}

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(PORT);

	// Convert IPv4 and IPv6 addresses from text to binary
	// form
	if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)
		<= 0) {
		printf(
			"\nInvalid address/ Address not supported \n");
		return -1;
	}

	if ((client_fd
		= connect(sock, (struct sockaddr*)&serv_addr,
				sizeof(serv_addr)))
		< 0) {
		printf("\nConnection Failed \n");
		return -1;
	}
	send(sock, hello, strlen(hello), 0);
	printf("Hello message sent\n");
	valread = read(sock, buffer, 1024);
	printf("%s\n", buffer);

	// closing the connected socket
	close(client_fd);
	return 0;
}
右鍵建置
看到左下角沒有錯誤就完成了
接下來可以在 資料夾中看到 client 的專案
執行 client 端程式碼後就可以發現可以正常溝通了

Comments

No comments yet. Why don’t you start the discussion?

    發佈留言

    發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *