C++服务器之文件传递(半关闭)

C++服务器之文件传递

为何使用半关闭:当主机A 向 主机B 传递一份数据时,主机A 调用 closesocket() 断开连接,之后主机A 就无法再接收主机B传输的数据。最终,由主机B传输主机A的必须接收的数据也就销毁了。

优雅的断开Socket

     相关函数:

int shutdown(int sock,int howto);

成功返回0,失败返回-1。
sock:套接字描述文件。
howto:传递断开方式信息。

第二个参数断开方式:

Linux/Unix

SHUT_RD:断开输入流。
SHUT_WR:断开输出流。
SHUT_RDWR:同时断开I/O流。

Windows

0:断开输入流。
1:断开输出流。
2:同时断开I/O流。

Windows 代码演示:

//Servers.h 头文件
#pragma once
#include <WinSock2.h>
#include <cstdio>

class ServerClass
{
public:

    ServerClass();
    ~ServerClass();

    void OutMessage(char* str);
    BOOLEAN CreateSocket();
    BOOLEAN BindSocket();
    BOOLEAN ListenSocket();
    SOCKET AcceptSocket();

    void FileToClnet(SOCKET clnt, FILE* fp);

    void CloseSocket(SOCKET pSockets);

private:
    SOCKET m_pServerSocket;

};
//  Servers.cpp 源文件
#include "Servers.h"
#include <iostream>
#define BUF_SIZE 30
using namespace std;

char buf[BUF_SIZE];

ServerClass::ServerClass()
{
    m_pServerSocket = -1;

    WSADATA wsaDATA;
    if (WSAStartup(MAKEWORD(2, 2), &wsaDATA) == -1)
    {
        OutMessage("Version checking fail...");
    }

}
ServerClass::~ServerClass()
{
    CloseSocket(m_pServerSocket);
}

void ServerClass::OutMessage(char* str)
{
    cout << "服务器消息:" << str << endl;
}


void ServerClass::CloseSocket(SOCKET pSockets)
{
    if (pSockets == m_pServerSocket)
    {
        closesocket(m_pServerSocket);
        WSACleanup();
    }
    else
    {
        closesocket(pSockets);
    }
}

BOOLEAN ServerClass::CreateSocket()
{
    if (m_pServerSocket == -1)
    {
        m_pServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (m_pServerSocket == -1)
        {
            OutMessage("Create socket fail...");
            return false;
        }
    }
    OutMessage("Create socket succeed...");
    return true;
}

BOOLEAN ServerClass::BindSocket()
{
    if (m_pServerSocket != -1)
    {

        sockaddr_in serv_addr;
        memset(&serv_addr, 0, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(9130);
        serv_addr.sin_addr.s_addr = inet_addr("192.168.1.101");

        if (bind(m_pServerSocket, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
        {
            OutMessage("Bind socket fail...");
            return false;
        }
    }
    OutMessage("Bind socket succeed...");
    return true;
}

BOOLEAN ServerClass::ListenSocket()
{
    if (m_pServerSocket != -1)
    {
        if (listen(m_pServerSocket, 5) == -1)
        {
            OutMessage("Listen socket fail..");
            return false;
        }
    }
    return true;
}

SOCKET ServerClass::AcceptSocket()
{
    SOCKET cln_sock;
    sockaddr_in cln_addr;
    int cln_len = sizeof(cln_addr);
    if ((cln_sock = accept(m_pServerSocket, (sockaddr*)&cln_addr, &cln_len)) != -1)
    {
        OutMessage("One accept service...");
        return cln_sock;
    }
    return -1;
}

void ServerClass::FileToClnet(SOCKET clnt, FILE* fp)
{
    int read_clnt;

    while (m_pServerSocket != -1)
    {
        // 其中的while循环是传输文件数据
        read_clnt = fread((void*)buf, 1, BUF_SIZE, fp);
        if (read_clnt < BUF_SIZE)
        {
            send(clnt, buf, read_clnt, 0);
            break;
        }
        send(clnt, buf, BUF_SIZE, 0);
    }
    // 对输出流进行半关闭。
    shutdown(clnt, 1);
    // 只关闭了输出了,输入流未关闭,所以输入流接收数据正常。
    recv(clnt, buf, BUF_SIZE, 0);
    OutMessage(buf);
    fclose(fp);
    CloseSocket(clnt);
    CloseSocket(m_pServerSocket);
}

Main.cpp调用

#include "Servers.h"
int main()
{
    ServerClass myServer;
    FILE * fp;
    if (myServer.CreateSocket())
    {
        // 打开文件以向客户端传输服务器源文件。
        fp = fopen("Servers.h", "rb");
        if (myServer.BindSocket())
        {
            if (myServer.ListenSocket())
            {
                SOCKET clnet = myServer.AcceptSocket();
                if (clnet != -1)
                {
                    myServer.FileToClnet(clnet, fp);
                }
            }
        }
    }
}

客户端代码

#include <WinSock2.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 1024

void ClentOUTMessage(char* str);

int main()
{
    WSADATA wsaDATA;
    char message[BUF_SIZE];
    SOCKET cln_sock;
    FILE * fp;
    sockaddr_in serv_addr, from_addr;

    int adr_sz;
    int read_len;

    if (WSAStartup(MAKEWORD(2, 2), &wsaDATA) == -1)
    {
        ClentOUTMessage("Version checking fail..");
        return 0;
    }
    // 创建新的文件来保存服务器传输文件的数据
    fp = fopen("s.h", "wb");
    cln_sock = socket(AF_INET, SOCK_STREAM, 0);

    memset(&serv_addr, 0, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("192.168.1.101");
    serv_addr.sin_port = htons(9130);

    connect(cln_sock, (sockaddr*)&serv_addr, sizeof(serv_addr));
    // 接收数据并且保存到 fp 创建的文件中,知道服务器发送EOF为止。
    while ((read_len = recv(cln_sock, message, BUF_SIZE, 0)) != 0)
    {
        fwrite((void*)message, 1, read_len, fp);
    }
    puts("Recevied file data!");
    // 若服务端没有关闭输入流,则可以接到此条信息。
    send(cln_sock,"Thank you",10,0);
    fclose(fp);
    closesocket(cln_sock);
    return 0;
}

void ClentOUTMessage(char* str)
{
    cout << "服务器信息:" << str << endl;
}

运行结果:

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值