【深圳大学计算机网络】实验四 MFC+Winsock网络编程设计报告

实验目的

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 归忆)

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
本文将介绍如何使用MFC Winsock实现基于TCP协议的C/S聊天程序。 1. 创建MFC应用程序 首先,我们需要创建一个MFC应用程序。在创建向导中选择“单文档应用程序”类型,勾选“包含MFC的ActiveX控件”和“支持ActiveX控件”,其他选项默认即可。 2. 添加界面元素 在资源视图中添加两个编辑框和一个按钮,用于输入和显示聊天内容,以及发送消息。 3. 编写代码 在Dlg.cpp文件中添加以下代码: 在头文件中添加以下头文件: #include "stdafx.h" #include "Dlg.h" #include "afxdialogex.h" #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") 在OnInitDialog()函数中添加以下代码: WSADATA wsaData; SOCKET ConnectSocket = INVALID_SOCKET; struct addrinfo *result = NULL, *ptr = NULL, hints; int iResult; // Initialize Winsock iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { MessageBox(_T("WSAStartup failed with error: %d"), iResult); return FALSE; } ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; // Resolve the server address and port iResult = getaddrinfo(_T("localhost"), _T("27015"), &hints, &result); if ( iResult != 0 ) { MessageBox(_T("getaddrinfo failed with error: %d"), iResult); WSACleanup(); return FALSE; } // Attempt to connect to an address until one succeeds for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) { // Create a SOCKET for connecting to server ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (ConnectSocket == INVALID_SOCKET) { MessageBox(_T("socket failed with error: %ld\n"), WSAGetLastError()); WSACleanup(); return FALSE; } // Connect to server. iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); if (iResult == SOCKET_ERROR) { closesocket(ConnectSocket); ConnectSocket = INVALID_SOCKET; continue; } break; } freeaddrinfo(result); if (ConnectSocket == INVALID_SOCKET) { MessageBox(_T("Unable to connect to server!")); WSACleanup(); return FALSE; } // Set the mode of the socket to be nonblocking u_long iMode = 1; iResult = ioctlsocket(ConnectSocket, FIONBIO, &iMode); if (iResult == SOCKET_ERROR) { MessageBox(_T("ioctlsocket failed with error: %d"), WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return FALSE; } // Disable Nagle's algorithm char value = 1; setsockopt(ConnectSocket, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); 在OnDestroy()函数中添加以下代码: // shutdown the connection since no more data will be sent iResult = shutdown(ConnectSocket, SD_SEND); if (iResult == SOCKET_ERROR) { MessageBox(_T("shutdown failed with error: %d"), WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return FALSE; } // cleanup closesocket(ConnectSocket); WSACleanup(); 在OnBnClickedButtonSend()函数中添加以下代码: // Send a message CString strMsg; m_edtMsg.GetWindowText(strMsg); char buf[1024]; strcpy_s(buf, CT2A(strMsg.GetBuffer())); iResult = send(ConnectSocket, buf, strlen(buf), 0); if (iResult == SOCKET_ERROR) { MessageBox(_T("send failed with error: %d"), WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return FALSE; } m_lstMsg.AddString(strMsg); m_edtMsg.SetWindowText(_T("")); 在OnReceive()函数中添加以下代码: char buf[1024]; int iResult = recv(ConnectSocket, buf, sizeof(buf), 0); if (iResult > 0) { CString strMsg(buf); m_lstMsg.AddString(strMsg); } else if (iResult == 0) { MessageBox(_T("Connection closed")); closesocket(ConnectSocket); WSACleanup(); return FALSE; } else { int err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { MessageBox(_T("recv failed with error: %d"), err); closesocket(ConnectSocket); WSACleanup(); return FALSE; } } 4. 编译运行 现在我们可以编译运行程序,在输入框中输入消息并点击发送按钮,就可以发送消息到服务器并在列表框中显示。当接收到服务器返回的消息时,也会在列表框中显示。 注意:本程序仅实现了客户端的代码,需要和服务器端代码配合使用才能实现完整的C/S聊天程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

归忆_AC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值