举例说明一下. 下面是一个Go语言包, 包含了两个函数 -- Random 和 Seed -- 是C语言库中random和srandom函数的马甲.
package rand
/*
#include <stdlib.h>
*/ import "C" func Random() int {return int(C.random()) } func Seed(i int) {C.srandom(C.uint(i)) }
我们来看一下这里都有什么内容. 开始是一个包的导入语句.
rand包导入了"C"包, 但你会发现在Go的标准库里没有这个包. 那是因为C是一个"伪包", 一个为cgo引入的特殊的包名, 它是C命名空间的一个引用.
rand 包包含4个到C包的引用: 调用 C.random和C.srandom, 类型转换 C.uint(i)还有引用语句.
Random函数调用libc中的random函数, 然后回返结果. 在C中, random返回一个C类型的长整形值, cgo把它轮换为C.long. 这个值必需转换成Go的类型, 才能在Go程序中使用. 使用一个常见的Go类型转换:
func Random() int {return int(C.random()) }
这是一个等价的函数, 使用了一个临时变量来进行类型转换:
func Random() int {var r C.long = C.random() return int(r) }
Seed函数则相反. 它接受一个Go语言的int类型, 转换成C语言的unsigned int类型, 然后传递给C的srandom函数.
func Seed(i int) {C.srandom(C.uint(i)) }
需要注意的是, cgo中的unsigned int类型写为C.uintcgo的文档中有完整的类型列表.
这个例子中还有一个细节我们没有说到, 那就是导入语句上面的注释.
/*
#include <stdlib.h>
*/ import "C"
Cgo可以识别这个注释, 并在编译C语言程序的时候将它当作一个头文件来处理. 在这个例子中, 它只是一个include语句, 然而其实它可以是使用有效的C语言代码. 这个注释必需紧靠在import "C"这个语句的上面, 不能有空行, 就像是文档注释一样.
Strings and things
与Go语言不同, C语言中没有显式的字符串类型. 字符串在C语言中是一个以0结尾的字符数组.
Go和C语言中的字符串转换是通过C.CString, C.GoString,和C.GoStringN这些函数进行的. 这些转换将得到字符串类型的一个副本.
下一个例子是实现一个Print函数, 它使用C标准库中的fputs函数把一个字符串写到标准输出上:
package print // #include <stdio.h>// #include <stdlib.h>import "C" import "unsafe" func Print(s string) {cs := C.CString(s) C.fputs(cs, (*C.FILE)(C.stdout)) C.free(unsafe.Pointer(cs)) }
在C程序中进行的内存分配是不能被Go语言的内存管理器感知的. 当你使用C.CString创建一个C字符串时(或者其它类型的C语言内存分配), 你必需记得在使用完后用C.free来释放它.
调用C.CString将返回一个指向字符数组开始处的指错, 所以在函数退出前我们把它转换成一个unsafe.Pointer(Go中与C的void 等价的东西), 使用C.free来释放分配的内存. 一个惯用法是在分配内存后紧跟一个defer(特别是当这段代码比较复杂的时候), 这样我们就有了下面这个Print函数:
func Print(s string) {cs := C.CString(s) defer C.free(unsafe.Pointer(cs)) C.fputs(cs, (*C.FILE)(C.stdout)) }
构建 cgo 包
如果你使用goinstall, 构建cgo包就比较容易了, 只要调用像平常一样使用goinstall命令, 它就能自动识别这个特殊的import "C", 然后自动使用cgo来编译这些文件.
如果你想使用Go的Makefiles来构建, 那在CGOFILES变量中列出那些要用cgo处理的文件, 就像GOFILES变量包含一般的Go源文件一样.
rand包的Makefile可以写成下面这样:
include $(GOROOT)/src/Make.inc
TARG=goblog/rand
CGOFILES=\ rand.go\ include $(GOROOT)/src/Make.pkg
然后输入gomake开始构建.
更多 cgo 的资源
cgo的文档中包含了关于C伪包的更多详细的说明, 以及构建过程. Go代码树中的cgo的例子给出了更多更高级的用法.
一个简单而又符合Go惯用法的基于cgo的包是Russ Cox写的gosqlite. 而Go语言的网站上也列出了更多的的cgo包.
最后, 如果你对于cgo的内部是怎么运作这个事情感到好奇的话, 去看看运行时包的cgocall.c文件的注释吧.
不知道你的winsock 是udp还是tcpip协议
这里给你一个我写的实例 我是用的udp 这个发送了一段数据不一定接收就正确 所以我做了校验
'以下是发送文件的
Option Explicit
Dim GetFileNum As Integer, LenFile As Long, SendByte() As Byte '发送的包
Private Sub Command1_Click()
On Error Resume Next
Command1.Enabled = False
GetFileNum = FreeFile '取得未使用的文件号
LenFile = FileLen(Text1.Text) '获得需传送的文件的长度
If Text2.Text = "" Or Right(Left(Text2.Text, 2), 1) <> ":" Then Text2.Text = Text1.Text
Winsock0.SendData "#SEND STA#" & LenFile & "//" & Text2.Text
Wt 0.5
Open Text1.Text For Binary As #GetFileNum '打开需传送的文件
Call TCPSendFile(Winsock0, GetFileNum, SplitFile) '传送文件
Me.Caption = Now
Ti.Enabled = True
End Sub
Private Sub Command2_Click()
Unload Me
End Sub
Private Sub Form_Load()
Dim A As String
On Error Resume Next
Command2.Top = -2000
Winsock0.RemoteHost = IPX '服务器ip
Winsock0.RemotePort = FilePort
End Sub
Private Function SplitFile() As Long '拆包'为了清晰,下面分别用两个子过程来完成计算这次还可以传多少个字节的数据和传送数据
On Error Resume Next
Dim GetCount As Long
If LenFile >= 4000 Then '计算出这次可发送的字节数
GetCount = 4000
LenFile = LenFile - GetCount
Else
GetCount = LenFile
LenFile = LenFile - GetCount
End If
SplitFile = GetCount
End Function
Private Sub TCPSendFile(objWinSock As Winsock, FileNumber As Integer, SendLen As Long)
On Error Resume Next
Dim FileByte() As Byte, I As Long, j As Long, Temp As String * 4
ReDim SendByte(0)
ReDim FileByte(SendLen - 1)
Temp = SendLen + 7
SendByte = Temp '把长度负值给包头
Get #FileNumber, , FileByte '读取文件
ReDim Preserve SendByte(SendLen + 7) '把包头+到文件头
For I = 0 To UBound(FileByte)
SendByte(I + 7) = FileByte(I)
'DoEvents
Next
Winsock0.SendData SendByte
End Sub
Private Sub Form_Unload(Cancel As Integer)
On Error Resume Next
Winsock0.Close
Err.Clear
End Sub
Private Sub TEnd_Timer()
On Error Resume Next
Winsock0.SendData "#END#"
Err.Clear
End Sub
Private Sub Ti_Timer()
On Error Resume Next
Winsock0.SendData "#ERR#"
End Sub
Private Sub Winsock0_DataArrival(ByVal bytesTotal As Long)
On Error Resume Next
Dim S As String
Winsock0.GetData S
Select Case S
Case "ok" '成功继续发送
If LenFile = 0 Then '发送完成
If S <> "#SEND END#" Then Winsock0.SendData "#SEND END#"
Me.Caption = "文件上传成功!"
Command1.Enabled = True
Ti.Enabled = False
TEnd.Enabled = True
Exit Sub
Else
Me.Caption = "文件上传完成:[" & Left((FileLen(Text1.Text) - LenFile) / FileLen(Text1.Text) * 100, 4) & "%]"
End If
Call TCPSendFile(Winsock0, GetFileNum, SplitFile)
Case "#END#"
TEnd.Enabled = False
FMain.TiF.Enabled = True
Case "no" '不成功重发上一个包
Winsock0.SendData SendByte
End Select
End Sub
'以下是接收文件的
Option Explicit
Dim FOK As Boolean, Fs As Long, FileNumber As Integer, LenFile As Long '文件的长度
Private Sub Command1_Click()
Unload Me
End Sub
Private Sub Command2_Click()
On Error Resume Next
Dim A As String
Me.Caption = "开始下载"
If Dir(Text2.Text) <> "" And ChV.Value = 0 Then
If MsgBox("文件已经存在,覆盖吗?", vbCritical + vbYesNo) = vbYes Then Kill Text2.Text Else Exit Sub
Else
Kill Text2.Text
End If
If Text2.Text = "" Then Text2.Text = Text1.Text
'Command2.Enabled = False
If Ch.Value = 0 Then A = "#DOW#" Else A = "#DOV#"
FMain.Wsk.SendData A & Text1.Text
Wt 1
FMain.Wsk.SendData "#DOE#" & Text2.Text
End Sub
Private Sub Form_Load()
Dim A As String
'FMain.Ts.Enabled = True
On Error Resume Next
If FMain.Cb.Text = "本地组" Or FMain.Cb.Text = "全部组" Then
Me.Caption = "请重选下载用户[“组”不能正确下载]"
Else
Command1.Top = -2000
Winsock0.LocalPort = FilePort
Winsock0.Bind
End If
End Sub
Private Sub Form_Unload(Cancel As Integer)
On Error Resume Next
Winsock0.Close
Err.Clear
End Sub
Private Sub La_Click()
CDL.FileName = ""
CDL.ShowOpen
Text2.Text = CDL.FileName
End Sub
Private Sub Winsock0_DataArrival(ByVal bytesTotal As Long)
On Error Resume Next
Dim FileByte() As Byte, A As String, MendByte() As Byte, I As Long, J As Long, Temp As String, W As String
Winsock0.GetData FileByte, vbArray + vbByte '接收类型为:字节数组
J = UBound(FileByte) '获得包长
For I = 0 To 7 Step 2 '合并包头
Temp = Temp & Chr(FileByte(I))
Next
For I = 0 To 9 '文件发送结束标记
A = A & Chr(FileByte(I))
Next
If A = "#ERR#" Then Winsock0.SendData "no"
If A = "#END#" Then
For I = 0 To Len(FMain.TIn.Text)
If I < 100 Then
W = Left(Right(FMain.TIn.Text, I), 1)
If W = "!" Then
W = Left(Right(FMain.TIn.Text, I + 4), 5)
Exit For
End If
Else
Exit For
End If
Next I
If W <> "下载完成!" Then
FOK = False
Me.Caption = "下载完成![" & Text2.Text & "]"
FMain.TIn.Text = FMain.TIn.Text & "[" & Now & "]" & "下载完成!(" & Fs & "<=" & LenFile & ")" & vbCrLf
Command2.Enabled = True
End If
Else
If Val(Temp) = J Then '比较长度看丢包没有
ReDim MendByte(J - 8)
For I = 0 To J - 8 '提出包头
MendByte(I) = FileByte(I + 7)
Next
Fs = Fs + UBound(FileByte) - 7
Put #FileNumber, , MendByte '写文件
Winsock0.SendData "ok" '发送继续发送的请求
Me.Caption = "文件下载完成:[" & Left(Fs / LenFile * 100, 4) & "%]"
Else
If Left(A, 10) = "#SEND STA#" Then
A = ""
For I = 10 To UBound(FileByte) '文件发送结束标记
A = A & Chr(FileByte(I))
Next
LenFile = Val(Left(A, InStr(A, "//") - 1))
For I = 0 To Len(Text2.Text)
A = Left(Right(Text2.Text, I), 1)
If A = "\" Then Exit For
Next
If Dir(Left(Text2.Text, Len(Text2.Text) - I + 1), vbDirectory) = "" Then MkDir Left(Text2.Text, Len(Text2.Text) - I + 1)
'"#SEND STA#" & FileLen(Text1.Text) & "//" & Text2.Text
FileNumber = FreeFile '取得未使用的文件号
Fs = 0
Open Text2.Text For Binary As #FileNumber '打开文件
Else
If A <> "#SEND END#" Then
Winsock0.SendData "no" '出现丢包,请求重发
Else
Winsock0.SendData "#END#" '发送继续发送的请求
Close #FileNumber
Reset
If FOK = False Then
FOK = True
Me.Caption = "下载完成![" & Text2.Text & "]"
FMain.TIn.Text = FMain.TIn.Text & "[" & Now & "]" & "下载完成!(" & Fs & "<=" & LenFile & ")" & vbCrLf
Command2.Enabled = True
Else
FOK = False
End If
End If
End If
End If
End If
End Sub