基于TCP的C/S通信仿真

一、实验要求

  模拟实现TCP协议通信过程,要求编程实现。客户端向服务器端发送“我是集美大学网络空间安全专业学生”,服务器回应“我也是集美大学网络空间安全专业学生”。

二、  编程环境

  Visual C++ 6.0

  本次实验要创建两个Win32工程,分别为服务器的Server和客户端的Client。

三、流程图(TCP)

  套接字相当于网络接口,将客户端的接口和服务器的接口连上,两者才能通信。

  服务器是一个特殊电脑上提供某种服务的特殊应用。

  每一台电脑都分配一个ip地址,电脑上的每一个应用对应分配一个端口号,即每一个服务器对应一个端口号,套接字和端口号绑定,从而抽象为这台电脑上的这个服务器。

  当客户端向服务器发起通信时,通过ip地址指定连接的电脑,通过端口号指定该电脑上的哪个服务器。

网络通信模型如下图:127.0.0.1(本地回环)意味着和本机上的服务器通信。

由此得出通信流程图为:

 

四、编程准备工作

在server和client两个网络工程中都应做到:

  1. 正确调用头文件和库
    #include<stdio.h>
    #include<string.h>
    #include<WinSock2.h>
    #pragma comment(lib,"ws2_32.lib")
  2. 加载和释放控制台,并重定向标准输入输出
    AllocConsole();
    freopen("CONOUT$","w",stdout);
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stderr);
    //程序
    
    FreeConsole();
  3. 初始化和清理winsock
    复制代码
    // 初始化 Winsock
    WSADATA wsaData;
    int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
    
    if (result != 0)
    {
        printf("WSAStartup failed with error: %d\n", result);
        return 1;
    }
    //网络编程相关的操作,如创建套接字等
    
    // 清理 Winsock
     WSACleanup();
    复制代码

五、Server端详解

1、在Server中新建server.cpp文件:

2、编程步骤

  1. 创建监听套接字
    复制代码
    //创建监听套接字并判断是否创建成功
        /*
        SOCKET socket(
        _In_ int af,        //协议地址簇    IPv4:AF_INET/IPv6:AF_INET6
        _In_ int type,        //类型            流式套接字:SOCK_STREAM/数据报套接字:SOCK_DGRAM
        _In_ int protocol    //保护协议        一般设置为 0,让系统自动选择合适的协议。
        );
        */
        SOCKET listen_socket=socket(AF_INET, SOCK_STREAM, 0);
        if (INVALID_SOCKET==listen_socket)
        {
            printf("create listen socket failed!!! errcode: %d\n", GetLastError());
            WSACleanup();
            return -1;
        }
    复制代码
  2. 为监听套接字绑定端口号
    复制代码
    //创建ip地址结构体并将监听套接字和ip地址结构体绑定
    /*
    * ip地址结构体
    struct sockaddr_in {
    ADDRESS_FAMILY sin_family;    //协议地址簇  2字节
    USHORT sin_port;            //端口号    2字节
    IN_ADDR sin_addr;            //ip地址    4字节
    CHAR sin_zero[8];            //保留字节    8字节
    };
    struct sockaddr {
    u_short sa_family;                    //协议地址簇  2字节
    CHAR sa_data[14];                   //14字节
    }
    */
    struct sockaddr_in local = {0};
    local.sin_family = AF_INET;
    local.sin_port = htons(8080);//绑定端口 大小端转化:中间设备使用大端序(路由器),但是电脑使用的是小端序,所以要转化

    /* 电脑上有多个网卡,服务器可以选择接收哪个或哪些网卡传过来的数据。 如:127.0.0.1(本地环回),则服务器只能和自己通信 INADDR_LOOPBACK 一般写全0地址(0.0.0.0),表示接收全部网卡的数据 INADDR_ANY */ //local.sin_addr.s_addr = htonl(INADDR_ANY);//接收全部网卡的数据 大小端转化 local.sin_addr.s_addr = inet_addr("0.0.0.0");//接收全部网卡的数据 字符串ip转成整数ip /* int bind( SOCKET s, //绑定的套接字 const struct sockaddr FAR * name, //ip地址结构体 int namelen //ip地址结构体的长度 ); */ bind(listen_socket,(struct sockaddr*)&local,sizeof(local));//sockaddr_in要转化为sockaddr
    复制代码
    创建ip地址结构体sockaddr_in,指定协议簇为ipv4(AF_INET),指定端口号为8080,指定服务器接收哪个网卡传来的数据;将监听套接字与转化后的ip地址结构sockaddr绑定
  3. 给监听套接字开启监听属性
    复制代码
    //给监听套接字开启监听属性,只用来接收连接
    /*
    int listen(
    _In_ SOCKET s,        //监听套接字
    _In_ int backlog    //指定等待连接队列的最大长度
    );
    */
        if (-1 == listen(listen_socket, 10))
        {
            printf("start listen failed!!! errcode: %d\n", GetLastError());
            WSACleanup();
            return -1;
        }
    复制代码
  4. 等待客户端连接
    复制代码
    //等待客户端连接
    /*
    SOCKET accept(
    SOCKET s,                    //监听套接字
    struct sockaddr * addr,        //客户端的ip地址和端口号
    int * addrlen                //结构的大小
    );
    成功时,accept 函数返回一个新的套接字用于与客户端进行通信。
    失败时,返回 INVALID_SOCKET。
    
    阻塞函数,等到有客户端连接进来就接受连接,然后返回,否则就阻塞
    */
    printf("This is a Server.\n");
    while (1)
    {
        printf("\nWaiting for connect...\n");
        SOCKET client_socket = accept(listen_socket, NULL, NULL);
    
        if (INVALID_SOCKET == client_socket)
        {
            printf("Connect invalid!!!\n");
            continue;
        }
    
        printf("Connected successfully!\n\n");
           //开始通信 
    }   
    复制代码
  5. 开始通信
    复制代码
    //5.开始通信(B/S)
    //浏览器向服务器发送http请求,http是tcp的上层协议,可以接受http的报文
    /*
    int recv(
    SOCKET s,        //与客户端进行通信的socket
    char * buf,        //接收的数据的存储地址
    int len,        //接收的长度
    int flags        //标志位,填0
    );
    */
    //接收信息
    char rbuffer[1024] = { 0 };
    int ret = recv(client_socket, rbuffer, 1024, 0);
    if (ret <= 0)
    {
        printf("The client has disconnected.\n");
        break;
    }
    
    printf("recive:\t%s\n", rbuffer);
    
    
    //发送信息
    char sbuffer[1024] = { 0 };
    // 检查接收到的消息
    if (strcmp(rbuffer, "我是集美大学网络空间安全专业学生") == 0)
    {
        sprintf(sbuffer, "我也是集美大学网络空间安全专业学生");
        printf("send:\t我也是集美大学网络空间安全专业学生\n");
    }
    else
    {
        printf("send:\t");
        scanf("%s", sbuffer);
    }
    
    send(client_socket, sbuffer, strlen(sbuffer), 0);
    复制代码
  6. 关闭连接
    //6.关闭读写通道和连接
    shutdown(client_socket, SD_BOTH);
    closesocket(client_socket);

 六、Client端详解

1、在Client中新建client.cpp文件:

2、编程步骤

  1. 创建socket套接字
    //创建socket套接字并判断是否创建成功
    SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET == client_socket)
    {
        printf("create socket failed!!! errcode: %d\n", GetLastError());
        WSACleanup();
        return -1;
    }
  2. 连接服务器
    复制代码
    //创建目标服务器的ip结构体,确定目标服务器的ip地址和端口号,发起连接
    struct sockaddr_in target;//目标服务器的ip结构体
    target.sin_family = AF_INET;
    target.sin_port = htons(8080);
    target.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    if (-1 == connect(client_socket, (struct sockaddr*)&target, sizeof(target)))
    {
        printf("connect server failed!!!\n");
        shutdown(client_socket, SD_BOTH);
        closesocket(client_socket);
        WSACleanup();
        return -1;
    }
    复制代码
  3. 开始通信
    复制代码
    //开始通讯
    printf("This is a Cilent.\n\n");
    while (1)
    {
        //发送信息
        printf("send:\t");
        char sbuffer[1024] = { 0 };
        scanf("%s", sbuffer);
        send(client_socket, sbuffer, strlen(sbuffer), 0);
    
        //接收消息
        char rbuffer[1024] = { 0 };
        int ret = recv(client_socket, rbuffer, 1024, 0);
        if (ret <= 0)
        {
            break;//断开连接
        }
        printf("recive:\t%s\n", rbuffer);
    }
    复制代码
  4. 关闭连接
    //4.关闭连接
    shutdown(client_socket, SD_BOTH); shutdown(client_socket, SD_BOTH);
    closesocket(client_socket);

七、完整代码

server.cpp

复制代码
#include<stdio.h>
#include<string.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    AllocConsole();
    freopen("CONOUT$","w",stdout);
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stderr);
    // 初始化 Winsock
    WSADATA wsaData;
    int result=WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (result != 0) 
    {
         printf("WSAStartup failed with error: %d\n", result);
         return 1;
    }

    //1.创建socket套接字
    SOCKET listen_socket=socket(AF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET==listen_socket)
    {
        printf("create listen socket failed!!! errcode: %d\n", GetLastError());
        WSACleanup();
        return -1;
    }

    //2.给socket绑定端口号
    struct sockaddr_in local = {0};

    local.sin_family = AF_INET;
    local.sin_port = htons(8080);//绑定端口
    //local.sin_addr.s_addr = htonl(INADDR_ANY);//接收全部网卡的数据 大小端转化
    local.sin_addr.s_addr = inet_addr("0.0.0.0");//接收全部网卡的数据 字符串ip转成整数ip
    bind(listen_socket,(struct sockaddr*)&local,sizeof(local));

    //3.给socke开启监听属性,只用来接收连接
    if (-1 == listen(listen_socket, 10))
    {
        printf("start listen failed!!! errcode: %d\n", GetLastError());
        WSACleanup();
        return -1;
    }

    //4.等待客户端连接
    printf("This is a Server.\n");
    while (1)
    {
        printf("\nWaiting for connect...\n");
        SOCKET client_socket = accept(listen_socket, NULL, NULL);
        
        if (INVALID_SOCKET == client_socket)
        {
            printf("Connect invalid!!!\n");
            continue;
        }

        printf("Connected successfully!\n\n");
        while (1)
        {
            //5.开始通信
            //接收信息
            char rbuffer[1024] = { 0 };
            int ret=recv(client_socket, rbuffer, 1024, 0);
            if (ret <= 0)
            {
                printf("The client has disconnected.\n");
                break;
            }
            printf("recive:\t%s\n", rbuffer);
            //发送信息
            char sbuffer[1024] = { 0 };
            // 检查接收到的消息
            if(strcmp(rbuffer, "我是集美大学网络空间安全专业学生") == 0) 
            {
                 sprintf(sbuffer, "我也是集美大学网络空间安全专业学生");
                 printf("send:\t我也是集美大学网络空间安全专业学生\n");
            }
            else
            {
                printf("send:\t");
                scanf("%s", sbuffer);
            }
            
            send(client_socket, sbuffer, strlen(sbuffer), 0);
        }
        
        //6.关闭连接
        shutdown(client_socket, SD_BOTH);
        closesocket(client_socket);
    }
    
    FreeConsole();
    // 清理 Winsock
    WSACleanup();


    return 0;
}
复制代码

 

client.cpp

复制代码
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    AllocConsole();
    freopen("CONOUT$","w",stdout);
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stderr);
    // 初始化 Winsock
    WSADATA wsaData;
    int result=WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (result != 0) 
    {
         printf("WSAStartup failed with error: %d\n", result);
         return 1;
    }

    //1.创建socket套接字
    SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET == client_socket)
    {
        printf("create socket failed!!! errcode: %d\n", GetLastError());
        WSACleanup();
        return -1;
    }
    //2.连接服务器
    struct sockaddr_in target;//目标服务器的ip结构体
    target.sin_family = AF_INET;
    target.sin_port = htons(8080);
    target.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (-1 == connect(client_socket, (struct sockaddr*)&target, sizeof(target)))
    {
        printf("connect server failed!!!\n");
        shutdown(client_socket, SD_BOTH);
        closesocket(client_socket);
        WSACleanup();
        return -1;
    }
    //3.开始通讯

    printf("This is a Cilent.\n\n");
    while (1)
    {
        //发送信息
        printf("send:\t");
        char sbuffer[1024] = { 0 };
        scanf("%s", sbuffer);
        send(client_socket, sbuffer, strlen(sbuffer),0);

        //接收消息
        char rbuffer[1024] = { 0 };
        int ret=recv(client_socket, rbuffer, 1024, 0);
        if (ret <= 0)
        {
            break;//断开连接
        }
        printf("recive:\t%s\n", rbuffer);
    }
    
    //4.关闭连接
    shutdown(client_socket, SD_BOTH);shutdown(client_socket, SD_BOTH);
    closesocket(client_socket);
    
    FreeConsole();
    // 清理 Winsock
    WSACleanup();


    return 0;
}
复制代码

 八、调试结果

先后执行Server工程和Client工程,如果询问是否建立文件选择是即可。

客户端输入:我是集美大学网络空间安全专业学生,并发送给服务器,服务器自动回复:我也是集美大学网络空间安全专业学生。

客户端和服务器可以随意交流数据

关闭客户端,则服务器失去连接,等待下一个客户端的连接

 九、总结

 

posted @   刘倩_网安2211  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示