Windows下VScode实现简单回声服务的方法
1. 相关知识
1.1 什么是回声服务
回声服务端可以将客户端传来的信息,再原封不动地发送给客户端,因而得名 epoch 服务。服务端 server 和 客户端 client 基于 TCP 进行通信。
1.2 服务端、客户端如何交互
下图给出了基于 TCP 的服务器端和客户端的交互过程。
首先服务端创建 socket 套接字,之后调用 bind 函数分配服务端 socket 地址,调用 listen 函数使服务端进入监听状态,同时维护一个半连接队列。服务端之后会调用 accept 函数,进入阻塞状态。accept 函数会从全连接的队列中取出一个连接进行处理。TCP 连接建立完成之后,服务端和客户端即可通过 send 和 recv 发送和接收数据。
注意:服务端调用 listen 函数进入等待连接状态后,客户端才能调用 connect 函数发起连接请求。
服务端和客户端交互就是一种通信过程,它们基于 TCP 实现 socket 通信。TCP 协议中有三次握手、四次挥手的协议内容,如下图所示。
服务端和客户端通过三次握手建立连接,四次挥手断开连接。
具体到socket编程实现,则是通过 listen 和 connect 函数实现 TCP 连接的建立,通过 close 函数关闭 socket 套接字,实现TCP连接的断开。
2. socket 编程
下面分别介绍客户端和服务端的常用函数和具体实现过程。
2.1 服务端
服务端的实现过程如下图所示。
下面给出实现基于TCP的服务端的常用函数。
1.首先需要对 Winsock 套接字库进行初始化,调用 WSAStartup 函数。
下面给出 WSAStartup 函数调用的基本格式,一般只需调用即可,无需了解参数含义。
#include <winsock2.h> int main(int argc, char* argv[]) { WSADATA wsaData; if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0) ErrorHandling("WSAStartup() error!"); return 0; }
成功时返回 0 ,失败返回非零的错误代码值。
2.创建 socket 套接字
SOCKET socket(int af, int type, int protocol);
成功时返回套接字句柄,失败返回 INVALID_SOCKET。
3.调用 bind 函数,为套接字分配 IP 地址和端口号
int bind(SOCKET s, const struct sockaddr * name, int namelen);
成功时返回 0,失败返回 SOCKET_ERROR。
4.调用 listen 函数,监听客户端连接
int listen(SOCKET s, int backlog);
成功时返回 0 ,失败返回 SOCKET_ERROR 。
5.调用 accept 函数,允许客户端连接
SOCKET accept(SOCKET s, struct sockaddr * addr, int * addrlen);
成功时返回套接字句柄,失败返回 INVALID_SOCKET 。
6.调用 send 函数, 给连接的客户端发送数据
int send(SOCKET s, const char * buf, int len, int flags):
成功时返回传输字节数,失败返回 SOCKET_ERROR 。
7.调用 recv 函数,接收连接的客户端发来的数据
int recv(SOCKET s, const char * buf, int len, int flags);
成功时返回接收字节数,失败返回 SOCKET_ERROR 。
8.调用 close 函数,关闭套接字。
int closesocket(SOCKET s);
成功时返回 0 ,失败时返回 SOCKET_ERROR 。
9.注销 Winsock 相关库
int WSACleanup(void);
成功时返回 0 ,失败时返回 SOCKET_ERROR 。
2.2 客户端
客户端的实现过程如下图所示。
下面给出实现基于TCP的客户端的常用函数。
1.创建 socket 套接字
SOCKET socket(int af, int type, int protocol);
成功时返回套接字句柄,失败返回 INVALID_SOCKET。
2.调用connect函数,发起连接请求
int connect(SOCKET s, const struct sockaddr * name, int namelen);
成功时返回 0,失败返回 SOCKET_ERROR。
3.调用 send 函数, 给连接的服务端发送数据
int send(SOCKET s, const char * buf, int len, int flags):
成功时返回传输字节数,失败返回 SOCKET_ERROR 。
4.调用 recv 函数,接收连接的服务端发来的数据
int recv(SOCKET s, const char * buf, int len, int flags);
成功时返回接收字节数,失败返回 SOCKET_ERROR 。
5.调用 close 函数,断开连接。
int closesocket(SOCKET s);
成功时返回 0 ,失败时返回 SOCKET_ERROR 。
3. demo展示
3.1 服务端源代码
回声服务端的C++代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") #define BUF_SIZE 1024 void ErrorHandling(char *message); int main(int argc, char *argv[]) { WSADATA wsaData; SOCKET hServSock, hClntSock; char message[BUF_SIZE]; int strLen, i; SOCKADDR_IN servAdr, clntAdr; int clntAdrSize; if (argc != 2) { printf("Usage : %s <port>\n", argv[0]); exit(1); } if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) ErrorHandling("WSAStartup() error!"); hServSock = socket(PF_INET, SOCK_STREAM, 0); if (hServSock == INVALID_SOCKET) ErrorHandling("socket() error"); memset(&servAdr, 0, sizeof(servAdr)); servAdr.sin_family = AF_INET; /*servAdr.sin_addr.s_addr = htonl(INADDR_ANY);*/ servAdr.sin_addr.s_addr = inet_addr("127.0.0.1"); servAdr.sin_port = htons(atoi(argv[1])); if (bind(hServSock, (SOCKADDR *)&servAdr, sizeof(servAdr)) == SOCKET_ERROR) ErrorHandling("bind() error"); if (listen(hServSock, 5) == SOCKET_ERROR) ErrorHandling("listen() error"); clntAdrSize = sizeof(clntAdr); for (i = 0; i < 5; i++) { hClntSock = accept(hServSock, (SOCKADDR *)&clntAdr, &clntAdrSize); if (hClntSock == -1) ErrorHandling("accept() error"); else printf("Connected client %d \n", i + 1); while ((strLen = recv(hClntSock, message, BUF_SIZE, 0)) != 0) send(hClntSock, message, strLen, 0); closesocket(hClntSock); } closesocket(hServSock); printf("game over"); WSACleanup(); return 0; } void ErrorHandling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
注意:运行服务端代码时,须加入命令行参数(端口号)。如代码所示, IP 地址已经绑定 127.0.0.1。配置 tasks.json 如下所示。
{ "version": "2.0.0", "tasks": [ { "type": "shell", "label": "C/C++: g++.exe build active file", "command": "E:\\mingw64\\bin\\g++.exe", "args": [ "-g", "${file}", "-lws2_32", "-o", "${fileDirname}\\${fileBasenameNoExtension}.exe" ], "options": { "cwd": "${workspaceFolder}" }, "problemMatcher": ["$gcc"], "group": { "kind": "build", "isDefault": true } } ] }
配置信息 launch.json 如下 。
{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "(gdb) 启动", "type": "cppdbg", "request": "launch", "program": "${fileDirname}/${fileBasenameNoExtension}.exe", "args": ["9190"], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "miDebuggerPath": "E:\\mingw64\\bin\\gdb.exe", "setupCommands": [ { "description": "为 gdb 启用整齐打印", "text": "-enable-pretty-printing", "ignoreFailures": true } ] } ] }
3.2 客户端源代码
回声客户端的C++代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #define BUF_SIZE 1024 void ErrorHandling(char *message); int main(int argc, char *argv[]) { WSADATA wsaData; SOCKET hSocket; char message[BUF_SIZE]; int strLen; SOCKADDR_IN servAdr; if (argc != 3) { printf("Usage : %s <IP> <port>\n", argv[0]); exit(1); } if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) ErrorHandling("WSAStartup() error!"); hSocket = socket(PF_INET, SOCK_STREAM, 0); if (hSocket == INVALID_SOCKET) ErrorHandling("socket() error"); printf("%s\n", argv[0]); printf("%s\n", argv[1]); printf("%s\n", argv[2]); memset(&servAdr, 0, sizeof(servAdr)); servAdr.sin_family = AF_INET; servAdr.sin_addr.s_addr = inet_addr(argv[1]); servAdr.sin_port = htons(atoi(argv[2])); if (connect(hSocket, (SOCKADDR *)&servAdr, sizeof(servAdr)) == SOCKET_ERROR) ErrorHandling("connect() error!"); else puts("Connected..........."); while (1) { fputs("Input message(Q to quit): ", stdout); fgets(message, BUF_SIZE, stdin); if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) break; send(hSocket, message, strlen(message), 0); strLen = recv(hSocket, message, BUF_SIZE - 1, 0); printf("Message from server: %s", message); } closesocket(hSocket); WSACleanup(); return 0; } void ErrorHandling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
同样,客户端也需要加入命令行参数 127.0.0.1 9190
运行。可以通过修改配置文件生成客户端。
也可以通过cmd或者终端生成客户端。cmd 方式如下:
首先通过 g++ 编译器对 client.cpp 文件进行编译生成 .exe 文件。
之后在终端中,输入 client.exe 127.0.0.1 9190
即可创建客户端。
3.3 运行结果
服务端可以服务 5 个客户端,即 accept 队列长度为 5。
客户端的运行结果如下,前5个客户端均与服务端连接成功,可以收到“回声”。第6次连接时,由于服务端断开连接,所以产生连接错误。
服务端的运行结果如下图所示。服务端可以连接5个客户端,之后服务端将断开连接。并显示 “game over”。
参考链接
深入理解TCP协议与UDP协议的原理及区别
VScode官方文档
到此这篇关于Windows下VScode实现简单回声服务的文章就介绍到这了,更多相关VScode回声服务内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!
相关文章
- 一般在线增加从库有两种方式,一种是通过mysqldump备份主库,恢复到从库,mysqldump是逻辑备份,数据量大时,备份速度会很慢,锁表的时间也会很长。另一种是通过xtrabackup工具备份主库,恢复到从库,xtrabackup是物理备份,备份速度快...2015-11-08
- 这篇文章主要介绍了vscode安装git及项目开发过程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-05-19
- 这篇文章主要介绍了Vscode上使用SQL的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-26
- 这篇文章主要介绍了VSCode 配置uni-app的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-07-11
- 这篇文章主要介绍了vscode搭建STM32开发环境的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-05-02
- 普通关闭 我的mysql是自己下载的tar包,自己设定安装目录来安装的。停止mysql服务,说来简单,但不知道的话,还真是挠头。在这和mysql入门的同学们共享:)正确方法是,进入mysql的bin目录下,然后执行./mysqladmin -uroot -p shut...2015-11-24
- 这篇文章主要介绍了VSCode C++多文件编译的简单使用方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-29
- 这篇文章主要给大家介绍了关于VSCode配置C#运行环境的完整步骤,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-12-08
- 这篇文章主要介绍了浅谈Vue开发人员的7个最好的VSCode扩展,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-01-20
- 这篇文章主要介绍了vscode和cmake编译多个C++文件的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-10
Windows配置VSCode+CMake+Ninja+Boost.Test的C++开发环境(教程详解)
这篇文章主要介绍了Windows配置VSCode+CMake+Ninja+Boost.Test的C++开发环境,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-05-12- 这篇文章主要介绍了vue配置多代理服务接口地址操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-08
如何使用VSCode配置Rust开发环境(Rust新手教程)
这篇文章主要介绍了如何使用VSCode配置Rust开发环境(Rust新手教程),本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-07-27vscode通过Remote SSH远程连接及离线配置的方法
这篇文章主要介绍了vscode通过Remote SSH远程连接及离线配置的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-16- Visual Studio Code是一款免费开源的现代化轻量级代码编辑器,支持几乎所有主流的开发语言的语法高亮、智能代码补全、自定义热键、括号匹配、代码片段、代码对比 Diff、GIT 等特性,这篇文章主要介绍了VSCode搭建C/C++编译环境,需要的朋友可以参考下...2020-05-15
- 这篇文章主要为大家详细介绍了AngularJS内建服务$location及$location功能,感兴趣的小伙伴们可以参考一下...2016-07-06
- 这篇文章主要介绍了VSCode 云同步扩展设置Settings Sync插件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-29
- 这篇文章主要介绍了C#创建Windows服务的实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
- 这篇文章主要介绍了VSCode格式化JS自动添加或去掉分号的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-10-12
VSCode 添加自定义注释的方法(附带红色警戒经典注释风格)
这篇文章主要介绍了VSCode 添加自定义注释的方法(附带红色警戒经典注释风格),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-08-28