利用tcp协议实现本机上的文件传输,使用c++中fstream对文件进行操作
实现时出现的问题
1. 在接受数据时,文件大小应当根据接收到字符串的实际长度来减少,而不是根据接收时recv返回值来减小
2.在接受数据时,接受循环条件写错,写为(文件大小 == 0 ) ,以及根据接收时recv返回值来减小,导致减为负数造成死循环
3.在vs2022中使用inet_addr()为sin_addr赋值会报错,不安全
sockline.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
所以使用WS2tcpip.h头文件下的InetPtonW()
InetPtonW(AF_INET, L"127.0.0.1", &sockline.sin_addr);
收获
1.在命名时注意名字的意义,一个好名字可以不出错
2.对待问题考虑要全面,不仅仅要知道怎么能改对,还要知道bug产生的原因,bug造成的后果
服务器
#include<iostream>
#include<WinSock2.h>
#include<Windows.h>
#include<fstream>
#pragma comment(lib,"Ws2_32")
using namespace std;
DWORD WINAPI ThreadProc(
_In_ LPVOID lpParameter
);//线程函数声明
int main()
{
//创建连接 wsastartup()
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;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
printf("The Winsock 2.2 dll was found okay\n");
//创建套接字
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET)
{
WSACleanup();
return 1;
}
//绑定地址 bind,这里 sockaddr 使用的是sockaddr_in类型的
sockaddr_in sockline;
sockline.sin_family = AF_INET;
sockline.sin_port = htons(1234);
sockline.sin_addr.S_un.S_addr = 0;//服务器为本机,可以直接赋值0
if (SOCKET_ERROR == bind(sock, (const sockaddr*)&sockline, sizeof(sockline)))
{
closesocket(sock);
WSACleanup();
return 1;
}
//监听信息
if (listen(sock, 10))
{
closesocket(sock);
WSACleanup();
return 1;
}
sockaddr sockll;
int nsize = sizeof(sockll);
while (1)
{
//为监听到的信息创建套接字
SOCKET sockwinter = accept(sock, &sockll, &nsize);
//创建线程
CreateThread(0, 0, &ThreadProc, (void*)sockwinter, 0, 0);
}
closesocket(sock);
WSACleanup();
return 0;
}
DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter)
{
SOCKET sockwinter = (SOCKET)lpParameter;
while (1)
{
char wenjianmingjie[1024] = {0};//接受信息的字符串
int wenjiandaxiao = 0;//文件大小
char wenjianming[64] = {0};//文件名
//接受文件名,文件大小
int nrecv1 = recv(sockwinter, wenjianmingjie, sizeof(wenjianmingjie), 0);
if (nrecv1 >= 0)//接受到一个字符串前面为名字,后面为大小,中间空格连接
{
char* c = wenjianmingjie;
int i = 0;
while (*c != ' '&&*c != '\0')
{
wenjianming[i++] = *c;
c++;
}
cout << wenjianming << endl;
c++;
while (*c != '\0')
{
wenjiandaxiao = wenjiandaxiao * 10 + ( * c - '0');
c++;
}
}
//是否接受,发送信息给客户端
cout << "是否接受" << endl;
cout << "yes or no" << endl;
char querenxinxi[100];
cin >> querenxinxi;
send(sockwinter, querenxinxi, sizeof(querenxinxi), 0);
//进行比较
if (strcmp(querenxinxi,"yes") == 0)
{
char baocunweizhi[1024];//保存位置字符串
cout << "请输入保存位置" << endl;
cin >> baocunweizhi;
ofstream os;
os.open(baocunweizhi);
while (wenjiandaxiao > 0)//文件大小,每次接收到就减小
{
char wenjianhuan[1024] = {0};
int nrecv2 = recv(sockwinter, wenjianhuan, sizeof(wenjianhuan),0);
if (nrecv2 >= 0)
{
wenjiandaxiao -= (int)strlen(wenjianhuan);//减去实际接受到的信息大小
os << wenjianhuan;//写入文件
}
}
os.close();//关闭文件
}
}
}
客户端
#include<iostream>
#include<WinSock2.h>
#include<Windows.h>
#include<fstream>
#include<WS2tcpip.h>
#pragma comment(lib,"Ws2_32")
using namespace std;
int main()
{
//建立连接
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;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
printf("The Winsock 2.2 dll was found okay\n");
//创建套接字
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in sockline;
sockline.sin_family = AF_INET;
sockline.sin_port = htons(1234);
InetPtonW(AF_INET, L"127.0.0.1", &sockline.sin_addr);
if (SOCKET_ERROR == connect(sock, (const sockaddr*)&sockline, sizeof(sockline)))
{
closesocket(sock);
WSACleanup();
return 1;
}
//打开文件
ifstream is;
char wenjiandizhi[1024];
cout << "请输入文件地址" << endl;
cin >> wenjiandizhi;
//解析出文件名
char wenjianming[62] = {0};
char* c = wenjiandizhi;
while (*c != '\0')
{
c++;
}
while (*c != '\\')
{
c--;
}
c++;
strcpy_s(wenjianming, c);
//读取文件大小
unsigned int wenjiandaxiao = 0;
char wenjian[1024];
is.open(wenjiandizhi);
while (is >> wenjian)
{
wenjiandaxiao += strlen(wenjian);
}
is.close();
//将文件名和文件大小串联在一起,中间用空格连接
char s[1024];
sprintf_s(s, "%d", wenjiandaxiao);
strcat_s(wenjianming," ");
strcat_s(wenjianming,s);
//发送文件名字与大小
send(sock, wenjianming, sizeof(wenjianming), 0);
//接受确认信息
char querenxinxi[100];
int recvn = recv(sock, querenxinxi, sizeof(querenxinxi), 0);
if (recvn >= 0)
{
if (strcmp(querenxinxi,"yes") == 0)
{
//发送文件
is.open(wenjiandizhi);
while( is >> wenjian)
send(sock, wenjian, sizeof(wenjian), 0);
is.close();
}
}
closesocket(sock);
WSACleanup();
return 0;
}
存在问题
在上面通信中使用的是多线程实现一个服务器与多线程通信,在创建线程和套接字时没有保存下来句柄和套接字,应使用数组记录下来,在使用结束时关闭线程和套接字