C++ Socket发送文件服务端代码

这是一个C++实现的Socket文件发送服务端代码,包括初始化Socket、监听客户端连接、处理消息、打开文件、发送文件等功能。代码中定义了各种消息类型常量,并通过结构体进行数据交互。
摘要由CSDN通过智能技术生成

/*
**服务端代码头文件
*/

pragma comment(lib, “WS2_32”)

include

include

//#include

include

ifndef COMMONDEF_H

define COMMONDEF_H

define MAX_PACKET_SIZE 10240 // 数据包的最大长度,单位是sizeof(char)

define MAXFILEDIRLENGTH 256 // 存放文件路径的最大长度

define PORT 4096 // 端口号

define SERVER_IP “127.0.0.1” // server端的IP地址

// 各种消息的宏定义

define INVALID_MSG -1 // 无效的消息标识

define MSG_FILENAME 1 // 文件的名称

define MSG_FILELENGTH 2 // 传送文件的长度

define MSG_CLIENT_READY 3 // 客户端准备接收文件

define MSG_FILE 4 // 传送文件

define MSG_SENDFILESUCCESS 5 // 传送文件成功

define MSG_OPENFILE_ERROR 10 // 打开文件失败,可能是文件路径错误找不到文件等原因

define MSG_FILEALREADYEXIT_ERROR 11 // 要保存的文件已经存在了

class CCSDef
{
public:

pragma pack(1) // 使结构体的数据按照1字节来对齐,省空间

// 消息头
struct TMSG_HEADER
{
    char    cMsgID;    // 消息标识

    TMSG_HEADER(char MsgID = INVALID_MSG)
        : cMsgID(MsgID)
    {
    }
};

// 请求传送的文件名
// 客户端传给服务器端的是全路径名称
// 服务器传回给客户端的是文件名
struct TMSG_FILENAME : public TMSG_HEADER
{
    char szFileName[256];   // 保存文件名的字符数组

    TMSG_FILENAME()
        : TMSG_HEADER(MSG_FILENAME)
    {
    }
};

// 传送文件长度
struct TMSG_FILELENGTH : public TMSG_HEADER
{
    long lLength;

    TMSG_FILELENGTH(long length)
        : TMSG_HEADER(MSG_FILELENGTH), lLength(length) 
    {

    }
};

// Client端已经准备好了,要求Server端开始传送文件
struct TMSG_CLIENT_READY : public TMSG_HEADER
{
    TMSG_CLIENT_READY()
        : TMSG_HEADER(MSG_CLIENT_READY)
    {
    }
};

// 传送文件
struct TMSG_FILE : public TMSG_HEADER
{
    union     // 采用union保证了数据包的大小不大于MAX_PACKET_SIZE * sizeof(char)
    {
        char szBuff[MAX_PACKET_SIZE];
        struct
        {
            int nStart;
            int nSize;
            char szBuff[MAX_PACKET_SIZE - 2 * sizeof(int)];
        }tFile;
    };

    TMSG_FILE()
        : TMSG_HEADER(MSG_FILE)
    {

    }
};

// 传送文件成功
struct TMSG_SENDFILESUCCESS : public TMSG_HEADER
{
    TMSG_SENDFILESUCCESS()
        : TMSG_HEADER(MSG_SENDFILESUCCESS)
    {
    }
};

// 传送出错信息,包括:
// MSG_OPENFILE_ERROR:打开文件失败
// MSG_FILEALREADYEXIT_ERROR:要保存的文件已经存在了
struct TMSG_ERROR_MSG : public TMSG_HEADER
{
    TMSG_ERROR_MSG(char cErrorMsg)
        : TMSG_HEADER(cErrorMsg)
    {
    }
};

pragma pack()

};

endif

/*
**服务端代码源文件
*/

include “SocketTransfer.h”

char g_szNewFileName[MAXFILEDIRLENGTH];
char g_szBuff[MAX_PACKET_SIZE + 1];
long g_lLength;
char* g_pBuff = NULL;

// 初始化socket库
bool InitSocket();
// 关闭socket库
bool CloseSocket();
// 解析消息进行相应的处理
bool ProcessMsg(SOCKET sClient);
// 监听Client的消息
void ListenToClient();
// 打开文件
bool OpenFile(CCSDef::TMSG_HEADER* pMsgHeader, SOCKET sClient);
// 传送文件
bool SendFile(SOCKET sClient);
// 读取文件进入缓冲区
bool ReadFile(SOCKET sClient);

int main()
{
InitSocket();
ListenToClient();
CloseSocket();

return 0;

}

void ListenToClient()
{
// 创建socket套接字
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (SOCKET_ERROR == sListen)
{
printf(“Init Socket Error!\n”);
return;
}

// 绑定socket到一个本地地址
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (::bind(sListen, (LPSOCKADDR)&sin, sizeof(sockaddr_in)) == SOCKET_ERROR)
{
    printf("Bind Error!\n");
    return;
}

// 设置socket进入监听状态
if (::listen(sListen, 10) == SOCKET_ERROR)
{
    printf("Listen Error!\n");
    return;
}

printf("Listening To Client...\n");

// 循环接收client端的连接请求
sockaddr_in ClientAddr;
int nAddrLen = sizeof(sockaddr_in);
SOCKET sClient;

while (INVALID_SOCKET == (sClient = ::accept(sListen, (sockaddr*)&ClientAddr, &nAddrLen)))
{
}

while (true == ProcessMsg(sClient))
{
}

// 关闭同客户端的连接
::closesocket(sClient);

::closesocket(sListen);

}

bool InitSocket()
{
// 初始化socket dll
WSADATA wsaData;
WORD socketVersion = MAKEWORD(2, 2);
if (::WSAStartup(socketVersion, &wsaData) != 0)
{
printf(“Init socket dll error\n”);
return false;
}

return true;

}

bool CloseSocket()
{
// 释放winsock库
::WSACleanup();

if (NULL != g_pBuff)
{
    delete [] g_pBuff;
    g_pBuff = NULL;
}

return true;

}

bool ProcessMsg(SOCKET sClient)
{
int nRecv = ::recv(sClient, g_szBuff, MAX_PACKET_SIZE + 1, 0);
if (nRecv > 0)
{
g_szBuff[nRecv] = ‘\0’;
}

// 解析命令
CCSDef::TMSG_HEADER* pMsgHeader = (CCSDef::TMSG_HEADER*)g_szBuff;
switch (pMsgHeader->cMsgID)
{
case MSG_FILENAME:    // 文件名
    {
        OpenFile(pMsgHeader, sClient);
    }
    break;
case MSG_CLIENT_READY:   // 客户端准备好了,开始传送文件
    {
        SendFile(sClient);
    }
    break;
case MSG_SENDFILESUCCESS: // 传送文件成功
    {
        printf("Send File Success!\n");
        return false;
    }
    break;
case MSG_FILEALREADYEXIT_ERROR: // 要保存的文件已经存在了
    {
        printf("The file reay to send already exit!\n");
        return false;
    }
    break;
}

return true;

}

bool ReadFile(SOCKET sClient)
{
if (NULL != g_pBuff)
{
return true;
}

// 打开文件
FILE *pFile;
if (NULL == (pFile = fopen(g_szNewFileName, "rb")))   // 打开文件失败
{
    printf("Cannot find the file, request the client input file name again\n");
    CCSDef::TMSG_ERROR_MSG tMsgErrorMsg(MSG_OPENFILE_ERROR);
    ::send(sClient, (char*)(&tMsgErrorMsg), sizeof(CCSDef::TMSG_ERROR_MSG), 0);
    return false;
}

// 把文件的长度传回到client去
fseek(pFile, 0, SEEK_END);
g_lLength = ftell(pFile);
printf("File Length = %d\n", g_lLength);
CCSDef::TMSG_FILELENGTH tMsgFileLength(g_lLength);
::send(sClient, (char*)(&tMsgFileLength), sizeof(CCSDef::TMSG_FILELENGTH), 0);

// 处理文件全路径名,把文件名分解出来
char szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFname[_MAX_FNAME], szExt[_MAX_EXT];
_splitpath(g_szNewFileName, szDrive, szDir, szFname, szExt);
strcat(szFname,szExt);
CCSDef::TMSG_FILENAME tMsgFileName;
strcpy(tMsgFileName.szFileName, szFname);
printf("Send File Name: %s\n", tMsgFileName.szFileName);
::send(sClient, (char*)(&tMsgFileName), sizeof(CCSDef::TMSG_FILENAME), 0);

// 分配缓冲区读取文件内容
g_pBuff = new char[g_lLength + 1];
if (NULL == g_pBuff)
{
    return false;
}

fseek(pFile, 0, SEEK_SET);
fread(g_pBuff, sizeof(char), g_lLength, pFile);
g_pBuff[g_lLength] = '\0';
fclose(pFile);

return true;

}

// 打开文件
bool OpenFile(CCSDef::TMSG_HEADER* pMsgHeader, SOCKET sClient)
{
CCSDef::TMSG_FILENAME* pRequestFilenameMsg = (CCSDef::TMSG_FILENAME*)pMsgHeader;

// 对文件路径名进行一些处理
char *p1, *p2;
for (p1 = pRequestFilenameMsg->szFileName, p2 = g_szNewFileName;
    '\0' != *p1;
    ++p1, ++p2)
{
    if ('\n' != *p1)
    {
        *p2 = *p1;
    }
    if ('\\' == *p2)
    {
        *(++p2) = '\\';
    }
}
*p2 = '\0';

ReadFile(sClient);

return true;

}

// 传送文件
bool SendFile(SOCKET sClient)
{
if (NULL == g_pBuff)
{
ReadFile(sClient);
}

int nPacketBufferSize = MAX_PACKET_SIZE - 2 * sizeof(int); // 每个数据包存放文件的buffer大小
// 如果文件的长度大于每个数据包所能传送的buffer长度那么就分块传送
for (int i = 0; i < g_lLength; i += nPacketBufferSize)
{  
    CCSDef::TMSG_FILE tMsgFile;
    tMsgFile.tFile.nStart = i;
    if (i + nPacketBufferSize + 1> g_lLength)
    {
        tMsgFile.tFile.nSize = g_lLength - i;
    }
    else
    {
        tMsgFile.tFile.nSize = nPacketBufferSize;
    }
    //printf("start = %d, size = %d\n", tMsgFile.tFile.nStart, tMsgFile.tFile.nSize);
    memcpy(tMsgFile.tFile.szBuff, g_pBuff + tMsgFile.tFile.nStart, tMsgFile.tFile.nSize);
    ::send(sClient, (char*)(&tMsgFile), sizeof(CCSDef::TMSG_FILE), 0);
    Sleep(0.5);
}

delete [] g_pBuff;
g_pBuff = NULL;

return true;

}

### 回答1: 好的,下面是关于 C 语言下 Socket 组播服务端与客户端代 码的示例: 服务端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #define PORT 8080 #define MAXLINE 1024 int main() { int sockfd; char buffer[MAXLINE]; char* message = "Hello, Multicast!"; struct sockaddr_in servaddr; // 创建套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(PORT); // 绑定套接字 if(bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 循环发送数据 while(1) { if(sendto(sockfd, (const char*)message, strlen(message), MSG_CONFIRM, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("sendto failed"); exit(EXIT_FAILURE); } sleep(1); } close(sockfd); return 0; } ``` 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #define PORT 8080 #define MAXLINE 1024 int main() { int sockfd; char buffer[MAXLINE]; struct sockaddr_in servaddr; // 创建套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr("239.0.0.1"); servaddr.sin_port = htons(PORT); // 加入组播组 struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr("239.0.0.1"); mreq.imr_interface.s_addr = htonl(INADDR_ANY); if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { perror("setsockopt failed"); exit(EXIT_FAILURE); } // 循环接收数据 while(1) { memset(buffer, 0, MAXLINE); if(recvfrom(sockfd, (char*)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("recvfrom failed"); exit(EXIT_FAILURE); } printf("Message received: %s\n", buffer); } close(sockfd); return 0; } ``` 这段示例代码展示了如何创建基于 UDP 协议的 Socket 组播服务端和客户端,其中服务端会不断向组播地址发送消息,而客户端会监听这个组播地址并接收到服务端发来的消息。 ### 回答2: 下面是一个使用Socket进行组播的服务端和客户端的代码示例: 服务端代码: ```python import socket # 创建UDP socket,并绑定端口 server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_socket.bind(('', 5000)) # 加入组播组 group_ip = '239.0.0.1' group_port = 5555 group = socket.inet_aton(group_ip) + socket.inet_aton('0.0.0.0') server_socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, group) while True: # 接收客户端发送的数据 data, address = server_socket.recvfrom(1024) # 打印接收到的数据 print(f"接收到来自 {address} 的消息: {data.decode()}") # 将接收到的数据发送给组播组 server_socket.sendto(data, (group_ip, group_port)) # 关闭socket连接 server_socket.close() ``` 客户端代码: ```python import socket # 创建UDP socket,并设置套接字为广播类型 client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) while True: # 获取用户输入消息 message = input("请输入要发送的消息(退出请输入q): ") if message == 'q': break # 将消息发送给组播组 client_socket.sendto(message.encode(), ('<broadcast>', 5000)) # 接收服务端发送的数据 data, address = client_socket.recvfrom(1024) # 打印接收到的数据 print(f"接收到来自 {address} 的回复: {data.decode()}") # 关闭socket连接 client_socket.close() ``` 以上代码中,服务端通过创建一个UDP socket,并绑定指定端口,然后加入指定的组播组。客户端通过创建一个UDP socket并设置为广播类型,然后发送消息给组播组。服务端接收到客户端发送的消息后,将消息发送给组播组,客户端收到组播消息后打印出来。通过这种方式,服务端和客户端可以进行组播通信。 ### 回答3: 组播(Multicast)是一种基于UDP的通信协议,它允许一个服务器同时向多个客户端发送数据。下面是Socket组播服务端与客户端的示例代码服务端代码: ```python import socket def multicast_server(): multicast_group = '224.0.0.1' server_address = ('', 10000) # 创建组播套接字 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 设置套接字为可复用 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定地址和端口 sock.bind(server_address) # 将套接字加入组播组 group = socket.inet_aton(multicast_group) mreq = struct.pack('4sL', group, socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) while True: data, address = sock.recvfrom(1024) print(f'Received message: {data.decode()} from {address}') # 关闭套接字 sock.close() if __name__ == '__main__': multicast_server() ``` 客户端代码: ```python import socket def multicast_client(): multicast_group = '224.0.0.1' server_address = ('', 10000) # 创建组播套接字 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 设置套接字为可复用 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定地址和端口 sock.bind(server_address) # 将套接字加入组播组 group = socket.inet_aton(multicast_group) mreq = struct.pack('4sL', group, socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) while True: message = input('Enter message to send: ') # 发送消息到组播组 sock.sendto(message.encode(), (multicast_group, 10000)) # 关闭套接字 sock.close() if __name__ == '__main__': multicast_client() ``` 以上是一个基本的组播示例,服务端通过创建组播套接字绑定地址和端口,并加入组播组。客户端同样创建组播套接字并加入组播组,然后从用户输入获取消息并发送到组播组。通过组播可以实现向多个客户端同时发送相同的消息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值