服务器端
.h头文件
#ifndef CTCPNET_H
#define CTCPNET_H
#include <winsock2.h>
#include <windows.h>
#include <process.h>
#include <list>
#include <map>
#include <iostream>
#include <QDebug>
using namespace std;
class CTCPNet
{
public:
CTCPNet();
public:
//初始化网络
bool InitNetWork();
void UnInitNetWork();
//收发数据
bool SendData(SOCKET sock,char *szbuf,int nlen);
void RecvData();
public:
static unsigned _stdcall ThreadProc(void*);//全局变成局部
static unsigned _stdcall ThreadRecv(void*);
private:
SOCKET m_sock;
HANDLE m_thread;
bool m_flagquit;
list<HANDLE> m_lstThread;//用来存放创建线程返回的句柄
map<unsigned int,SOCKET> m_mapIdToSock;
};
#endif // CTCPNET_H
类.cpp
#include "ctcpnet.h"
#include <iostream>
#include <QDebug>
using namespace std;
CTCPNet::CTCPNet()
{
m_sock = NULL;
m_flagquit = true;
}
bool CTCPNet::InitNetWork()
{
//1.选择种类 火锅 韩餐 烤肉--
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 false;
}
if (LOBYTE(wsaData.wVersion) != 2 ||
HIBYTE(wsaData.wVersion) != 2)
{
UnInitNetWork();
return false;
}
else
printf("The Winsock 2.2 dll was found okay\n");
//2.雇个店长--
m_sock = socket(AF_INET,SOCK_STREAM,0);
if(m_sock == INVALID_SOCKET)
{
UnInitNetWork();
return false;
}
//3.找个地方
sockaddr_in addrserver;
addrserver.sin_family = AF_INET;
addrserver.sin_port = htons(8899);
addrserver.sin_addr.S_un.S_addr = 0;
if(SOCKET_ERROR == bind(m_sock,( const sockaddr *)&addrserver,sizeof(addrserver)))
{
UnInitNetWork();
return false;
}
//4.店长宣传
if(SOCKET_ERROR == listen(m_sock,10))
{
UnInitNetWork();
return false;
}
m_thread = (HANDLE)_beginthreadex(0,0,&ThreadProc,this,0,0);
if(m_thread)
{
m_lstThread.push_back(m_thread);
}
return true;
}
unsigned _stdcall CTCPNet::ThreadProc(void *lpvoid)
{
CTCPNet *pthis =(CTCPNet*)lpvoid;
unsigned int threadid;
//接收连接
while(pthis->m_flagquit){
//阻塞
SOCKET sockWaiter = accept(pthis->m_sock,0,0);
if (sockWaiter == INVALID_SOCKET){
continue;
}
//多线程
HANDLE hThread = (HANDLE)_beginthreadex(0,0,ThreadRecv,pthis,0,&threadid);
if (hThread){
pthis->m_lstThread.push_back(hThread);
pthis->m_mapIdToSock[threadid] = sockWaiter; //线程id与sockwaiter的映射
}
}
return 0;
}
unsigned _stdcall CTCPNet::ThreadRecv(void *lpvoid)
{
//一个线程对应一个waiter 用map
CTCPNet *pthis =(CTCPNet*)lpvoid;
pthis->RecvData();
return 0;
}
void CTCPNet::RecvData()
{
//获取线程id映射sockWaiter
SOCKET sockWaiter = m_mapIdToSock[GetCurrentThreadId()];
int nPackSize;
int nRecvNum;
char* szbuf=NULL;
int offset;
while(m_flagquit)
{
nRecvNum = recv(sockWaiter,(char*)&nPackSize,sizeof (int),0);
if (nRecvNum<0)
{
//客户端下线
if (WSAGetLastError() == WSAECONNRESET)
{
break;
}
continue;
}
if (nPackSize<=0) continue;
//解决粘包问题
szbuf = new char[nPackSize];
offset = 0;
while(nPackSize)
{
nRecvNum = recv(sockWaiter,szbuf+offset,nPackSize,0);
offset += nRecvNum;
nPackSize -= nRecvNum;
}
//处理数据
cout<<szbuf<<endl;
delete []szbuf;
szbuf = NULL;
}
}
bool CTCPNet::SendData(SOCKET sock,char *szbuf,int nlen)
{
if(sock == INVALID_SOCKET || !szbuf || nlen<0)
return false;
//发送大小
if(send(sock,(char*)&nlen,sizeof(int),0) <= 0)
return false;
//发送内容
if(send(sock,szbuf,nlen,0)<=0)
return false;
return true;
}
void CTCPNet::UnInitNetWork()
{
//销毁线程
m_flagquit = false;
list<HANDLE>::iterator ite = m_lstThread.begin();
while(ite != m_lstThread.end())
if (*ite)
{
if (WAIT_TIMEOUT==WaitForSingleObject(*ite,100))
{
TerminateThread(*ite,-1);
}
CloseHandle(*ite);
*ite = NULL;
}
ite++;
m_lstThread.clear();
//关闭套接字
if(m_sock){
closesocket(m_sock);
m_sock = NULL;
}
WSACleanup();
}
main.cpp
#include <QCoreApplication>
#include <ctcpnet.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CTCPNet tcpnet;
if (!tcpnet.InitNetWork())
{
cout<<" ctcpnet InitNetWork failed"<<endl;
}
return a.exec();
}
客户端
#include <QCoreApplication>
#include <winsock.h>
#include <iostream>
#include <QDebug>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
struct FileHeader{
char m_szFileName[MAX_PATH];
long m_FileSize;
};
#define MAX_RESULT 5
#define MAX_PAGE 4096
char g_szResult[MAX_RESULT];
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//加载库
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) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
printf("The ClientWinsock 2.2 dll was found okay\n");
//创建socket
SOCKET sock = socket(AF_INET,SOCK_STREAM,0);
if (sock == INVALID_SOCKET)
{
WSACleanup();
return 1;
}
//链接
sockaddr_in addrserver;
addrserver.sin_family = AF_INET;
addrserver.sin_port = htons(8899);
addrserver.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
if (SOCKET_ERROR == connect(sock,(const sockaddr*)&addrserver,sizeof (addrserver)))
{
closesocket(sock);
WSACleanup();
return 1;
}
//文件传输
//1.获取文件名、文件大小
char SZ_FileName[MAX_PATH] = {0};//SZ_FileName:文件路径 MAX_PATH:260字节
//cout<<"请输入要传输的文件名"<<endl;
qDebug()<<"请输入要传输的文件名";
cin>>SZ_FileName;
char *ptemp = SZ_FileName; //获取文件名:先让指针走到尾部,在往回走
while(*ptemp != '\0') ptemp++;
while(*ptemp != '\\') ptemp--;//“//”代表“/” “/”代表转义字符
ptemp++; //文件名
//文件大小
FILE *pfile = fopen(SZ_FileName,"rb");//rb:以二进制流的方式在读
if(!pfile) return 1;
//fseek():移动文件指针到文件尾:C中没有获取文件大小的方法,但是可以先移动到尾部,在获取当前指针的位置来得到文件的大小
fseek(pfile,0,SEEK_END);
long lFileSize = ftell(pfile);//ftell():获取文件指针指针当前所在位置。
//移动文件指针到文件头
fseek(pfile,0,SEEK_SET);
//2.发送
FileHeader fh;
//结构体成员赋初值
fh.m_FileSize = lFileSize;
strcpy(fh.m_szFileName,ptemp);
send(sock,(const char *)&fh,sizeof(fh),0);
//3.等对方回复 no yes
recv(sock,g_szResult,sizeof(g_szResult),0);
char szContent[MAX_PAGE] = {0};
//4.如果接收的话
if (0 == strcmp(g_szResult,"yes"))
{
while (1)
{
//5.读文件内容
size_t nReadNum = fread(szContent,sizeof(char),MAX_PAGE,pfile);
if (nReadNum > 0)
{
//6.发送
send(sock,szContent,nReadNum,0);
}
else{
break;
}
}
}
//7.关闭文件
fclose(pfile);
char szbuf[1024] = {0};
cin>>szbuf;
//发送
//1.发送包长度
int nPackSize = sizeof(szbuf);
send(sock,(const char*)&nPackSize,sizeof(int),0);//发送一块缓冲区,所以第二个参数就是(const char*)&nPackSize 第三个参数:代表第二个参数的大小 第二个参数是int类型,所以是sizeof(int)
//2.发送包内容
send(sock,szbuf,sizeof(szbuf),0);
//接收
int RecvNum = recv(sock,szbuf,sizeof (szbuf),0);
if (RecvNum > 0)
{
cout<<szbuf<<endl;
}
//关闭socket
closesocket(sock);
//卸载库
WSACleanup();
return a.exec();
}
该代码实现了一个基于Windows Socket的TCP网络服务器,能够接收客户端连接并进行文件传输。服务器端通过监听指定端口等待客户端连接,接收到文件传输请求后,接收文件并按块发送确认。客户端则负责连接服务器,发送文件信息并等待服务器确认,然后发送文件内容。整个过程涉及多线程处理、阻塞IO和文件读写操作。

被折叠的 条评论
为什么被折叠?



