重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
Private Sub Form_Load()
创新互联专注于企业全网营销推广、网站重做改版、平果网站定制设计、自适应品牌网站建设、成都h5网站建设、商城网站建设、集团公司官网建设、外贸网站制作、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为平果等各大城市提供网站开发制作服务。
With Winsock1
.RemoteHost = "124.135.13.106" '对方的IP
.RemotePort = 1002 ' 对方的端口号
.Bind 1001 ' 本机的端口号
End With
End Sub
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim strData As String
Winsock1.GetData strData '接收字节
List1.AddItem strData
End Sub
这个用UDP进行连接的。只不过是对单个机器进行的。
在编写VB.Net windows应用程序的时候我们经常会遇到这种问题,怎么样在两个窗体间传递数据呢?例如,用VB.Net做一个文本编辑器,里面有一个搜索功能(即搜索我打开的文本里面的文字),点搜索则弹出搜索对话框,输入要搜索的内容,然后确定,就可以搜索到我打开的文本里面的文字了,这里就用到了两个窗体间的相互通信。我查看了相关的资料想了想,得出一些想法和方法。
也许有的人会觉得这个很简单呀。假如主框架为Form1,打开的搜索对话框是Form2.直接在Form2类中申明一个Form1实例:dim f1 as new Form1然后就可以通过f1来调用Form1中的域和函数了。其实不是这样的,你申明的新的Form1实例不是原来的那个Form1对象了,这样操作的是新的Form1中的域和函数,和最先打开的Form1是没有关系的。
那应该如何来完成两个窗体的通讯呢?我们要做的是把当前的Form1实例传递给Form2,如果是这样的话,问题就很好解决了。
方法1:首先,我们在Form2中定义:
Private mF_Form As Form1
我们更改Form2的构造函数为有参数的
Public Sub New(ByVal form As Form1)
MyBase.New()
'该调用是 Windows 窗体设计器所必需的。
InitializeComponent()
Me.mF_Form = form
'在 InitializeComponent() 调用之后添加任何初始化
End Sub
在Form1中,我在 要用到Form2的地方申明如下:
Dim f1 As New Form2(Me)
这里的me指的就是Form1当前的实例,也就是把当前Form1的实例通过Form2的构造函数传递给Form2类(其实在网上看到过比较蠢的方式,就是在构造函数里面传递要传递的信息如:字符串或是数字等,这样做很有局限性,不能传递其他的,所有我们可以直接传递实例,来完成传递更多的信息。)
这样在Form2中使用myForm 就可以对原来的Form1窗口进行操作了。但是你要把要操作的Form1中的域和函数定义成public形式的(这样可能不安全),此时的myForm就是真正的最开始打开的Form1了,你可以用这个实例来进行两个窗体的通讯了。
方法2:其实VB.Net中提供了窗体间进行通讯的现成的属性,呵呵,我们能想到的,微软也想到了,他们创造的语言其实确实可以说是人性化了。
在Form1类中申明Form2时用如下代码:
Dim f2As New Form2 '类Form2中的构造函数不改,还是无参的
f2.owner=me
也可以使用函数的方法,给当前实例添加一个附属窗口 代码:Me.AddOwnedForm(f2)
在Form2类的定义中写如下代码:
dim f1 as Form1=me.owner
这样f1对应的就是原来的Form1的实例了,也就可以用这个进行通讯了。但是还是要把不同类之间访问的域和函数定义成public,哎,安全确实是一个问题!!
串口API通信函数编程
16位串口应用程序中,使用的16位的Windows API通信函数:
①OpenComm()打开串口资源,并指定输入、输出缓冲区的大小(以字节计)
CloseComm() 关闭串口;
例:int idComDev;
idComDev = OpenComm("COM1", 1024, 128);
CloseComm(idComDev);
②BuildCommDCB() 、setCommState()填写设备控制块DCB,然后对已打开的串口进行参数配置; 例:DCB dcb;
BuildCommDCB("COM1:2400,n,8,1", dcb);
SetCommState(dcb);
③ ReadComm 、WriteComm()对串口进行读写操作,即数据的接收和发送.
例:char *m_pRecieve; int count;
ReadComm(idComDev,m_pRecieve,count);
Char wr[30]; int count2;
WriteComm(idComDev,wr,count2);
16位下的串口通信程序最大的特点就在于:串口等外部设备的操作有自己特有的API函数;而32位程序则把串口操作(以及并口等)和文件操作统一起来了,使用类似的操作。
在MFC下的32位串口应用程序
32位下串口通信程序可以用两种方法实现:利用ActiveX控件;使用API 通信函数。
使用ActiveX控件,程序实现非常简单,结构清晰,缺点是欠灵活;使用API 通信函数的优缺点则基本上相反。
使用ActiveX控件:
VC++ 6.0提供的MSComm控件通过串行端口发送和接收数据,为应用程序提供串行通信功能。使用非常方便,但可惜的是,很少有介绍MSComm控件的资料。
⑴.在当前的Workspace中插入MSComm控件。
Project菜单------Add to Project----Components and Controls-----Registered
ActiveX Controls---选择Components: Microsoft Communications Control,
version 6.0 插入到当前的Workspace中。
结果添加了类CMSComm(及相应文件:mscomm.h和mscomm.cpp )。
⑵.在MainFrm.h中加入MSComm控件。
protected:
CMSComm m_ComPort;
在Mainfrm.cpp::OnCreare()中:
DWORD style=WS_VISIBLE|WS_CHILD;
if (!m_ComPort.Create(NULL,style,CRect(0,0,0,0),this,ID_COMMCTRL)){
TRACE0("Failed to create OLE Communications Control\n");
return -1; // fail to create
}
⑶.初始化串口
m_ComPort.SetCommPort(1); //选择COM?
m_ComPort. SetInBufferSize(1024); //设置输入缓冲区的大小,Bytes
m_ComPort. SetOutBufferSize(512); //设置输入缓冲区的大小,Bytes//
if(!m_ComPort.GetPortOpen()) //打开串口
m_ComPort.SetPortOpen(TRUE);
m_ComPort.SetInputMode(1); //设置输入方式为二进制方式
m_ComPort.SetSettings("9600,n,8,1"); //设置波特率等参数
m_ComPort.SetRThreshold(1); //为1表示有一个字符引发一个事件
m_ComPort.SetInputLen(0);
⑷.捕捉串口事项。MSComm控件可以采用轮询或事件驱动的方法从端口获取数据。我们介绍比较使用的事件驱动方法:有事件(如接收到数据)时通知程序。在程序中需要捕获并处理这些通讯事件。
在MainFrm.h中:
protected:
afx_msg void OnCommMscomm();
DECLARE_EVENTSINK_MAP()
在MainFrm.cpp中:
BEGIN_EVENTSINK_MAP(CMainFrame,CFrameWnd )
ON_EVENT(CMainFrame,ID_COMMCTRL,1,OnCommMscomm,VTS_NONE) //映射ActiveX控件事件
END_EVENTSINK_MAP()
⑸.串口读写. 完成读写的函数的确很简单,GetInput()和SetOutput()就可。两个函数的原型是:
VARIANT GetInput();及 void SetOutput(const VARIANT newValue);都要使用VARIANT类型(所有Idispatch::Invoke的参数和返回值在内部都是作为VARIANT对象处理的)。
无论是在PC机读取上传数据时还是在PC机发送下行命令时,我们都习惯于使用字符串的形式(也可以说是数组形式)。查阅VARIANT文档知道,可以用BSTR表示字符串,但遗憾的是所有的BSTR都是包含宽字符,即使我们没有定义_UNICODE_UNICODE也是这样! WinNT支持宽字符, 而Win95并不支持。为解决上述问题,我们在实际工作中使用CbyteArray,给出相应的部分程序如下:
void CMainFrame::OnCommMscomm(){
VARIANT vResponse; int k;
if(m_commCtrl.GetCommEvent()==2) {
k=m_commCtrl.GetInBufferCount(); //接收到的字符数目
if(k0) {
vResponse=m_commCtrl.GetInput(); //read
SaveData(k,(unsigned char*) vResponse.parray-pvData);
} // 接收到字符,MSComm控件发送事件 }
。。。。。 // 处理其他MSComm控件
}
void CMainFrame::OnCommSend() {
。。。。。。。。 // 准备需要发送的命令,放在TxData[]中
CByteArray array;
array.RemoveAll();
array.SetSize(Count);
for(i=0;iCount;i++)
array.SetAt(i, TxData[i]);
m_ComPort.SetOutput(COleVariant(array)); // 发送数据 }
二 使用32位的API 通信函数:
⑴.在中MainFrm.cpp定义全局变量
HANDLE hCom; // 准备打开的串口的句柄
HANDLE hCommWatchThread ;//辅助线程的全局函数
⑵.打开串口,设置串口
hCom =CreateFile( "COM2", GENERIC_READ | GENERIC_WRITE, // 允许读写
0, // 此项必须为0
NULL, // no security attrs
OPEN_EXISTING, //设置产生方式
FILE_FLAG_OVERLAPPED, // 我们准备使用异步通信
NULL );
我使用了FILE_FLAG_OVERLAPPED结构。这正是使用API实现非阻塞通信的关键所在。
ASSERT(hCom!=INVALID_HANDLE_VALUE); //检测打开串口操作是否成功
SetCommMask(hCom, EV_RXCHAR|EV_TXEMPTY );//设置事件驱动的类型
SetupComm( hCom, 1024,512) ; //设置输入、输出缓冲区的大小
PurgeComm( hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
| PURGE_RXCLEAR ); //清干净输入、输出缓冲区
COMMTIMEOUTS CommTimeOuts ; //定义超时结构,并填写该结构
…………
SetCommTimeouts( hCom, CommTimeOuts ) ;//设置读写操作所允许的超时
DCB dcb ; // 定义数据控制块结构
GetCommState(hCom, dcb ) ; //读串口原来的参数设置
dcb.BaudRate =9600; dcb.ByteSize =8; dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT ;dcb.fBinary = TRUE ;dcb.fParity = FALSE;
SetCommState(hCom, dcb ) ; //串口参数配置
上述的COMMTIMEOUTS结构和DCB都很重要,实际工作中需要仔细选择参数。
⑶启动一个辅助线程,用于串口事件的处理。
Windows提供了两种线程,辅助线程和用户界面线程。辅助线程没有窗口,所以它没有自己的消息循环。但是辅助线程很容易编程,通常也很有用。
在次,我们使用辅助线程。主要用它来监视串口状态,看有无数据到达、通信有无错误;而主线程则可专心进行数据处理、提供友好的用户界面等重要的工作。
hCommWatchThread=
CreateThread( (LPSECURITY_ATTRIBUTES) NULL, //安全属性
0,//初始化线程栈的大小,缺省为与主线程大小相同
(LPTHREAD_START_ROUTINE)CommWatchProc, //线程的全局函数
GetSafeHwnd(), //此处传入了主框架的句柄
0, dwThreadID );
ASSERT(hCommWatchThread!=NULL);
⑷为辅助线程写一个全局函数,主要完成数据接收的工作。请注意OVERLAPPED结构的使用,以及怎样实现了非阻塞通信。
UINT CommWatchProc(HWND hSendWnd){
DWORD dwEvtMask=0 ;
SetCommMask( hCom, EV_RXCHAR|EV_TXEMPTY );//有哪些串口事件需要监视?
WaitCommEvent( hCom, dwEvtMask, os );// 等待串口通信事件的发生
检测返回的dwEvtMask,知道发生了什么串口事件:
if ((dwEvtMask EV_RXCHAR) == EV_RXCHAR){ // 缓冲区中有数据到达
COMSTAT ComStat ; DWORD dwLength;
ClearCommError(hCom, dwErrorFlags, ComStat ) ;
dwLength = ComStat.cbInQue ; //输入缓冲区有多少数据?
if (dwLength 0) { BOOL fReadStat ;
fReadStat = ReadFile( hCom, lpBuffer,dwLength, dwBytesRead,READ_OS( npTTYInfo ) ); //读数据
注:我们在CreareFile()时使用了FILE_FLAG_OVERLAPPED,现在ReadFile()也必须使用
LPOVERLAPPED结构.否则,函数会不正确地报告读操作已完成了.
使用LPOVERLAPPED结构, ReadFile()立即返回,不必等待读操作完成,实现非阻塞
通信.此时, ReadFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
if (!fReadStat){
if (GetLastError() == ERROR_IO_PENDING){
while(!GetOverlappedResult(hCom,READ_OS( npTTYInfo ), dwBytesRead, TRUE )){
dwError = GetLastError();
if(dwError == ERROR_IO_INCOMPLETE) continue;//缓冲区数据没有读完,继续
…… ……
::PostMessage((HWND)hSendWnd,WM_NOTIFYPROCESS,0,0);//通知主线程,串口收到数据}
所谓的非阻塞通信,也即异步通信。是指在进行需要花费大量时间的数据读写操作(不仅仅是指串行通信操作)时,一旦调用ReadFile()、WriteFile(), 就能立即返回,而让实际的读写操作在后台运行;相反,如使用阻塞通信,则必须在读或写操作全部完成后才能返回。由于操作可能需要任意长的时间才能完成,于是问题就出现了。
非常阻塞操作还允许读、写操作能同时进行(即重叠操作?),在实际工作中非常有用。
要使用非阻塞通信,首先在CreateFile()时必须使用FILE_FLAG_OVERLAPPED;然后在 ReadFile()时lpOverlapped参数一定不能为NULL,接着检查函数调用的返回值,调用GetLastError(),看是否返回ERROR_IO_PENDING。如是,最后调用GetOverlappedResult()返回重叠操作(overlapped operation)的结果;WriteFile()的使用类似。
⑸.在主线程中发送下行命令。
BOOL fWriteStat ; char szBuffer[count];
…………//准备好发送的数据,放在szBuffer[]中
fWriteStat = WriteFile(hCom, szBuffer, dwBytesToWrite,
dwBytesWritten, WRITE_OS( npTTYInfo ) ); //写数据
//我在CreareFile()时使用了FILE_FLAG_OVERLAPPED,现在WriteFile()也必须使用LPOVERLAPPED结构.否则,函数会不正确地报告写操作已完成了.
使用LPOVERLAPPED结构,WriteFile()立即返回,不必等待写操作完成,实现非阻塞 通信.此时, WriteFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
int err=GetLastError();
if (!fWriteStat) {
if(GetLastError() == ERROR_IO_PENDING){
while(!GetOverlappedResult(hCom, WRITE_OS( npTTYInfo ),
dwBytesWritten, TRUE )) {
dwError = GetLastError();
if(dwError == ERROR_IO_INCOMPLETE){// normal result if not finished
dwBytesSent += dwBytesWritten; continue; }
......................
//我使用了多线程技术,在辅助线程中监视串口,有数据到达时依靠事件驱动,读入数据并向主线程报告(发送数据在主线程中,相对说来,下行命令的数据总是少得多);并且,WaitCommEvent()、ReadFile()、WriteFile()都使用了非阻塞通信技术,依靠重叠(overlapped)读写操作,让串口读写操作在后台运行。
'Private Sub DataGrid1_Click()
' If DataGrid1.Row 0 Then
' dwbhTXT.Text = DataGrid1.Columns(0).Value
' Text1.Text = DataGrid1.Columns(1).Value
' Adodc2.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" App.Path "\ktcms.mdb;Jet OLEDB:Database Password=701109"
' Adodc2.RecordSource = "select * from consumer where 用户编号=" dwbhTXT.Text
' Adodc2.Refresh
' If Adodc2.Recordset.RecordCount 0 Then
' Combo1.Text = Adodc2.Recordset.Fields("省份").Value
' End If
' End If
'End Sub
用 vb.net socket通信
Dim th As Threading.Thread
2 Dim tcpl As System.Net.Sockets.TcpListener
3
4 Private Sub Form1_Load()Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
5 th = New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf MyListen))
6 th.Start()
7 End Sub
8
9 Public Sub SendMessage()Sub SendMessage(ByVal IP As String, ByVal SendMsg As String)
10 Try
11 If IP "" Then
12 Dim tcpc As New System.Net.Sockets.TcpClient(IP, 5656)
13 Dim tcpStream As Net.Sockets.NetworkStream = tcpc.GetStream
14 Dim reqStream As New IO.StreamWriter(tcpStream)
15 reqStream.Write(SendMsg)
16 reqStream.Flush()
17 tcpStream.Close()
18 tcpc.Close()
19 End If
20 Catch ex As Exception
21 MsgBox(ex.Message.ToString)
22 End Try
23 End Sub
24 Private Sub MyListen()Sub MyListen()
25 Try
26 Dim ipAddress As System.Net.IPAddress = System.Net.Dns.Resolve(System.Net.Dns.GetHostName).AddressList(0)
27 tcpl = New System.Net.Sockets.TcpListener(ipAddress, 5656)
28 tcpl.Start()
29 While True
30 Dim s As System.Net.Sockets.Socket = tcpl.AcceptSocket()
31 Dim MyBuffer(1024) As Byte
32 Dim i As Integer
33 i = s.Receive(MyBuffer)
34 If i 0 Then
35 Dim lstrRec As String
36 Dim j As Integer
37 For j = 0 To i - 1
38 TextBox1.Text += Chr(MyBuffer(j)) ","
39 Next
40 End If
41 End While
42 Catch ex As Exception
43 MsgBox(ex.Message.ToString)
44 End Try
45 End Sub
46
47 Private Sub Button1_Click()Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
48 SendMessage("192.168.0.61", TextBox2.Text)
49 End Sub