利用Winsock控件可以编制一个简单的通讯程序,用于局域网之间的微机通讯。
1 Winsock控件的使用方法
1.1通讯协议的选择
Winsock控件提供了访问 TCP 和 UDP 网络服务的方便途径。采用TCP协议可以提供双向的、可靠的、有序的和无重复投递的数据流。TCP允许创建和维护与远程计算机的连接,连接两台计算机就可彼此进行数据传输。用户数据文报协议 (UDP) 是一个无连接协议。采用UDP协议,支持双向的数据流,但不保证有序、可靠或无重复的投递。但计算机并不建立连接。
在使用中,采用TCP协议的编程模型应用最为广泛,因为面向连接协议提供了一系列的数据纠错功能,可以保证在网络上传输的数据及时、无误地到达对方,Internet上的多数服务(WWW、FTP、Telnet、SMTP)就是靠此协议运转的。采用UDP协议由于不用建立连接,传输比连接协议快,无数据纠错功能,不保证数据的可靠传递。在这里本文只介绍使用TCP/IP协议进行编程的方法。
1.2使用方法和主要函数
Winsock控件的使用方法和步骤如下图所示。
1.2.1 VARIANT类型变量的使用
Winsock控件使用的一个特殊的数据类型为VARIANT结构,其中vt的值类型说明了它所存储的数据,vt的几个主要的值类型及值域为:
类型 值域 说明
vt=VT_BSTR bstrVal 字符串类型
vt=VT_I2 Ival 整数类型
vt=VT_ERROR scode 错误代码
在使用之前,应对VARIANT变量的类型和值域进行赋值。一般的使用方法为:
VARIANT [variant name]
[variant name].vt=[variant type]
[variant name].[variant type]=[value]
例如:
Cstring m_host;
m_host="Room312";
VARIANT vtHost;
vtHost.vt=VT_BSTR;
vtHost.bstrVal=m_host.AllocSysString();
1. 2.2 Server程序使用的主要函数
Bind()函数指定用于 TCP 连接的 LocalPort 和 LocalIP。
Listen()函数设置侦听模式。该方法仅适用于 TCP 连接。
Accept()在处理 ConnectionRequest 事件时用这个方法接受新连接。
SendData()用于数据的发送。
Close()关闭与对方的连接。
1.2.3Client程序使用的主要函数
Connect()要求连接到远程计算机。
GetData()得到远程计算机上的数据。
2 使用Winsock控件的局域网通讯程序
2.1 Server程序
利用ClassWizard建立一个ServerDemo程序,注意选择SDI,支持ActiveX,将View类设为CFormView类, 从Project->Add File->Compont and Control, 在对话框中选Registered ActiveX,选择Microsoft WinSock Control ,点击Insert按钮,依照缺省设置即可。这样在工程文件中会出现mswinsockcontrol类编辑ServerDemo程序的主对话框,可以看到在控件面版上出现mswinsockcontrol类的控件。在对话框中加入控件和相应的变量,如表1所示
控件
|
控件ID
|
变量名
|
类型
|
Edit
|
IDC_EDIT1
|
m_edit
|
CString
|
按钮
|
IDC_SEND
|
|
|
Winsock
|
IDC_WINSOCK1
|
m_server
|
控制
|
Winsock
|
IDC_WINSOCK2
|
m_connect
|
控制
|
(1)在ServerDemoView.h中,加入以下变量
VARIANT localport;
VARIANT localip;
CString strport;
CString strip;
VARIANT vtCommand;
CString m_command;
VARIANT vtData;
VARIANT vtType;
VARIANT vtMaxlen;
(2)在ServerDemoView .cpp的构造函数中加入:
localport.vt=VT_BSTR;
localip.vt=VT_BSTR;
strport="4000";
strip="128.1.0.139";//在程序中应将“128.1.25.240”设为自己server
//的IP地址
vtCommand.vt=VT_BSTR;
m_command="Server Send";
vtData.vt=VT_BSTR;
vtType.vt=VT_ERROR;
(3)利用ClassWizar映射OnInitialUpdate()的响应:在其中加入代码:
localport.bstrVal=strport.AllocSysString();
localip.bstrVal=strip.AllocSysString();
m_server.Bind(localport, localip);
m_server.Listen();
(4)利用ClassWizar响应IDC_WINSOCK1的ConnectionRequest事件,在OnConnectionRequestWinsock1()中加入代码:
if(m_connect.GetState()==0 )
m_connect.Accept(requestID);
else
AfxMessageBox("no more connects",MB_OK);
(5)利用ClassWizar映射ID_SEND的动作:OnSend(),在其中加入代码:
m_edit.GetWindowText(m_command);
vtCommand.bstrVal=m_command.AllocSysString();
m_connect.SendData(vtCommand);
(6)利用ClassWizar响应ID_WINSOCK2的DataArrival事件,在OnDataArrivalWinsock2()中加入代码:
CString m_MBody;
vtMaxlen.vt=VT_I2;
vtMaxlen.iVal=bytesTotal;
vtData.bstrVal=strport.AllocSysString();
vtType.bstrVal=strport.AllocSysString();
m_connect.GetData(&vtData,vtType,vtMaxlen);
m_MBody=vtData.bstrVal;
m_edit.SetWindowText( m_MBody);
(7)利用ClassWizar响应ID_WINSOCK2的Close()事件,在OnCloseWinsock2()中加入代码:
m_connect.Close();
(8)在DestroyWindow() 中加入:
m_server.Close();
m_connect.Close();
2.2 Client程序
利用ClassWizard建立一个ClientDemo程序,注意选择SDI,支持ActiveX,将View类设为CFormView类, 从Project->Add File->Compont and Control, 在对话框中选Registered ActiveX,选择Microsoft WinSock Control ,点击Insert按钮,依照缺省设置即可。这样在工程文件中会出现mswinsockcontrol类。编辑ClientDemo程序的主对话框,可以看到在控件面版上出现mswinsockcontrol类的控件。在对话框中加入控件和相应的变量,如表2所示
表2控件表
控件
|
控件ID
|
变量名
|
类型
|
Edit
|
IDC_EDIT1
|
m_edit
|
CString
|
按钮
|
IDC_CONNECT
|
m_connect
|
控制
|
按钮
|
IDC_SEND
|
|
|
Winsock
|
IDC_WINSOCK1
|
m_socket
|
控制
|
(1)在ClientDemoView.h中,加入以下变量
CString m_host;
CString m_port;
VARIANT vtHost;
VARIANT vtPort;
VARIANT vtData;
VARIANT vtType;
VARIANT vtMaxlen;
VARIANT vtCommand;
CString m_command;
(2)在ClientDemo.cpp的构造函数中加入:
m_host=_T("Room312");//在程序中将“Room312”设为自己server的主机
//名
m_port=_T("4000"); //端口设为4000;
m_command=_T("Client send");
vtHost.vt=VT_BSTR;
vtPort.vt=VT_BSTR;
vtData.vt=VT_BSTR;
vtType.vt=VT_ERROR;
vtCommand.vt=VT_BSTR;
(3)利用ClassWizar映射ID_CONNECT的动作:在OnConnect()中加入代码:
m_socket.SetRemoteHost("Room312");//在程序中将“Room312”设为自己
//server的主机名
m_socket.SetRemotePort(4000);
vtHost.bstrVal=m_host.AllocSysString();//见CString类字符串转化为
//系统字符串
vtPort.bstrVal=m_port.AllocSysString();
m_socket.Connect(vtHost,vtPort);
if(m_socket.GetState()==0) //如果m_socket已经断开连接,将connect
//设置为可用
m_connect.EnableWindow(true);
else
m_connect.EnableWindow(false);
(4)利用ClassWizar映射ID_SEND的动作:在OnSend:
m_edit.GetWindowText(m_command);
vtCommand.bstrVal=m_command.AllocSysString();
m_socket.SendData(vtCommand);
(5)利用ClassWizar响应ID_WINSOCK1的DataArrival事件,在 OnDataArrivalWinsock1(long bytesTotal)中加入代码:
CString m_MBody;
vtMaxlen.vt=VT_I2;
vtMaxlen.iVal=bytesTotal;
vtData.bstrVal=m_host.AllocSysString();
vtType.bstrVal=m_port.AllocSysString();
m_socket.GetData(&vtData,vtType,vtMaxlen);
m_MBody=vtData.bstrVal;
m_edit.SetWindowText(m_MBody);
(6)在DestroyWindow()中加入:
m_socket.Close();
(7)利用ClassWizar响应ID_WINSOCK1的Close事件,在OnCloseWinsock1()中加入代码:
m_socket.Close();
if(m_socket.GetState()==0)
m_connect.EnableWindow(true);
else
m_connect.EnableWindow(false);
3.程序演示及结论
将上述程序分别编辑、连接为执行程序,生成serverdemo.exe和clientdemo.exe文件。确认程序中主机名和IP地址无误。如果计算机没有连网,可以在程序中使用本机的主机名和IP地址。首先运行serverdemo.exe。它自动进入到监听状态。随后运行clientdemo.exe,点击connect按钮。如果client与server 连接成功,connect按钮变为灰色(不可用)。连接成功后,在编辑框中输入文字,点击send按钮,在serverdemo程序的编辑框中可以看到clientdemo程序发送的文字。同样,在serverdemo的编辑框中输入文字,点击send按钮,在clientdemo程序的编辑框中可以看到serverdemo程序发送的文字。
通过上述程序可以看到,同MFC的Csocket类相比,Winsock控件的使用简单方便。程序中并未涉及Winsock最基本的API函数以及TCP/IP的相关知识,编程简单明了,是在VC++程序中进行网络通讯的一种简便方法。