Windows平台Socket通信实例
2023-09-11 14:22:29 时间
1. 概述
Windows平台下的Socket通信方式主要采用的有TCP(SOCK_STREAM)通信和UDP通信(SOCK_DGRAM)两种。对于第一种通信方式需要建立可靠的连接且要进行校验;另外一种网络传输方式不需要建立可靠的连接,也不进行校验,使用在语音通信和视频通信中。下面就将对Windows平台下创建TCP通信进行流程的说明和相关解释。
Windows上TCP通信的步骤:
文件发送端:
1. 加载和创建套接字,使用到的函数有WSAStartup()(初始化Windows的相关DLL)和socket()(创建一个套接字绑定到一个特定的传输方式如传输的方式、通信的类型)。
2. 向目标端发送连接请求使用的是connect()函数,函数调用成功返回0;
3. 向目标端发送数据send()函数;
4. 关闭套接字,关闭加载的套接字库closesocket()、WSACleanup()。
2. 向目标端发送连接请求使用的是connect()函数,函数调用成功返回0;
3. 向目标端发送数据send()函数;
4. 关闭套接字,关闭加载的套接字库closesocket()、WSACleanup()。
文件接收端:
1. 加载和创建套接字,使用到的函数有WSAStartup()(初始化Windows的相关DLL)和socket()(创建一个套接字绑定到一个特定的传输方式如传输的方式、通信的类型)。
2. 绑定套接字到一个IP地址和一个端口上,使用bind()函数;
3. 将套接字设置为监听模式等待连接请求,listen();
4. 配置好监听之后,调用accept()函数等待请求,当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字;
5. 用等待连接函数返回的套接字,调用send()和recv()客户端进行通信;
6. 处理完数据之后,就等待另一连接请求;
7. 关闭套接字,关闭加载的套接字库closesocket()、WSACleanup()。
2. 绑定套接字到一个IP地址和一个端口上,使用bind()函数;
3. 将套接字设置为监听模式等待连接请求,listen();
4. 配置好监听之后,调用accept()函数等待请求,当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字;
5. 用等待连接函数返回的套接字,调用send()和recv()客户端进行通信;
6. 处理完数据之后,就等待另一连接请求;
7. 关闭套接字,关闭加载的套接字库closesocket()、WSACleanup()。
2. 编码
头文件内容:
#pragma once
#include <winsock.h>
#include <string>
#define BufferSize 1024*1024 //发送的缓存的大小
// CTCPFileTrans
class CTCPFileTrans : public CWnd
{
DECLARE_DYNAMIC(CTCPFileTrans)
public:
CTCPFileTrans();
CTCPFileTrans(std::string ip, int port);
virtual ~CTCPFileTrans();
protected:
DECLARE_MESSAGE_MAP()
private:
SOCKADDR_IN addr;
public:
CProgressCtrl* m_SendProcessBar; //socket文件发送进度条
CEdit* m_SendLog; //socket文件发送打印日志
public:
static DWORD WINAPI SendThreadProc(LPVOID lpParameter); //创建线程去执行Socket操作
static DWORD WINAPI ReceiveThreadProc(LPVOID lpParameter); //创建线程去执行Socket操作
CString* file_path; //需要发送的文件路劲字符串
int m_SendFileNum; //需要发送的文件数目
void SetSendFilePath(CString* file_name, const int file_num); //发送字符串初始化,里面保存了需要发送的文件的路径
public:
void ShowSocketMessage(int Error_Code, bool is_show=true); //根据错误代码,提示相关错误
bool TestConnection(); //检查设备连接
//发送数据
public:
std::string ip_address; //接收方的IP地址
unsigned int port_num; //接收方的端口号
SOCKET m_socket; //发送的套接字变量
public:
bool GetSendSocket(); //初始化SOCKET
bool Sendfile(CString file_name); //
bool Sendfile(CString* file_name, const int file_count); //
//接收数据
public:
bool GetRecSocket(int listen_num = 5); //初始化监听调套接字
bool ReceiveFile(); //
SOCKET m_RecSocket; //监听套接字
};
源代码文件内容:
// TCPFileTrans.cpp : 实现文件
//
#include "stdafx.h"
#include "File_Trans.h"
#include "TCPFileTrans.h"
#include <winsock.h>
#include <windows.h>
// CTCPFileTrans
IMPLEMENT_DYNAMIC(CTCPFileTrans, CWnd)
CTCPFileTrans::CTCPFileTrans()
{
this->file_path = nullptr;
}
//
CTCPFileTrans::CTCPFileTrans(std::string ip, int port) : ip_address(ip), port_num(port)
{
this->file_path = nullptr;
}
CTCPFileTrans::~CTCPFileTrans()
{
if (file_path)
{
delete[] file_path;
file_path = nullptr;
}
}
BEGIN_MESSAGE_MAP(CTCPFileTrans, CWnd)
END_MESSAGE_MAP()
// CTCPFileTrans 消息处理程序
//************************************************************************
// 函数名称: InitSocket
// 访问权限: public
// 创建日期: 2017/04/10
// 创 建 人:
// 函数说明: 初始化SOCKET
// 返 回 值: bool
//************************************************************************
bool CTCPFileTrans::GetSendSocket()
{
if (this->ip_address=="")
{
MessageBox(_T("SOCKET初始化过程中,输入的IP地址为空,请检查配置"), _T("错误"), MB_OK|MB_ICONERROR);
return false;
}
if (this->port_num<=1024)
{
MessageBox(_T("SOCKET初始化过程中,输入的端口号与系统端口号冲突,请检查配置"), _T("错误"), MB_OK | MB_ICONERROR);
return false;
}
WSAData data;
if (0 != WSAStartup(MAKEWORD(2, 2), &data)) //初始化Windows Socket
{
int error_code(WSAGetLastError()); //获取错误代码
CString error_num=_T("");
error_num.Format(_T("%d"), error_code);
MessageBox(_T("windows socket 初始化错误,错误代码:")+error_num, _T("错误"), MB_OK|MB_ICONERROR);
WSACleanup();
return false;
}
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //网络流的TCP传输
memset(&this->addr, 0, sizeof(SOCKADDR_IN)); //初始化结构体
this->addr.sin_family = AF_INET; //通信类型
this->addr.sin_port = htons(this->port_num); //设置端口号
this->addr.sin_addr.S_un.S_addr = inet_addr(this->ip_address.c_str()); //设置ip地址
if (0 != connect(m_socket, (SOCKADDR*)&this->addr, sizeof(this->addr))) //连接到客户端
{
int error_code(WSAGetLastError()); //获取错误代码
this->ShowSocketMessage(error_code);
closesocket(this->m_socket);
WSACleanup();
return false;
}
return true;
}
//************************************************************************
// 函数名称: Sendfile
// 访问权限: public
// 创建日期: 2017/04/10
// 创 建 人:
// 函数说明: TCP发送单个文件
// 函数参数: std::string file_name 文件的路径
// 返 回 值: bool
//************************************************************************
bool CTCPFileTrans::Sendfile(CString file_name)
{
if (!this->GetSendSocket())
{
MessageBox(_T("发送单个文件,获取TCP连接失败!"), _T("错误"), MB_OK|MB_ICONERROR);
return false;
}
HANDLE hFile = CreateFile(file_name, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0); //获取文件句柄
if (INVALID_HANDLE_VALUE == hFile)
{
MessageBox(_T("打开需要发送的本地文件失败!"), _T("错误"), MB_OK | MB_ICONERROR);
return false;
} //有效性检查
LARGE_INTEGER m_SendFileSize; //文件的大小变量
GetFileSizeEx(hFile, &m_SendFileSize); //获取文件的字节大小
LARGE_INTEGER m_SizeLeft2Send(m_SendFileSize); //剩余需要发送的字节数
CString m_FileSize;
m_FileSize.Format(_T("%d"), m_SendFileSize.QuadPart); //转换得到的大小
//设置发送文件日志
this->m_SendLog->SetWindowTextW(_T(" 发送文件:") + file_name + _T(" 开始..."));
//发送文件的名字和大小
CString m_SendStr = file_name + _T("&&&&&&&&") + m_FileSize;
int send_length(m_SendStr.GetLength());
std::string send_str = CStringA(m_SendStr);
send(m_socket, send_str.c_str(), 2*m_SendStr.GetLength(), 0);
//定义发送缓存
char* m_SendBuffer = new char[BufferSize];
memset(m_SendBuffer, 0, sizeof(char)*BufferSize);
while (m_SizeLeft2Send.QuadPart > 0)
{
DWORD m_Bytes2Send(BufferSize); //每次发送字节的大小
DWORD m_ByteReaded(0); //每次读取文件读取到的字节数
if (m_SizeLeft2Send.QuadPart <= m_Bytes2Send)
{
m_Bytes2Send = m_SizeLeft2Send.QuadPart;
} //如果需要传输的文件字节数小于发送的缓存大小,就将发送的缓存设置为文件的大小
ReadFile(hFile, m_SendBuffer, m_Bytes2Send, &m_ByteReaded, 0);
m_SizeLeft2Send.QuadPart -= m_ByteReaded;
//更新发送进度条
this->m_SendProcessBar->SetPos(int(m_SendFileSize.QuadPart-m_SizeLeft2Send.QuadPart)/m_SendFileSize.QuadPart);
send(m_socket, m_SendBuffer, m_ByteReaded, 0);
}
//设置发送文件日志
//更新发送进度条
this->m_SendProcessBar->SetPos(100);
this->m_SendLog->SetWindowTextW(_T(" 发送文件:") + file_name + _T(" 100%"));
CloseHandle(hFile); //关闭文件句柄
closesocket(this->m_socket);
WSACleanup();
delete[] m_SendBuffer;
m_SendBuffer = nullptr;
return true;
}
//************************************************************************
// 函数名称: Sendfile
// 访问权限: public
// 创建日期: 2017/04/10
// 创 建 人:
// 函数说明: TCP发送一组文件
// 函数参数: std::string * file_name 一组文件路径
// 函数参数: const int file_count 文件的数目
// 返 回 值: bool
//************************************************************************
bool CTCPFileTrans::Sendfile(CString* file_name, const int file_count)
{
//if (!this->GetSendSocket())
//{
// MessageBox(_T("发送一组文件,获取TCP连接失败!"), _T("错误"), MB_OK | MB_ICONERROR);
// return false;
//}
for (int i=0; i<file_count; i++)
{
if (!this->Sendfile(file_name[i]))
{
MessageBox(_T("在发送文件:") + file_name[i] + _T(" 时发生错误,发送失败!"),
_T("错误"), MB_ICONERROR|MB_OK);
return false;
}
}
return true;
}
//************************************************************************
// 函数名称: SetSendFilePath
// 访问权限: public
// 创建日期: 2017/04/12
// 创 建 人:
// 函数说明: 发送字符串初始化,里面保存了需要发送的文件的路径
// 函数参数: CString * file_name
// 函数参数: const int file_num
// 返 回 值: void
//************************************************************************
void CTCPFileTrans::SetSendFilePath(CString* file_name, const int file_num)
{
this->file_path = new CString[file_num];
this->m_SendFileNum = file_num;
for (int i=0; i<file_num; i++)
{
this->file_path[i] = file_name[i];
}
}
//************************************************************************
// 函数名称: SendThreadProc
// 访问权限: public static
// 创建日期: 2017/04/12
// 创 建 人:
// 函数说明: socket发送文件的线程
// 函数参数: LPVOID lpParameter
// 返 回 值: DWORD WINAPI
//************************************************************************
DWORD WINAPI CTCPFileTrans::SendThreadProc(LPVOID lpParameter) //创建线程去执行Socket操作
{
DWORD temp = 0;
CTCPFileTrans* file_send = (CTCPFileTrans*)lpParameter;
if (1 == file_send->m_SendFileNum)
{
file_send->Sendfile(file_send->file_path[0]);
}
else if (1 <= file_send->m_SendFileNum)
{
file_send->Sendfile(file_send->file_path, file_send->m_SendFileNum);
}
return temp;
}
//************************************************************************
// 函数名称: ReceiveThreadProc
// 访问权限: public static
// 创建日期: 2017/04/12
// 创 建 人:
// 函数说明: socket文件接收线程
// 函数参数: LPVOID lpParameter
// 返 回 值: DWORD WINAPI
//************************************************************************
DWORD WINAPI CTCPFileTrans::ReceiveThreadProc(LPVOID lpParameter) //创建线程去执行Socket操作
{
DWORD temp = 0;
CTCPFileTrans* file_send = (CTCPFileTrans*)lpParameter;
while (true)
{
file_send->ReceiveFile();
}
return temp;
}
//************************************************************************
// 函数名称: GetRecSocket
// 访问权限: public
// 创建日期: 2017/04/11
// 创 建 人:
// 函数说明: 获取监听的Socket
// 返 回 值: bool
//************************************************************************
bool CTCPFileTrans::GetRecSocket(int listen_num)
{
if (this->ip_address == "")
{
MessageBox(_T("SOCKET初始化过程中,输入的IP地址为空,请检查配置"), _T("错误"), MB_OK | MB_ICONERROR);
return false;
}
if (this->port_num <= 1024)
{
MessageBox(_T("SOCKET初始化过程中,输入的端口号与系统端口号冲突,请检查配置"), _T("错误"), MB_OK | MB_ICONERROR);
return false;
}
WSAData data;
if (0 != WSAStartup(MAKEWORD(2, 2), &data)) //初始化Windows Socket
{
int error_code(WSAGetLastError()); //获取错误代码
CString error_num = _T("");
error_num.Format(_T("%d"), error_code);
MessageBox(_T("windows socket 初始化错误,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
WSACleanup();
return false;
}
m_RecSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //网络流的TCP传输
memset(&this->addr, 0, sizeof(SOCKADDR_IN)); //初始化结构体
this->addr.sin_family = AF_INET; //通信类型
this->addr.sin_port = htons(this->port_num); //设置端口号
this->addr.sin_addr.S_un.S_addr = inet_addr(this->ip_address.c_str()); //设置ip地址
if (0 != bind(m_RecSocket, (SOCKADDR*)&this->addr, sizeof(this->addr))) //绑定本地端口
{
int error_code(WSAGetLastError()); //获取错误代码
CString error_num = _T("");
error_num.Format(_T("%d"), error_code);
MessageBox(_T("windows socket 绑定本地端口错误,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
WSACleanup();
return false;
}
//配置监听
if (0 != listen(m_RecSocket, listen_num))
{
int error_code(WSAGetLastError()); //获取错误代码
CString error_num = _T("");
error_num.Format(_T("%d"), error_code);
MessageBox(_T("windows socket 绑定本地端口错误,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
WSACleanup();
return false;
}
return true;
}
//************************************************************************
// 函数名称: ReceiveFile
// 访问权限: public
// 创建日期: 2017/04/11
// 创 建 人:
// 函数说明: 接收文件
// 返 回 值: bool
//************************************************************************
bool CTCPFileTrans::ReceiveFile()
{
int len(sizeof(this->addr));
SOCKET m_SocketAccept = accept(m_RecSocket, (SOCKADDR*)&this->addr, &len); //接入套接字
if (INVALID_SOCKET == m_SocketAccept) //获取
{
int error_code(WSAGetLastError()); //获取错误代码
CString error_num = _T("");
MessageBox(_T("windows socket 接入套接字获取错误,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
WSACleanup();
return false;
}
int receive_size(-1);
char* m_ReveiveBuffer = new char[BufferSize];
memset(m_ReveiveBuffer, 0, sizeof(char)*BufferSize);
receive_size = recv(m_SocketAccept, m_ReveiveBuffer, BufferSize, 0);
LARGE_INTEGER m_FileSize; //文件大小
HANDLE hFile; //文件句柄
std::string file_name;
if (receive_size > 0)
{
std::string file_str = m_ReveiveBuffer;
file_name = file_str.substr(0, file_str.find_first_of("&&&&&&&&"));
file_name = file_name.substr(file_name.find_last_of("\\") + 1, file_name.length());
std::string file_size = file_str.substr(file_str.find_first_of("&&&&&&&&") + 8, file_str.length());
m_FileSize.QuadPart = std::atoi(file_size.c_str());
}
else{ return false; }
hFile = CreateFile(CA2T(file_name.c_str()), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
MessageBox(_T("创建下载文件:") + (CString)CA2T(file_name.c_str()) + _T("文件失败"), _T("错误"), MB_OK | MB_ICONERROR);
return false;
}
while (m_FileSize.QuadPart > 0)
{
DWORD m_WirtedByte;
receive_size = ::recv(m_SocketAccept, m_ReveiveBuffer, BufferSize, 0);
::WriteFile(hFile, m_ReveiveBuffer, receive_size, &m_WirtedByte, NULL);
m_FileSize.QuadPart -= m_WirtedByte;
}
CloseHandle(hFile);
}
//************************************************************************
// 函数名称: TestConnection
// 访问权限: public
// 创建日期: 2017/04/11
// 创 建 人:
// 函数说明: 检查网络连接
// 返 回 值: bool
//************************************************************************
bool CTCPFileTrans::TestConnection()
{
if (this->ip_address == "")
{
MessageBox(_T("SOCKET初始化过程中,输入的IP地址为空,请检查配置"), _T("错误"), MB_OK | MB_ICONERROR);
return false;
}
if (this->port_num <= 1024)
{
MessageBox(_T("SOCKET初始化过程中,输入的端口号与系统端口号冲突,请检查配置"), _T("错误"), MB_OK | MB_ICONERROR);
return false;
}
WSAData data;
if (0 != WSAStartup(MAKEWORD(2, 2), &data)) //初始化Windows Socket
{
int error_code(WSAGetLastError()); //获取错误代码
CString error_num = _T("");
error_num.Format(_T("%d"), error_code);
MessageBox(_T("windows socket 初始化错误,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
WSACleanup();
return false;
}
SOCKET m_socket_temp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //网络流的TCP传输
memset(&this->addr, 0, sizeof(SOCKADDR_IN)); //初始化结构体
this->addr.sin_family = AF_INET; //通信类型
this->addr.sin_port = htons(this->port_num); //设置端口号
this->addr.sin_addr.S_un.S_addr = inet_addr(this->ip_address.c_str()); //设置ip地址
if (0 != connect(m_socket_temp, (SOCKADDR*)&this->addr, sizeof(this->addr))) //连接到客户端
{
int error_code(WSAGetLastError()); //获取错误代码
this->ShowSocketMessage(error_code);
closesocket(m_socket_temp);
WSACleanup();
return false;
}
closesocket(m_socket_temp);
WSACleanup();
return true;
}
//************************************************************************
// 函数名称: ShowSocketMessage
// 访问权限: public
// 创建日期: 2017/04/11
// 创 建 人:
// 函数说明: 根据Socket返回回来的错误代码,弹出响应的消息提示框
// 函数参数: int Error_Code
// 返 回 值: void
//************************************************************************
void CTCPFileTrans::ShowSocketMessage(int Error_Code, bool is_show)
{
if (!is_show)
{
return;
} //不显示错误代码提示
int code(Error_Code);
CString error_num = _T("");
error_num.Format(_T("%d"), code);
switch (code)
{
case WSAEALREADY:
MessageBox(_T("非阻塞的连接请求正在特定的socket中处理,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
break;
case WSAEADDRNOTAVAIL:
MessageBox(_T("远程的连接地址不可用,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
break;
case WSAEAFNOSUPPORT:
MessageBox(_T("指定的传输类型在本socket中不受支持,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
break;
case WSAECONNREFUSED:
MessageBox(_T("socket连接请求被阻止,检查客户端是否开机启动,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
break;
case WSAEFAULT:
MessageBox(_T("当前本地地址配置对于当前传输协议不正确,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
break;
case WSAEISCONN:
MessageBox(_T("当前socket已经连接了,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
break;
case WSAETIMEDOUT:
MessageBox(_T("连接次超过了规定次数,错误代码:") + error_num, _T("错误"), MB_OK | MB_ICONERROR);
break;
default:
break;
}
}
相关文章
- Windows PE第九章 线程局部存储
- Windows 签名伪造工具的使用,Python,签名
- C# 编写Windows Service(windows服务程序)
- windows默认管理员运行程序
- windows系统-函数的条件分支实现用汇编语言解释
- 通过Windows API 创建窗口的过程理解windows消息机制
- 【Windows】+ windows下在某一文件夹下按“shift+鼠标右键”打开CMD窗口
- 《Windows网络与通信程序设计(第3版)》——2.4 网络对时程序实例
- Windows知识点-解决端口占用
- 微软自曝Windows 10新预览版:更多变化
- Windows cmd 快捷操作
- Windows中删除长路径文件
- Windows下MySQL的安装步骤(有图详解)
- 云服务器 ECS快速入门:Windows 格式化数据盘
- Windows下C++多线程编程(入门实例)
- Docker for Windows 10应用进入测试阶段
- MySQL8.0.15基于mycat读写分离(windows环境)
- Windows使用cmd命令创建用户
- Windows/Centos安装GO语言环境