Windows平台Socket通信实例

来源:互联网 发布:改短信号码软件 编辑:程序博客网 时间:2024/06/05 06:12

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 <winsock.h>#include <string>#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();//初始化SOCKETbool 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>// 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<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;}}


0 0