2024-02-28
大学课程
00

目录

1 WSL + VS Code + C/C++配置
2 服务端代码
3 客户端代码
4 运行结果
5 WSL其他问题

实验目标:通过套接字,创建客户端和服务端,并且实现基础的互相通信。

环境: Ubuntu 22.04 LTS(WSL),gcc 11.04,GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1

1 WSL + VS Code + C/C++配置

先安装WSL(必须是WSL2),并且在WSL中安装C/C++编译器,在VS Code中安装WSL扩展,C/C++所需扩展(后续记得在WSL中安装): image.png

安装C/C++编译器命令:

  1. sudo apt install gcc-11
  2. sudo apt install g++-11
  3. sudo apt install gdb

image.png

之后在文件管理器里找到WSL的位置,随便创建一个.c文件,然后使用VS Code打开,并且在左侧的资源管理器里打开文件夹:

image.png

打开之后,左下角会提示:We are working on folders in \wsl...,点击“Reopen Folder in WSL”:

image.png

在这个项目文件夹下新建.vscode文件夹,并且新建三个文件(settings.json不必要):

image.png

c_cpp_properties.json(C语言版):

json
{ "configurations": [ { "name": "Linux", // includePath中的内容除了第一条 剩下的在WSL中运行"gcc-11 -v -E -x c -"(c配置)或"gcc-11 -v -E -x c++ -"(c++配置)后 见下图寻找内容 "includePath": [ "${workspaceFolder}/**", "/usr/lib/gcc/x86_64-linux-gnu/11/include", "/usr/local/include", "/usr/include/x86_64-linux-gnu", "/usr/include" ], "defines": [], "compilerPath": "/usr/bin/gcc", // 编译器路径 "intelliSenseMode": "linux-gcc-x64", "cStandard": "c11" // c语言标准 } ], "version": 4 }

image.png

c_cpp_properties.json(C++语言版):

json
{ "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/**", "/usr/include/c++/11", "/usr/include/x86_64-linux-gnu/c++/11", "/usr/include/c++/11/backward", "/usr/lib/gcc/x86_64-linux-gnu/11/include", "/usr/local/include", "/usr/include/x86_64-linux-gnu", "/usr/include" ], "defines": [], "compilerPath": "/usr/bin/g++", "intelliSenseMode": "linux-gcc-x64", "cStandard": "c11" } ], "version": 4 }

launch.json:

json
{ // 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。 // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "gcc-11 - 生成和调试活动文件", "type": "cppdbg", "request": "launch", "program": "${fileDirname}/${fileBasenameNoExtension}", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "miDebuggerPath": "gdb", "setupCommands": [ { "description": "为 gdb 启用整齐打印", "text": "-enable-pretty-printing", "ignoreFailures": true } ], "preLaunchTask": "Launch: build active file" } ] }

tasks.json(C语言版):

json
{ "tasks": [ { "type": "cppbuild", "label": "Launch: build active file", "command": "/usr/bin/gcc", // 编译器路径 "args": [ "-fdiagnostics-color=always", "-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}" ], "options": { "cwd": "${fileDirname}" }, "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true }, "detail": "use /usr/bin/gcc-11" } ], "version": "2.0.0" }

task.json(C++语言版):

json
{ "tasks": [ { "type": "cppbuild", "label": "Launch: build active file", "command": "/usr/bin/g++", "args": [ "-fdiagnostics-color=always", "-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}" ], "options": { "cwd": "${fileDirname}" }, "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true }, "detail": "use /usr/bin/gcc-11" } ], "version": "2.0.0" }

之后,就可以编译并且运行C/C++代码了:

image.png

2 服务端代码

c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> // 创建一个简单UDP服务器 监听1324端口 int main(int argc, char const *argv[]) { printf("start monitor on 127.0.0.1:1324 \n"); // 创建套接字 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // SOCK_DGRAM是UDP套接字 // sockfd这个名字中的fd 是文件描述符的缩写 在unix/linux中 fd是进程独有的文件描述符表的索引 它的意思是 socket可以被视作一个文件 if (sockfd == -1) { // 创建socket失败 perror("create socket failed: \n"); // 该函数会查找全局变量errno 并且将其保存的错误码转换为可读的报错信息 // 全局变量errno 是大部分标准库函数出错时保存报错码的位置 return -1; } // 设置服务器地址 struct sockaddr_in addr; addr.sin_family = AF_INET; // 地址族 addr.sin_port = htons(1324); // 端口 addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP 另外 INADDR_ANY可表示0.0.0.0 // 将套接字绑定到服务器地址上 int ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); // addr这里 可以视作做了一个转型 if (0 > ret) { // 绑定失败 perror("bind failed: \n"); close(sockfd); // 关闭套接字 return -1; } // 设置客户端地址 这个结构体的作用是在下面的recvfrom函数中保存发送者的地址信息 struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); char buffer[1024]; //缓冲区 while (1) { ssize_t bytes_recv = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_len); if (bytes_recv == -1) { // 接收信息错误 perror("receive message error: \n"); continue; } char client_ip[INET_ADDRSTRLEN]; // INET_ADDRSTRLEN表示 32 位 IPv4 地址 的字符串表示形式所需的最大长度 为16 inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); // 该函数用于将二进制格式的IPv4或IPv6地址 转换为文本格式 int clinet_port = ntohs(client_addr.sin_port); // 转换字节的网络序和主机序 这里的ntohs是Network to Host Short的缩写 网络序转主机16位无符号短整数(u_short) // 类似的还有htonl: Host to Network u_long // htons: Host to Network u_short // ntohl: Network to Host u_long printf("client ip: %s, port: %d\n", client_ip, clinet_port); printf("received from client: %s\n", buffer); memset(buffer, 0, sizeof(buffer)); // 清除缓冲区 const char* response = "Hello from server! \0"; ssize_t bytes_send = sendto(sockfd, response, strlen(response), 0, (struct sockaddr*)&client_addr, client_len); // 发回去 if (bytes_send == -1) { // 没发成功 perror("sending message error: \n"); } } close(sockfd); return 0; }

3 客户端代码

c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> int main(int argc, char const *argv[]) { printf("start sending message to server on 127.0.0.1:1324\n"); // 创建客户端套接字 int socketfd = socket(AF_INET, SOCK_DGRAM, 0); if (socketfd == -1) { perror("create socket failed: \n"); return -1; } // 创建服务器端地址 struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(1324); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 开始发送数据 char* message = "Hello from client! \0"; ssize_t bytes_sent = sendto(socketfd, message, strlen(message), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (bytes_sent == -1) { perror("sending message failed: \n"); close(socketfd); return -1; } // 开始接收数据 char buffer[1024]; ssize_t bytes_recv = recvfrom(socketfd, buffer, sizeof(buffer), 0, NULL, NULL); if (bytes_recv == -1) { perror("receiving data failed: \n"); close(socketfd); return -1; } printf("received from server: %s\n", buffer); // 关闭套接字 close(socketfd); return 0; }

4 运行结果

客户端:

image.png

服务端:

image.png

5 WSL其他问题

  1. 修改WSL用户密码

在PowerShell中,运行wsl.exe --user root进入WSL,之后再运行passwd root(root替换要修改密码的用户名)来修改用户名:

image.png

  1. WSL在默认情况下是英文,没有中文,所以中文显示会是乱码。对于Ubuntu来说,安装以下解决问题:
bash
sudo apt install fonts-noto-cjk
  1. WSL文件管理器,Chrome安装,
bash
sudo apt install nautilus -y wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb sudo apt install --fix-missing ./google-chrome-stable_current_amd64.deb
  1. Linux运行程序报错:error while loading shared libraries: libQt5Widgets.so.5: cannot open shared object file: No such file or directory

运行sudo apt-get install libqt5widgets5解决。

  1. Linux运行二进制文件报错

image.png

该文件没有执行权限,chmod 777 mNetAssist即可解决。

  1. 其他问题见官方说明 在适用于 Linux 的 Windows 子系统上运行 Linux GUI 应用

本文作者:御坂19327号

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!