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;
}
运行结果: