编译环境:Win10 VS2019
server.cpp
// server.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <stdio.h>
#include "stdafx.h"
#include "network.h" //UDP 网络编程
int _tmain(int argc, char* argv[])
{
int errCode = 0;
errCode = InitializeSocket(); //初始化套接字库
if (errCode == -1) //初始化套接字库失败,结束
{
return -1;
}
int s = BindSocketPort(8090); //绑定到本地地址和8090端口上,成功返回套接字,失败返回-1
if (s == -1)
{
return -1;
}
StartServer(s);
return 0;
}
network.h
#pragma once
#pragma pack(1)
struct FileData
{
int id; //数据类型标志位(数据或者文件名及大小)
int index; //索引号
char fileData[1024]; //对应索引的数据
};
#pragma pack()
//初始化套接字库
int InitializeSocket();
//bind套接字到某个端口之上
int BindSocketPort(short port);
//开始接收和发送数据
void StartServer(int s);
//线程函数
unsigned long RecvProc(void* lpParameter);
network.cpp
#include "network.h"
#include <stdio.h>
#include "targetver.h"
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib") //导入网络库
typedef unsigned long DWORD;
//初始化套接字库
int InitializeSocket()
{
WORD wVersionRequested;
WSADATA wsadata;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsadata);
if (err != 0)
{
printf("WSAStartup failed with error: %d\n", err);
return -1;
}
return 1;
}
//bind套接字到某个端口之上
int BindSocketPort(short port)
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建一个UDP套接字
sockaddr_in addr; //代表IP地址和端口
addr.sin_family = AF_INET;
addr.sin_port = htons(port); //转化字节序
addr.sin_addr.S_un.S_addr = 0; //愿意在本机的任意IP上接收数据
//绑定到本机的IP和端口上
int ret = bind(sockfd, (sockaddr*)&addr, sizeof(sockaddr));
if (ret == -1)
{
printf("bind failed\n");
return -1;
}
return sockfd;
}
//开始接收和发送数据
void StartServer(int s)
{
sockaddr_in clientaddr;
int len = sizeof(sockaddr);
char recvBuf[1024] = { 0 };
recvfrom(s, recvBuf, 1024, 0, (sockaddr*)&clientaddr, &len); //建立与客户端的连接
printf("client say:%s\n", recvBuf);
//创建一个线程之后,创建完成后线程函数开始执行,且与主线程同时执行
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&RecvProc, (void*)&s, 0, 0);
while (1)
{
char buf[1024] = { 0 };
gets_s(buf, 1024); //接收用的数据
sendto(s, buf, strlen(buf), 0, (sockaddr*)&clientaddr, sizeof(sockaddr));
}
}
//线程函数
unsigned long RecvProc(void *lpParameter)
{
int sockfd = *(int*)lpParameter; //套接字通过参数传进来
printf("waiting for receive data...\n");
while (1)
{
char recvBuf[1500] = { 0 };
int recvLen = recvfrom(sockfd, recvBuf, 1500, 0, 0, 0); //接收数据,阻塞的函数,如果对方不发数据,该函数将一直等待
//区分消息类型是数据还是文件名及文件大小
int id = *(int*)recvBuf; //得到id号
static unsigned char* pRecvFileData; //文件数据指针
//接收到的数据
char fileName[256] = { 0 }; //文件名
static int fileSize = 0; //文件大小
static int recvedFileLen = 0; //已经接收到的文件大小
static FILE* fp = 0;
switch (id)
{
case 1: //消息类型是数据
{
//消息到来
FileData* fd = (FileData*)recvBuf; //消息
//printf("data: %s %d", recvLen, fd->fileData);
fd->id;
fd->fileData;
memcpy(pRecvFileData + fd->index * 1024, fd->fileData, recvLen - sizeof(int) * 2); //将到来消息上的数据放到指定位置
//printf("%0x -- %s\n", pRecvFileData, pRecvFileData);
recvedFileLen += (recvLen - sizeof(int) * 2);
printf("recvFileLen = %d - %d\n", recvedFileLen, recvLen);
}
break;
case 2: //消息类型为文件名及文件大小
{
memcpy(&fileSize, recvBuf + 4, 4); //将收到的数据的前4字节拷贝到filesize得到文件大小
strcpy_s(fileName, 256, recvBuf + 8); //获得文件名称
pRecvFileData = new unsigned char[fileSize]; //用来接收数据
memset(pRecvFileData, 0, fileSize); //清空脏数据
fopen_s(&fp, fileName, "wb"); //打开文件,只是文件名,没有路径
printf("fileName: %s, fileSize: %d\n", fileName, fileSize);
}
break;
default:
break;
}
//接收到数据
//当文件接收完成,关闭文件
//printf("%d - %d\n", recvedFileLen, fileSize);
if (recvedFileLen == fileSize && recvedFileLen != 0) //文件接收完毕
{
//将所有数据写入文件
printf("receive file finished: %s\n", pRecvFileData);
fwrite(pRecvFileData, recvedFileLen, 1, fp);
fclose(fp);
recvedFileLen = 0;
}
}
return 0;
}
client.cpp
// client.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "stdafx.h"
#include "network.h"
int _tmain(int argc, _TCHAR *argv[])
{
InitializeSocket();
StartTalking("127.0.0.1", 8090);
return 0;
}
network.h
//头文件存放函数声明,类的声明
//防止头文件被重复包含
#pragma once
//初始化套接字库
int InitializeSocket();
//创建套接字,开始发送及接收数据
int StartTalking(char* ip, short port);
//由文件路径得到文件名
char* getFileName(char* filePath);
//获取文件长度
int GetFileLength(char* filename);
//线程函数
unsigned long RecvProc(void* lpParameter);
network.cpp
#include "network.h"
#include <stdio.h>
#include "targetver.h"
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib") //导入网络库
//初始化套接字库
int InitializeSocket()
{
WORD wVersionRequested;
WSADATA wsadata;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsadata);
if (err != 0)
{
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
return 0;
}
struct FileData
{
int id; //数据标志位(数据或者文件名及大小)
int index; //索引号
char fileData[1024]; //对应索引的数据
};
//创建套接字
int StartTalking(char *ip, short port)
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建一个UDP套接字
sockaddr_in addr; //代表IP地址和端口
addr.sin_family = AF_INET;
addr.sin_port = htons(port); //转化字节序
addr.sin_addr.S_un.S_addr = inet_addr(ip); //可以在本机的任意IP上接收数据
if (sockfd != -1)
{
sendto(sockfd, "say hello", strlen("say hello"), 0, (sockaddr*)&addr, sizeof(addr)); //和服务器建立连接
}
//创建一个线程,创建完之后线程函数开始执行,且与主线程同时执行
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&RecvProc, (void*)&sockfd, 0, 0);
//发送数据
while (1)
{
char buf[1024] = { 0 };
printf("please input filename to send:");
gets_s(buf, 1024); //获得用户的输入,发送到对方,buf放的不是文件(路径)
//文件大小获取
int filesize = GetFileLength(buf); //获取文件大小,放到filesize中
char sendBuf[1024] = { 0 }; //用于发送到服务器缓冲区
int fileSizeID = 2; //该消息的标志
char* fileName = getFileName(buf); //获得文件名
memcpy(sendBuf, &fileSizeID, 4);
memcpy(sendBuf + 4, &filesize, 4); //内存拷贝,将文件大小拷贝到缓冲区的前4字节
memcpy(sendBuf + 8, fileName, strlen(fileName)); //拷贝文件名称到内存缓冲区
//将文件大小及名称发送到服务器
sendto(sockfd, sendBuf, sizeof(int) * 2 + strlen(fileName), 0, (sockaddr*)&addr, sizeof(addr));
printf("filename is sended,press any key to continue.");
getchar();
//获取文件数据
FILE* fp = 0;
fopen_s(&fp, buf, "rb"); //以读取形式打开文件(r读文本文件,rb可读二进制文件)
//循环发送数据到服务器,直到文件末尾
int index = 0;
while(!feof(fp)) //如果没有到文件的结尾,一直循环
{
FileData fd;
memset(&fd, 0, sizeof(fd)); //清空变量fd(缓冲区清空)
fd.id = 1; //数据类型的标志
fd.index = index++;
int readSize = fread_s(fd.fileData, 1024, 1, 1024, fp); //从文件中读取1024字节的数据,放入fileData中
printf("reading file: %d\n", readSize);
//发送文件中的数据
sendto(sockfd, (char *)&fd, readSize + sizeof(int) * 2, 0, (sockaddr*)&addr, sizeof(addr)); //将fileData发送到服务器
Sleep(10); //休眠1ms
}
}
return sockfd;
}
//由文件路径得到文件名
char* getFileName(char* filePath)
{
int len = strlen(filePath);
for (int i = len; i >= 0; i--)
{
if (filePath[i] == '/' || filePath[i] == '\\')
{
return &filePath[i + 1];
}
}
return 0;
}
//获取文件大小,文件必须存在
int GetFileLength(char* fileName)
{
FILE *fp;
fopen_s(&fp, fileName, "r");
if (fp == 0)
{
return -1;
}
fseek(fp, 0, SEEK_END);
int size = ftell(fp);
return size;
}
//线程函数
unsigned long RecvProc(void* lpParameter)
{
int sock = *(int*)lpParameter;
printf("waiting to receive data...\n");
while (1)
{
char recvBuf[1024] = { 0 };
int ret = recvfrom(sock, recvBuf, 1024, 0, 0, 0);
printf("server say:%s\n", recvBuf);
}
return 0;
}
targetver.h
#pragma once
#include <SDKDDKVer.h> //将定义可用的最高版本的Windows平台
stdafx.h
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
stdafx.cpp
#pragma once
#include "stdafx.h"