Windows套接字在两种模型下执行I/O操作,阻塞和非阻塞。在阻塞模式下,在I/O操作完成前,执行操作的WinSock函数会一直等待下去,不会立即返回程序(将控制权交换给程序)。而在非阻塞模式下,WinSock函数无论如何都会立即返回。
Windows Sockets为了支持Windows消息驱动极值,使应用程序开发者能够方便地处理网络通信,它对网络时间采用了基于消息的异步存取策略。
Windows Sockets的异步选择函数WSAAsyncSelect()函数提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与事件相关的一些信息。
WSASocket:创建一个与指定传送服务提供者捆绑的套接口,可选地创建和/或加入一个套接口组。
SOCKET WSASocket(
int af,
int type,
int protocol,
LPWSAPROTOCOL_INFO lpProtocolInfo,
GROUP g,
DWORD dwFlags
);
/*
参数描述
af:[in]一个地址族规范。目前仅支持AF_INET格式,亦即ARPA Internet地址格式。
type:新套接口的类型描述。
protocol:套接口使用的特定协议,如果调用者不愿指定协议则定为0。
lpProtocolInfo:一个指向PROTOCOL_INFO结构的指针,该结构定义所创建套接口的特性。如果本参数非零,则前三个参数(af, type, protocol)被忽略。
g:保留给未来使用的套接字组。套接口组的标识符。
iFlags:套接口属性描述。
*/
socket() 函数创建一个通讯端点并返回一个套接口。但是在socket库中例程在应用于阻塞套接口时会阻塞。WSASocket()的发送操作和接收操作都可以被重叠使用。接收函数可以被多次调用,发出接收缓冲区,准备接收到来的数据。发送函数也可以被多次调用,组成一个发送缓冲区队列。可是socket()却只能发过之后等待回消息才可做下一步操作!
WSAAsyncSelect:基于windows消息的网络事件通知套接字请求
int WSAAsyncSelect(
__in SOCKET s,
__in HWND hWnd,
__in unsigned int wMsg,
__in long lEvent
);
/*
s:用于标识需要事件通知的套接字的描述符
hWnd:一个句柄,用于标识在发生网络事件时将接收消息的窗口。
wMsg:发生网络事件时要接收的消息。
lEvent:一个位掩码,指定应用程序感兴趣的网络事件的组合。
*/
Value | Meaning |
---|---|
FD_READ | Set to receive notification of readiness for reading. |
FD_WRITE | Wants to receive notification of readiness for writing. |
FD_OOB | Wants to receive notification of the arrival of OOB data. |
FD_ACCEPT | Wants to receive notification of incoming connections. |
FD_CONNECT | Wants to receive notification of completed connection or multipoint join operation. |
FD_CLOSE | Wants to receive notification of socket closure. |
FD_QOS | Wants to receive notification of socket Quality of Service (QOS) changes. |
FD_GROUP_QOS | Wants to receive notification of socket group Quality of Service (QOS) changes (reserved for future use with socket groups). Reserved. |
FD_ROUTING_INTERFACE_CHANGE | Wants to receive notification of routing interface changes for the specified destination(s). |
FD_ADDRESS_LIST_CHANGE | Wants to receive notification of local address list changes for the socket protocol family. |
WSARecvFrom:接收套接字上的数据并存储源地址。
int WSARecvFrom(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
struct sockaddr FAR* lpFrom,
LPINT lpFromlen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
/*
s:标识套接字描述符
lpBuffers:一个指向WSABUF结构体的指针,每一个WSABUF结构体包含一个缓冲区的指针和缓冲区的长度。
dwBufferCount:lpBuffers数组中WSABUF结构体的数目。
lpNumberOfBytesRecvd:如果接收操作立即完成,则为一个指向本次调用所接收的字节数的指针
lpFlags:一个指向标志位的指针
lpFrom:可选指针,指向重叠操作完成后存放源地址的缓冲区
lpFromlen:指向from缓冲区大小的指针,仅当指定了lpFrom才需要
lpOverlapped:一个指向WSAOVERLAPPED结构体的指针(对于非重叠套接字则忽略)
lpCompletionRoutine:一个指向接收操作完成时调用的完成例程的指针(对于非重叠套接字则忽略)
/*
WSASendTo:此函数使用重叠的I / O将数据发送到特定目标。
int WSASendTo(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
const struct sockaddr FAR* lpTo,
int iToLen,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
以上就是我们本次需要用到的函数,另外我们需要引入头文件WinSock2.h,以及链接ws2_32.lib。
Chart.cpp中加载套接字库2.2版本
BOOL CChartApp::InitInstance()
{
//加载套接字库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return FALSE;
}
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 ) {
WSACleanup( );
return FALSE;
}
//省略....
}
ChartDlg.cpp
// ChartDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "Chart.h"
#include "ChartDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CChartDlg::CChartDlg(CWnd* pParent /*=NULL*/)
: CDialog(CChartDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_socket = 0;
}
CChartDlg::~CChartDlg()
{
if (m_socket)
{
closesocket(m_socket);
}
}
void CChartDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CChartDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_MESSAGE(UM_SOCK, OnSock)
ON_BN_CLICKED(IDC_BTN_SEND, &CChartDlg::OnBnClickedBtnSend)
END_MESSAGE_MAP()
// CChartDlg 消息处理程序
BOOL CChartDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//....
// TODO: 在此添加额外的初始化代码
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->SetAddress(127,0,0,1);
InitSocket();
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
LRESULT CChartDlg::OnSock(WPARAM wParam, LPARAM lParam)
{
switch(LOWORD(lParam))
{
case FD_READ:
WSABUF wsaBuf;
wsaBuf.buf = new char[200];
wsaBuf.len = 200;
DWORD dwRead;
DWORD dwFlag = 0;
SOCKADDR_IN addrFrom;
int len = sizeof(SOCKADDR);
if (SOCKET_ERROR == WSARecvFrom(m_socket, &wsaBuf, 1, &dwRead, &dwFlag,
(SOCKADDR*)&addrFrom, &len, NULL, NULL))
{
AfxMessageBox(_T("接收数据失败!"));
return FALSE;
}
CString strMsg, strTemp;
strMsg.Format(_T("%s say: %s"), inet_ntoa(addrFrom.sin_addr), wsaBuf.buf);
strMsg.Append(_T("\r\n"));
GetDlgItemText(IDC_EDIT_RECV, strTemp);
strMsg.Append(strTemp);
SetDlgItemText(IDC_EDIT_RECV, strMsg);
break;
}
return TRUE;
}
BOOL CChartDlg::InitSocket()
{
m_socket = WSASocket(AF_INET, SOCK_DGRAM, 0,NULL, 0, 0);
if (INVALID_SOCKET == m_socket)
{
AfxMessageBox(_T("创建套接字失败!"));
return FALSE;
}
SOCKADDR_IN addrSock;
addrSock.sin_family = AF_INET;
addrSock.sin_port = htons(6000);
addrSock.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
int ret = bind(m_socket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR));
if (SOCKET_ERROR == ret)
{
closesocket(m_socket);
AfxMessageBox(_T("bind fail"));
return FALSE;
}
if (SOCKET_ERROR == WSAAsyncSelect(m_socket, m_hWnd, UM_SOCK, FD_READ))
{
AfxMessageBox(_T("注册网络读取事件失败!"));
return FALSE;
}
return TRUE;
}
void CChartDlg::OnBnClickedBtnSend()
{
// TODO: 在此添加控件通知处理程序代码
DWORD dwIp;
DWORD dwSend;
CString strSend;
WSABUF wsaBuf;
int len;
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIp);
SOCKADDR_IN addrTo;
addrTo.sin_family = AF_INET;
addrTo.sin_port = htons(6000);
addrTo.sin_addr.S_un.S_addr = htonl(dwIp);
GetDlgItemText(IDC_EDIT_SEND, strSend);
len = strSend.GetLength();
wsaBuf.buf = strSend.GetBuffer(len);
wsaBuf.len = len + 1;
strSend.ReleaseBuffer(len);
SetDlgItemText(IDC_EDIT_SEND, _T(""));
if (SOCKET_ERROR == WSASendTo(m_socket, &wsaBuf, 1, &dwSend, 0,
(SOCKADDR*)&addrTo, sizeof(SOCKADDR), NULL, NULL))
{
AfxMessageBox(_T("发送数据失败!"));
return;
}
}
ChartDlg.h
// ChartDlg.h : 头文件
//
#pragma once
#define UM_SOCK WM_USER + 1
// CChartDlg 对话框
class CChartDlg : public CDialog
{
// 构造
public:
CChartDlg(CWnd* pParent = NULL); // 标准构造函数
~CChartDlg();
// 对话框数据
enum { IDD = IDD_CHART_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg LRESULT OnSock(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
private:
SOCKET m_socket;
private:
BOOL InitSocket();
public:
afx_msg void OnBnClickedBtnSend();
};