基于C/S的数据包发送和接收程序的实现——计算机网络课程设计

我在大二上学期(2021-2022学年)的时候学习了计算机网络,同时呢也做了这个课程设计,当时这个课程设计选题有20几个,我选的是《基于C/S的数据包发送和接收程序的实现》,这个课题相对于其他课题要简单一些,都有什么课题我会在另一篇文章中发布,下面直接上代码!(程序环境以及课题报告都在代码后面,本文较长,请耐心阅读)

 

【关键词】  C/S模式 ;服务器 ;客户端 ;TCP/IP

 

服务器代码如下:

  1 #define _WINSOCK_DEPRECATED_NO_WARNINGS     
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4 #include<iostream>
  5 #include<string>
  6 #include<cstring>
  7 #include<WS2tcpip.h>
  8 #include <WinSock2.h>                        //一般情况下,这个头文件位于windows.h之前,避免发生某些错误
  9 #include<Windows.h>
 10 #pragma comment(lib, "ws2_32.lib")             //加载 ws2_32.dll    ws2_32.dll就是最新socket版本
 11 using namespace std;
 12 
 13 int main()
 14 {
 15     cout << "-----------服务器-----------" << endl;
 16 
 17     //    1    初始化
 18     WSADATA wsadata;
 19     WSAStartup(MAKEWORD(2, 2), &wsadata);    
 20 
 21 
 22     //    2    创建服务器的套接字
 23     SOCKET serviceSocket;
 24     serviceSocket = socket(AF_INET, SOCK_STREAM, 0);    //socket(协议族,socket数据传输方式,某个协议)    
 25     if (SOCKET_ERROR == serviceSocket) {
 26         cout << "套接字创建失败!" << endl;
 27     }
 28     else {
 29         cout << "套接字创建成功!" << endl;
 30     }
 31 
 32 
 33     //    3    绑定套接字    指定绑定的IP地址和端口号
 34     sockaddr_in socketAddr;                                //一个绑定地址:有IP地址,有端口号,有协议族
 35     socketAddr.sin_family = AF_INET;
 36     socketAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");        //代码开头第一行我们定义的宏在这就其作用啦
 37     socketAddr.sin_port = htons(1234);
 38     int bRes = bind(serviceSocket, (SOCKADDR*)&socketAddr, sizeof(SOCKADDR));    //绑定注意的一点就是记得强制类型转换
 39     if (SOCKET_ERROR == bRes) {
 40         cout << "绑定失败!" << endl;
 41     }
 42     else {
 43         cout << "绑定成功!" << endl;
 44     }
 45 
 46     //    4    服务器监听    
 47     int lLen = listen(serviceSocket, 5);    //监听的第二个参数就是:能存放多少个客户端请求,到并发编程的时候很有用哦
 48     if (SOCKET_ERROR == lLen) {
 49         cout << "监听失败!" << endl;
 50     }
 51     else {
 52         cout << "监听成功!" << endl;
 53     }
 54 
 55 
 56     //    5    接受请求(客户端消息)
 57     sockaddr_in revClientAddr;
 58     SOCKET recvClientSocket = INVALID_SOCKET;    //初始化一个接受的客户端socket
 59     int _revSize = sizeof(sockaddr_in);
 60     recvClientSocket = accept(serviceSocket, (SOCKADDR*)&revClientAddr, &_revSize);
 61     if (INVALID_SOCKET == recvClientSocket) {
 62         cout << "服务端接受请求失败!" << endl;
 63     }
 64     else {
 65         cout << "服务端接受请求成功!" << endl;
 66     }
 67 
 68 
 69     //    6    发送/接受 数据(向客户端发送消息)
 70     char recvBuf[1024] = {};
 71     int reLen = recv(recvClientSocket, recvBuf, 1024, 0);
 72     int sLen = send(recvClientSocket, recvBuf, reLen, 0);
 73     if (SOCKET_ERROR == reLen) {
 74         cout << "服务端发送数据失败" << endl;
 75     }
 76     else {
 77         cout << "服务器接受到数据:    " << recvBuf << endl << endl;
 78     }
 79     while (true)
 80     {
 81 
 82         //    6    发送/接受 数据
 83         char recvBuf[1024] = {};
 84         int reLen = recv(recvClientSocket, recvBuf, 1024, 0);//阻塞函数,等待接受数据
 85 
 86         if (SOCKET_ERROR == reLen) {
 87             cout << "服务端发送数据失败" << endl;
 88         }
 89         else {
 90             cout << "请求命令:    " << recvBuf;
 91             if (0 == strcmp("cls", recvBuf)) {
 92                 //服务端执行命令    
 93                 system(recvBuf);
 94             }
 95             // 中文请求仅仅是为了测试可行性,一般都是用英文
 96             else if (0 == strcmp("获取版本信息", recvBuf)) {
 97                 //返回数据
 98                 string verData = "Version: 1.0.1\nAuthor: Primer\nReleaseData: 2021-12-12";
 99                 int sLen = send(recvClientSocket, (char*)verData.c_str(), verData.length(), 0);
100             }
101             else if (0 == strcmp("exit", recvBuf)) {
102                 cout << endl << "退出服务器" << endl;
103                 break;
104             }
105             else {
106                 cout << "\t不正确..." << endl;
107             }
108             cout << endl;
109         }
110     }
111 
112 
113     //    7    关闭socket
114     closesocket(recvClientSocket);
115     closesocket(serviceSocket);
116 
117     //    8    终止
118     WSACleanup();
119 
120     cout << "服务器停止" << endl;
121     cin.get();
122     return 0;
123 }

客户端代码:

 1 #define _WINSOCK_DEPRECATED_NO_WARNINGS 
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include<iostream>
 5 #include<string>
 6 #include<cstring>
 7 #include<WS2tcpip.h>
 8 #include <WinSock2.h>
 9 #include<Windows.h>
10 #pragma comment(lib, "ws2_32.lib")  //加载 ws2_32.dll
11 using namespace std;
12 
13 int main()
14 {
15     cout << "-----------客户端-----------" << endl;
16 
17     //    1    初始化
18     WSADATA wsadata;
19     WSAStartup(MAKEWORD(2, 2), &wsadata);
20 
21 
22     //    2    创建套接字
23     SOCKET clientSocket = {};
24     clientSocket = socket(PF_INET, SOCK_STREAM, 0);
25     if (SOCKET_ERROR == clientSocket) {
26         cout << "套接字创建失败!" << endl;
27     }
28     else {
29         cout << "套接字创建成功!" << endl;
30     }
31 
32 
33     //    3    绑定套接字    指定绑定的IP地址和端口号 向服务器发送消息
34     sockaddr_in socketAddr;
35     socketAddr.sin_family = PF_INET;
36     socketAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
37     socketAddr.sin_port = htons(1234);
38     int cRes = connect(clientSocket, (SOCKADDR*)&socketAddr, sizeof(SOCKADDR));
39     if (SOCKET_ERROR == cRes) {
40         cout << "客户端:\t\t与服务器连接失败....." << endl;
41     }
42     else {
43         cout << "客户端:\t\t与服务器连接成功....." << endl;
44     }
45 
46 
47     //    4    发送请求
48     char sendBuf[1024] = "from Client:   hello service.";
49     send(clientSocket, sendBuf, strlen(sendBuf), 0);
50 
51 
52     //    5    发送/接受 数据
53     char recvBuf[1024] = {};
54     recv(clientSocket, recvBuf, 1024, 0);
55     cout << "客户端接收数据    :    " << recvBuf << endl << endl;
56 
57     while (true)
58     {
59         //    4    发送请求
60         string s;
61         cout << "输入发送数据:\t";
62         getline(cin, s);                                    //可输入空格,默认以换行符结束输入,
63         send(clientSocket, (char*)s.c_str(), (int)s.length(), 0);
64 
65         //因为recv接受函数是阻塞函数,所以我们加以判断
66         //请求正确我才接收数据,否则不影响我继续请求
67         if (0 == strcmp("获取版本信息", s.c_str())) {
68             char recvBuf[4024] = {};
69             int reLen = recv(clientSocket, recvBuf, 4024, 0);//阻塞函数,等待接受数据
70             cout << endl << recvBuf << endl << endl;
71         }
72 
73     }
74 
75     //    6    关闭socket
76     closesocket(clientSocket);
77 
78 
79     //    7    终止
80     WSACleanup();
81 
82     cout << "客户端退出" << endl;
83     cin.get();
84     return 0;
85 }

 

  

以上两段代码需要分别建立两个空的工程项目,再分别copy进去,下面开始说一下环境的配置!

 

【摘要】  本课程设计采用Microsoft Visual Studio 2022 的编辑器,利用socket的最新版本ws2_32.dll,在Win32的编译环境下取消了SDL检查机制,从而完成了基于C/S模式完成了数据包的发送和接收,并且提出了一些可以改进优化的思路。

【环境配置】在工程中点击右键,属性,点进去

 

 然后在C/C++——常规中找到SDL检查,改成否

 

 属性页界面右上角要改成Win32平台

 

 

 然后在这里找到管理器的平台,改成Win32(就是下面这个图的x64点那个下三角会弹出来,点配置)

 

 

以上就完成了环境基本配置,下面进入正题!

 

1 引言

TCP是一种面向连接的、可靠的传输层协议。TCP协议工作在网络层IP的基础上。本课程设计的目的是设计一个发送和接收TCP数据包的程序,其功能是填充一个TCP数据包,发送给目的主机,并在目的主机接收此TCP数据包,将数据字段显示显示在标准输出上。

 

2 基于C/S的数据包发送和接收程序的实现

本设计实现了基于C/S模式的数据包发送和接收,让服务器对客户端的请求作出回应。

 

2.1 C/S模式

  所谓C/S模式指的是Client/Sever模式,即客户端/服务器模式。

 

2.1.1 模式简介

随着计算机网络技术的成熟和应用普及,特别是局域网的发展、PC机的出现,越来越多的用户和企业开始使用计算机管理一些事务。PC机的资源没有大型、中型甚至小型主机丰富,但将多台PC机联成网,必然会增加资源含量,各个用户都在网络上来共享所有资源。根据客户/服务器(Client/Server简记为C/S)体系结构的概念,至少用两台计算机来分别充当客户机和服务器角色。

服务器-客户机,即Client-Server(C/S)结构。C/S结构通常采取两层结构。服务器负责数据的管理,客户机负责完成与用户的交互任务。

客户机通过局域网与服务器相连,接受用户的请求,并通过网络向服务器提出请求,对数据库进行操作。服务器接受客户机的请求,将数据提交给客户机,客户机将数据进行计算并将结果呈现给用户。服务器还要提供完善安全保护及对数据完整性的处理等操作,并允许多个客户机同时访问服务器,这就对服务器的硬件处理数据能力提出了很高的要求。

在C/S结构中,应用程序分为两部分:服务器部分和客户机部分。服务器部分是多个用户共享的信息与功能,执行后台服务,如控制共享数据库的操作等;客户机部分为用户所专有,负责执行前台功能,在出错提示、在线帮助等方面都有强大的功能,并且可以在子程序间自由切换。

C/S结构在技术上已经很成熟,它的主要特点是交互性强、具有安全的存取模式、响应速度快、利于处理大量数据。但是C/S结构缺少通用性,系统维护、升级需要重新设计和开发,增加了维护和管理的难度,进一步的数据拓展困难较多,所以C/S结构只限于小型的局域网。

 

2.1.2 主要功能

服务器提供的服务可以包括:文件服务、打印服务、Web服务、数据库服务、应用服务和代理服务等。通常客户端运行应用程序,服务器端运行服务程序,应用程序向服务程序提出申请,服务程序分析该申请是否合理,来决定返回数据信息还是禁止申请信息。

由于客户端实现与服务器的直接相连,没有中间环节,因此响应速度快。客户操作界面设计个性化,具有直观、简单、方便的特点,可以满足客户个性化的操作要求。同时由于开发是针对性的,因此,操作界面漂亮、形式多样,可以充分满足客户自身的个性化要求。由于是针对性开发,因此缺少通用性的特点,业务变更或改变不够灵活,需要重新设计和开发,增加了维护和管理的难度,进一步的业务拓展困难较多。需要专门的客户端安装程序,分布功能弱,不能够实现快速部署安装和配置。兼容性差,对于不同的开发工具,相互之间很难兼容,具有较大的局限性。若采用不同工具,需要重新改写程序。开发成本较高,需要具有一定专业水准的技术员才能完成。

 

2.1.3 发展经历

C/S模式的发展经历了从两层结构到三层结构。

两层结构由两部分构成:前端是客户机,主要完成用户界面显示,接受数据输入,校验数据有效性,向后台数据库发请求,接受返回结果,处理应用逻辑;后端是服务器,运行DBMS,提供数据库的查询和管理。

两层结构存在一些不足,主要表现在:系统的可伸缩性差;难以和其它系统进行互操作;难以支持多个异构数据库客户端程序和服务器端DBMS交互频繁,网络通讯量大;所有客户机都需要安装、配置数据库客户端软件,是一件十分庞杂的工作。

基于二层结构的以上不足,三层结构伴随着中间件技术的成熟而兴起。其核心概念是利用中间件将应用分为表示层、业务逻辑层和数据存储层三个不同的处理层次。

三层结构较二层结构具有一定的优越性:具有良好的开放性;减少整个系统的成本,维护升级十分方便;系统的可扩充性良好;系统管理简单,可支持异种数据库,有很高的可用性;可以进行严密的安全管理。

 

2.1.4 优缺点

C/S结构的优点是能充分发挥客户端PC的处理能力,很多工作可以在客户端处理后再提交给服务器。对应的优点就是客户端响应速度快。具体表现在以下两点:

(1)应用服务器运行数据负荷较轻。最简单的C/S体系结构的数据库应用由两部分组成,即客户应用程序和数据库服务器程序。二者可分别称为前台程序与后台程序。运行数据库服务器程序的机器,也称为应用服务器。一旦服务器程序被启动,就随时等待响应客户程序发来的请求;客户应用程序运行在用户自己的电脑上,对应于数据库服务器,可称为客户电脑,当需要对数据库中的数据进行任何操作时,客户程序就自动地寻找服务器程序,并向其发出请求,服务器程序根据预定的规则作出应答,送回结果,应用服务器运行数据负荷较轻。

(2)数据的储存管理功能较为透明。在数据库应用中,数据的储存管理功能,是由服务器程序和客户应用程序分别独立进行的,并且通常把那些不同的(不管是已知还是未知的)前台应用所不能违反的规则,在服务器程序中集中实现,例如访问者的权限,编号可以重复、必须有客户才能建立订单这样的规则。所有这些,对于工作在前台程序上的最终用户,是“透明”的,他们无须过问(通常也无法干涉)背后的过程,就可以完成自己的一切工作。在客户服务器架构的应用中,前台程序不是非常“瘦小”,麻烦的事情都交给了服务器和网络。在C/S体系下,数据库不能真正成为公共、专业化的仓库,它受到独立的专门管理。

随着互联网的飞速发展,移动办公和分布式办公越来越普及,这需要我们的系统具有扩展性。这种方式远程访问需要专门的技术,同时要对系统进行专门的设计来处理分布式的数据。

客户端需要安装专用的客户端软件。首先涉及到安装的工作量,其次任何一台电脑出问题,如病毒、硬件损坏,都需要进行安装或维护。特别是有很多分部或专卖店的情况,不是工作量的问题,而是路程的问题。还有,系统软件升级时,每一台客户机需要重新安装,其维护和升级成本非常高。 

对客户端的操作系统一般也会有限制。可能适应于Win98, 但不能用于Windows2000或Windows XP。或者不适用于微软新的操作系统等等,更不用说Linux、Unix等。(目前,大多数客户端都适应win XP系统,但对微软新的操作系统或其他开发系统就兼用不了。)

传统的C/S体系结构虽然采用的是开放模式,但这只是系统开发一级的开放性,在特定的应用中无论是Client端还是Server端都还需要特定的软件支持。由于没能提供用户真正期望的开放环境,C/S结构的软件需要针对不同的操作系统开发不同版本的软件, 加之产品的更新换代十分快,已经很难适应百台电脑以上局域网用户同时使用。而且代价高, 效率低。

C/S架构的劣势还有高昂的维护成本且投资大。首先,采用C/S架构,要选择适当的数据库平台来实现数据库数据的真正“统一”,使分布于两地的数据同步完全交由数据库系统去管理,但逻辑上两地的操作者要直接访问同一个数据库才能有效实现,有这样一些问题,如果需要建立“实时”的数据同步,就必须在两地间建立实时的通讯连接,保持两地的数据库服务器在线运行,网络管理工作人员既要对服务器维护管理,又要对客户端维护和管理,这需要高昂的投资和复杂的技术支持,维护成本很高,维护任务量大。

其次,传统的C/S结构的软件需要针对不同的操作系统系统开发不同版本的软件,由于产品的更新换代十分快,代价高和低效率已经不适应工作需要。在JAVA这样的跨平台语言出现之后,B/S架构更是猛烈冲击C/S,并对其形成威胁和挑战。

 

2.2 TCP的传输原理和过程

 

2.2.1 TCP握手协议 

在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。大体上是:先发报文头——然后接受报文头回复——再发报文(三次握手)开始正式通信。三次握手如下:

第一次握手:客户端发送一个SYN(包含有同步序列号的标志位的数据段和通信请求)给服务器,然后等待服务器的回应发确认信息。

第二次握手:服务器发送一个SYN-ACK给客户端,确认已经收到客户端发来的信息

第三次握手:客户端接收到服务器发来的确认信息后,在回馈一个ACK给服务器,此时就与服务器建立可靠的连接

2.3 Windows Sockets与 套接口

 

2.3.1 Windows Sockets

Windows下网络编程的规范-Windows Sockets是Windows下得到广泛应用的、开放的、支持多种协议的网络编程接口。从1991年的1.0版到1995年的2.0.8版,经过不断完善并在Intel、Microsoft、Sun、SGI、Informix、Novell等公司的全力支持下,已成为Windows网络编程的事实上的标准。

Windows Sockets规范本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。此外,在一个特定版本Windows的基础上,Windows Sockets也定义了一个二进制接口(ABI),以此来保证应用Windows Sockets API的应用程序能够在任何网络软件供应商的符合Windows Sockets协议的实现上工作。

因此这份规范定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。遵守这套Windows Sockets规范的网络软件,我们称之为Windows Sockets兼容的,而Windows Sockets兼容实现的提供者,我们称之为Windows Sockets提供者。一个网络软件供应商必须百分之百地实现Windows Sockets规范才能做到现Windows Sockets兼容。任何能够与Windows Sockets兼容实现协同工作的应用程序就被认为是具有Windows Sockets接口。我们称这种应用程序为Windows Sockets应用程序。

Windows Sockets规范定义并记录了如何使用API与Internet协议族(IPS,通常我们指的是TCP/IP)连接,尤其要指出的是所有的Windows Sockets实现都支持流套接口和数据报套接口.应用程序调用Windows Sockets的API实现相互之间的通讯。Windows Sockets又利用下层的网络通讯协议功能和操作系统调用实现实际的通讯工作。

通信的基础是套接口(Socket),一个套接口是通讯的一端。在这一端上你可以找到与其对应的一个名字。一个正在被使用的套接口都有它的类型和与其相关的进程。套接口存在于通讯域中。通讯域是为了处理一般的线程通过套接口通讯而引进的一种抽象概念。套接口通常和同一个域中的套接口交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。

 

2.3.2 套接口概念

为什么把网络编程接口叫做套接字 (Socket ) 编程接口呢?

Scocket这个词,字面上的意思,是凹槽、插座和插孔的意思。这让我们联想到电插座和电话插座,这些简单的设备,给我们带来了很大的方便。

网络的层次型体系结构是很复杂的,通过在发送端自上而下的层层加码,传输介质的传输,在接受端自下而上的层层处理,才在传输层提供了端到端的进程之闻的通信通路。但是,如果抛掉这些复杂的过程,把这个通路看成一条管道,在它的端点安装一个连接设备,用它来与应用进程相连,应用进程就能方便地通过网络来交换数据了。套接字就是为了这个目的,按照这个思路而引入的socket,人们习惯把它叫做套接字(因为最终用一个整数来代表它),其实把它称为套接口可能更好理解。

套接口是对网络中不同主机_上应用进程之间进行双向通信的端点的抽象,从效果上来说,一个套接口就是网络上迸程通信的一端。套接字提供了应用层进程利用网络协议栈交换数据的机制;两个应用进程只要分别连接到自己的套接字,就能方便地通过计算机网络进行通信了,既不用去管网络的复杂结构,也不用去管数据传输的复杂过程。图1说明了套接字的位置和作用。

 

 

 

1 应用进程、套接口、网络协议栈及操作系统的关系

 

套接字上连应用进程,下连网络协议栈,是应用程序通过网络协议栈进行通信的接口,是应用程序与网络协议栈进行交互的接口。对于套接字的操作形成了一种网络应用程序的编程接口(API ),包括一组操作套接字的系统调用,或者是库函数。应用程序使用这些系统调角,可以构造套接字,安装绑定套接字,连接套接字,通过套接字交换数据,关闭套接字。实现网络中的各种分布式应用。套接字编程接口是一套操作套接字的编程接口函数,套接字是它的操作对象。

 

 

2.3.3 套接口编程原理

网络进程间面向连接的通信方式基于TCP,因而必须借助流式套接字来编程,应用程序分为服务器端和客户机端,双方是不对称的,需要分别编制。如图2所示为服务器端和客户机端操作流式套接字的基本步骤。

双方都首先要创建并安装套接字,做好准备后,才能进行客户端与服务器端的通信。一个完整的通信过程历经建立连接、发送/接收数据和释放连接3个阶段:建立连接的过程按照TCP三次握手的规范进行;发送接收数据阶段称为客户机与服务器的会话期,会话的内容是有一定格式的,一来一往的数据交换还必须遵守一定的顺序,这些都由应用层协议来规定;最后要释放连接。

套接口的工作过程如下:服务器首先启动,通过调用socket()建立一个套接口,然后调用bind()将该套接口和本地网络地址联系在一起,再调用listen()使用套接口做好侦听准备,并规定它的请求队列的长度,之后就调用accept()来接收连接。客户在建立套接口后就可以调用connect()和服务器建立连接。连接一旦建立,客户机和服务器之间就可以调用read()和write()来发送和接受数据。最后,待数据传送结束后,双方调用close()关闭套接口。

 

 

 

2 服务器端和客户机端操作流式套接字的基本步骤

 

而网络进程间无连接的通信方式基于UDP,因而必须借助数据报套接字来编程,应用程序分为服务器端和客户机端,双方是不对称的,需要分别编制。如图3所示为服务器端和客户机端操作数据包套接字的基本步骤。

 

 

 

3 C/S模式的数据报套接字的编程模型

 

应用双方不是对等的:服务器要先启动,被动等待访问,要经过创建套接字、绑定套接字、发送/接收数据、关闭套接字4个阶段,将套接字绑定到众所周知的端口上;客户机套接字使用动态分配的自由端口上,不需要进行绑定;客户机主动请求服务,并在数据报中携带双方的地址;服务器可以接受多个客户端的数据。

 

2.4 服务器

在标准的C/S模式计算机网络中,网络服务器可在两种不同的方式下工作:循环方式(iterative mode)和并发方式(concur-rent mode)。循环方式是在服务器中一次只能运行一个服务器进程,当多个客户请求服务时,服务器进程就按请求的先后次序依次做出响应;并发方式则可在服务器中同时运行多个服务器进程,而每个服务器进程都对某个特定的客户请求做出响应。

这两种服务器模型各有优缺点。当服务耗时较长时,如果采用循环服务器,客户机将得不到快速响应,甚至可能出现客户机请求被拒绝的情况,但是这种服务器消耗的系统资源很少,实现起来比较简单。并发服务器为每一个请求创建一个子进程,可以保证同时处理多个客户机请求,使用这种服务器模型一般不会造成拒绝客户机请求的情况,但是如果频繁创建子进程,将会加重服务器的负担。在实际的网络应用中,服务器要同时处理多个客户的请求,所以通常采用并发方式。

 

 

 

 

2.5 客户端

客户端与服务器类似,只要负责接收指令和执行指令即可。

 

 

 

 

 

 

 

 

2.6实现数据包的发送和接收

 

 

 

3 实现的截图

服务器与客户端连接失败:

 

 

 

 

服务器与客户端成功连接:

 

 

 

 

 

 

就本次课程设计而言,我觉得有以下不足之处可以改进:

1、本程序仅仅简单地利用了Windows系统的控制台,界面过于简陋,可以设计出一个窗体MFC AppWizard (exe) 将这个利用控制台的代码转化为一个真正意义上的简便易行的应用程序。这样的窗口还可以让界面简洁美观,便于使用者的轻松使用。

2、本程序的服务器功能比较弱,我仅仅设计了几个备用的指令,比如说查询程序的开发版本信息,等等,我可以从客户的需求出发,考虑客户端的需求,进一步完善可执行的指令,进而提升客户端的使用体验。

 

好啦,本次课程设计就到这里结束了,感谢各位耐心阅读到这里的伙伴,万水千山总是情,顺便点个赞吧!

posted @ 2021-12-19 21:41  且听山风  阅读(2375)  评论(0)    收藏  举报