博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Windows平台Socket通信实例
阅读量:3527 次
发布时间:2019-05-20

本文共 15077 字,大约阅读时间需要 50 分钟。

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()。
文件接收端:
1. 加载和创建套接字,使用到的函数有WSAStartup()(初始化Windows的相关DLL)和socket()(创建一个套接字绑定到一个特定的传输方式如传输的方式、通信的类型)。
2. 绑定套接字到一个IP地址和一个端口上,使用bind()函数;
3. 将套接字设置为监听模式等待连接请求,listen();
4. 配置好监听之后,调用accept()函数等待请求,当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字;
5. 用等待连接函数返回的套接字,调用send()和recv()客户端进行通信;
6. 处理完数据之后,就等待另一连接请求;
7. 关闭套接字,关闭加载的套接字库closesocket()、WSACleanup()。

2. 编码

头文件内容:
#pragma once#include 
#include
#define BufferSize 1024*1024 //发送的缓存的大小// CTCPFileTransclass 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 
#include
// CTCPFileTransIMPLEMENT_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
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_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; }}

转载地址:http://bykhj.baihongyu.com/

你可能感兴趣的文章