实验目标:通过套接字,创建客户端和服务端,并且实现基础的互相通信。
环境: Ubuntu 22.04 LTS(WSL),gcc 11.04,GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
先安装WSL(必须是WSL2),并且在WSL中安装C/C++编译器,在VS Code中安装WSL扩展,C/C++所需扩展(后续记得在WSL中安装):
安装C/C++编译器命令:
sudo apt install gcc-11
sudo apt install g++-11
sudo apt install gdb
之后在文件管理器里找到WSL的位置,随便创建一个.c文件,然后使用VS Code打开,并且在左侧的资源管理器里打开文件夹:
打开之后,左下角会提示:We are working on folders in \wsl...,点击“Reopen Folder in WSL”:
在这个项目文件夹下新建.vscode文件夹,并且新建三个文件(settings.json不必要):
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
}
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++代码了:
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;
}
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;
}
客户端:
服务端:
在PowerShell中,运行wsl.exe --user root
进入WSL,之后再运行passwd root
(root替换要修改密码的用户名)来修改用户名:
bashsudo apt install fonts-noto-cjk
bashsudo 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
运行sudo apt-get install libqt5widgets5
解决。
该文件没有执行权限,chmod 777 mNetAssist
即可解决。
本文作者:御坂19327号
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!