实验目的
1. 设计一个简单的聊天室程序采用客户/服务器模式,分为客户端程序和服务器端程序。由于服务器只能支持一个客户,实际上是一个点对点通信的程序。客户端程序和服务器程序通过网络交换聊天字符串内容,并在窗口的列表框中显示。
2. 通过网络编程实践,使自己理解并掌握传输层协议基本工作原理及过程,特别是TCP协议的程序设计流程,重点学习流式套接字对象的使用和处理网络事件的方法,以及协议的封装方法。
3. 掌握Windows系统下的窗口编程方法,按照下面图的窗口设计界面,利用校园网络环境进行测试和验证。
4. 设计扩展,在聊天界面设计基础上,增加文件传输功能,实现双方的的文件发送和接收功能。
实验环境
Windows操作系统、校园网环境
实验内容
聊天室程序的功能需求如下:
(1)由两个程序组成:服务器程序(Server)和客户端程序(Client)。首先需要启动服务器,再启动客户端,并登录服务器(需指定服务器IP地址以便与服务器建立连接)。登录成功后,客户端和服务器之间可以在自己的界面上直接与对方进行聊天。
窗口界面如图所示
(2)在客户端界面增添“本机网络配置信息”按钮,点击显示本机网络配置信息(如同ipconfig命令),通过调用gethostbyname()函数获取信息。
(3)尝试改用UDP协议实现上述设计,设计流程和代码如何修改,分析其特点和应用场景,在报告中给出说明。
实验步骤
1.设置服务端程序
2.设置客户端程序
3.首先运行服务端程序
服务端程序在等待客户端进行连接。
4.运行客户端程序
5.在客户端输入消息发送
6.发现服务端收到消息
7.在服务端输入回复消息
8.发现在客户端收到回复消息
若要将TCP改为UDP实现聊天功能,需要进行以下修改:
1.在服务器端,创建的套接字类型需要改为SOCK_DGRAM,同时监听和接收连接的函数listen()和accept()也需要删除,因为UDP是无连接的,不需要进行监听和接收连接。发送消息使用sendto()函数,接收消息使用recvfrom()函数。
2.在客户端,创建的套接字类型也需要改为SOCK_DGRAM,连接函数connect()需要删除,因为UDP是无连接的,也不需要连接服务端。发送消息使用sendto()函数,接收消息使用recvfrom()函数。
特点和应用场景:
UDP协议的特点是不可靠、无连接和简单。由于UDP不需要建立连接,因此通信的开销较小,且无需等待对方回复确认,因此传输速度较快。但是由于UDP无法保证消息的可靠性,因此在传输过程中可能会丢失部分消息或重复传输部分消息,需要在应用层进行处理。
UDP协议适用于实时传输数据的场景,例如视频、音频等实时应用,因为这些应用对数据的实时性要求高,而对数据的可靠性要求不高。此外,UDP还适用于需要广播或多播的应用场景,例如在线游戏中的广播消息、网络会议中的多人会议等。
但是,由于UDP协议无法保证消息的可靠性,因此在一些对数据可靠性要求较高的场景中,如文件传输、邮件发送等应用中,通常会选择使用TCP协议。
server.cpp
// 引入头文件
#include <iostream>
#include <winsock2.h>
// 链接Winsock库
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main()
{
// 定义变量
WSADATA wsaData;
SOCKET serverSocket, clientSocket;
SOCKADDR_IN serverAddr, clientAddr;
int clientAddrLen = sizeof(clientAddr);
char buffer[1024];
// 初始化Winsock库
WSAStartup(MAKEWORD(2, 2), &wsaData);
// 创建套接字
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 绑定服务器地址和端口
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(12345); // 端口号为12345
serverAddr.sin_addr.s_addr = INADDR_ANY; // IP地址为本机地址
bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr));
// 监听客户端连接
listen(serverSocket, 1);
// 等待客户端连接
cout << "等待客户端连接..." << endl;
clientSocket = accept(serverSocket, (SOCKADDR*)&clientAddr, &clientAddrLen);
// 接收和发送消息
while (true)
{
// 接收消息
memset(buffer, 0, sizeof(buffer));
int len = recv(clientSocket, buffer, sizeof(buffer), 0); // 接收消息
if (len > 0)
{
cout << "客户端: " << buffer << endl; // 输出客户端发送的消息
}
// 发送消息
memset(buffer, 0, sizeof(buffer));
cout << "请输入回复消息:";
cin.getline(buffer, sizeof(buffer)); // 输入回复消息
send(clientSocket, buffer, strlen(buffer), 0); // 发送回复消息
}
// 关闭套接字
closesocket(serverSocket);
closesocket(clientSocket);
// 释放Winsock库
WSACleanup();
return 0;
}
client.cpp
#include <iostream>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main()
{
WSADATA wsaData; // 声明WSADATA结构体,保存Winsock库初始化的信息
SOCKET clientSocket; // 声明客户端套接字
SOCKADDR_IN serverAddr; // 声明服务端地址结构体
char buffer[1024]; // 声明缓冲区,用于发送和接收消息
// Initialize Winsock library
WSAStartup(MAKEWORD(2, 2), &wsaData); // 初始化Winsock库,版本号为2.2,将初始化信息保存在wsaData中
// Create socket
clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 创建TCP套接字
// Connect to server
serverAddr.sin_family = AF_INET; // 地址族为IPv4
serverAddr.sin_port = htons(12345); // 连接的端口号为12345
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 连接的IP地址为本地IP地址
connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)); // 连接服务端
// Send and receive messages
while (true)
{
// Send message
memset(buffer, 0, sizeof(buffer)); // 将缓冲区清零
cout << "Please enter a message: ";
cin.getline(buffer, sizeof(buffer)); // 读入用户输入的消息
send(clientSocket, buffer, strlen(buffer), 0); // 发送消息到服务端
// Receive message
memset(buffer, 0, sizeof(buffer)); // 将缓冲区清零
int len = recv(clientSocket, buffer, sizeof(buffer), 0); // 接收服务端发来的消息
if (len > 0)
{
cout << "Server: " << buffer << endl; // 输出服务端发送的消息
}
}
// Close socket
closesocket(clientSocket); // 关闭客户端套接字
// Clean up Winsock library
WSACleanup(); // 释放Winsock库
return 0;
}
实验结果
运用c++完成了服务端和客户端的设计和编程实现,成功实现了进行二者的聊天发送和接收消息。
实验小结
通过网络编程实践,理解并掌握了传输层协议基本工作原理及过程,以及TCP协议的程序设计流程,流式套接字对象的使用和处理网络事件的方法,以及协议的封装方法。
(ps.由于当时还未学过java以及框架知识,因此未实现图形化界面)
(by 归忆)