;在上一教程里,我们学习了简单的处理服务器返回的数据
;在这一教程里,我们了解一下,当连接上网站后,应该发送什么数据过去的问题
;这里有个简单的方式学习,以下是一个示例
;我们上网的时候可以用谷歌浏览器,打开一个网站,然后右键单击,选择检查这项
;然后再刷新这个网页。
;在右边,点击Network,找到下面的Name,找到其网址,再找到右边的Headers这项,在右边就会显示出其相关信息
;里面包含了访问网页的网址、IP、端口、发送的信息,返回信息的数据头等
;我们在下图点上红色的地方,就是这些内容
;通过研究一下,我们就可以学到更多,本教程的代码尝试,把图里的Request Headers内容抄进发送的内容里,看看效果
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
include kernel32.inc
include wsock32.inc ;需要添加的头文件,涉及socket
includelib user32.lib
includelib kernel32.lib
includelib wsock32.lib
; 自定义函数声明
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD ;对话框窗口函数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
MAINDIALOG equ 1
ICO_MAIN equ 1000 ;图标
ID_BUTTON01 equ 41
ID_BUTTON02 equ 42
ID_BUTTON03 equ 43
ID_EDIT01 equ 11
ID_EDIT02 equ 12
TCP_PORT equ 80 ;端口
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
szErrIP db "无效的服务器IP地址!",0
szErrConnect db "无法连接到服务器!",0
szSucConnect db "可以连接到服务器!",0
szIP db "103.113.93.101",0
szEnter db 13,10,0
szHello db "GET http://www.kepai2023.cn/ HTTP/1.1", 13, 10 ;GET是单纯请求内容,就是不带密码登录类的,13是回车,10是换行
db "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",13,10 ;接受的返回信息的格式
;db "Accept-Encoding: gzip, deflate",13,10 ;网页内容经gzip压缩后再发送过来,我们只是接收信息,不是浏览器,解压不了,所以不用它
db "Accept-Language: zh-CN,zh;q=0.9",13,10 ;支持的语言分别是简体中文和繁体中文,优先支持简体中文
db "Cache-Control: max-age=0",13,10 ;客户端请求的资源被认为立即过时
db "Connection: keep-alive",13,10 ;保持连接状态,别让服务器断了
db "HOST:www.kepai2023.cn", 13, 10 ;服务器主机
;db "If-Modified-Since: Thu, 13 Mar 2025 08:28:36 GMT",13,10 ;这句的意思是让服务器检查网页是否更新,如果更新就发内容过来,如果没有就不发
;db "Upgrade-Insecure-Requests: 1",13,10 ;设置为1时,这个请求头会向服务器发出信号,表明客户端可以处理HTTPS加密和认证的响应,并且愿意接收升级后的安全请求
db "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",13,10 ;这是约定成俗的格式,假装自已是各种浏览器
db 13, 10, 0
.data?
hInstance HINSTANCE ?
hMainhwnd HWND ?
hEdithwnd01 HWND ?
hEdithwnd02 HWND ?
hW_IP HWND ? ;IP地址控件的句柄
nGetIP dd ? ;存放从IP地址控件取得的值的指针
hSocket dd ?
dwLastTime dd ?
hbytesRead dd ? ;最终接收到的总字节数
.const
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke DialogBoxParam, hInstance, MAINDIALOG,NULL, addr DlgProc, NULL
invoke ExitProcess,eax
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;处理接收到的字符串,把链接内容提取出来
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_HandleData proc _lpData,_dwSize
LOCAL hSign ;用来当记号,当发现有链接开始,即<a 字符时定为1,当a>字符时定为2,没有情况时为0
LOCAL szBuffer[256]:byte
LOCAL szSaveBuffer[1024]:byte
LOCAL @stCR:CHARRANGE
mov esi,_lpData
mov hSign,0
invoke RtlZeroMemory,addr szBuffer,sizeof szBuffer
invoke RtlZeroMemory,addr szSaveBuffer,sizeof szSaveBuffer
invoke SendMessage,hEdithwnd02,WM_SETTEXT,0,0 ;清空编辑框
mov ebx,0
mov edx,0
.while TRUE
.break .if ebx == _dwSize
.if byte ptr [esi+ebx] == "<" && byte ptr [esi+ebx+1] == "a"
mov hSign,1
.endif
.if byte ptr [esi+ebx] == ">" && byte ptr [esi+ebx-1] == "a"
mov hSign,2
.endif
.if hSign == 1
mov al,byte ptr [esi+ebx]
mov byte ptr [szBuffer+edx],al
inc edx
.endif
.if hSign == 2
invoke GetWindowTextLength,hEdithwnd02
mov @stCR.cpMin,eax
mov @stCR.cpMax,eax
invoke MultiByteToWideChar,65001,0,addr szBuffer,256,addr szSaveBuffer,1024
invoke SendMessageW,hEdithwnd02,EM_EXSETSEL,0,addr @stCR
invoke SendMessageW,hEdithwnd02,EM_REPLACESEL,FALSE,addr szSaveBuffer
invoke SendMessage,hEdithwnd02,EM_REPLACESEL,FALSE,addr szEnter ;加上回车换行符
mov hSign,0
mov edx,0
.endif
inc ebx
.endw
ret
_HandleData endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 在规定的时间内等待数据到达
; 输入:dwTime = 需要等待的时间(微秒)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WaitData proc _hSocket,_dwTime
local @stFdSet:fd_set,@stTimeval:timeval
mov @stFdSet.fd_count,1 ;1是只监控1个句柄,比如多个客户端连接服务器时,则意味着可以监控更多的连接句柄
push _hSocket
pop @stFdSet.fd_array ;把要监控的句柄给fd_set结构
push _dwTime
pop @stTimeval.tv_usec ;把等待的时间给timeval结构,tv_usec成员是微秒单位
mov @stTimeval.tv_sec,0 ;tv_sec是秒单位,置0
invoke select,0,addr @stFdSet,NULL,NULL,addr @stTimeval ;select函数是告诉系统内核,把这个暗桩插入_hSocket,实现等待的时间
ret
_WaitData endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WorkThread proc _lParam
LOCAL @stSin:sockaddr_in
LOCAL @szBuffer[1500]:byte ;网络传输的最大单元,1500字节,也就是客户端发过来的数据,一次最大就是1500字节,这是协议规定,
LOCAL @Rec_szBuffer[4500]:byte
LOCAL @stCR:CHARRANGE
invoke RtlZeroMemory,addr @stSin,sizeof @stSin
invoke inet_addr,addr szIP ;将字符串类型的IP地址进行转换,转换成网络字节序
mov @stSin.sin_addr,eax
mov @stSin.sin_family,AF_INET
invoke htons,TCP_PORT
mov @stSin.sin_port,ax
invoke socket,AF_INET,SOCK_STREAM,0
mov hSocket,eax
; 连接到服务器
invoke connect,hSocket,addr @stSin,sizeof @stSin
.if eax == SOCKET_ERROR
invoke MessageBox,NULL,addr szErrConnect,NULL,MB_OK or MB_ICONSTOP
.endif
invoke GetTickCount ;得到自电脑启动以来的毫秒数
mov dwLastTime,eax ;保存毫秒数
.while hSocket ;开始循环读取返回的数据
invoke GetTickCount ;得到自电脑启动以来的毫秒数
sub eax,dwLastTime ;当前时间减去前面保存的毫秒数
.break .if eax >= 60 * 1000 ;如果差距达到1分钟,则跳出读取返回数据的循环
invoke _WaitData,hSocket,200 * 1000 ;等待数据200ms,即200毫秒
invoke RtlZeroMemory, addr @szBuffer, 1500
invoke recv, hSocket, addr @szBuffer, 1549, 0 ;想要接收到的字节个数,一般是参数2的字节数-1,把\0字符串结尾留出来
invoke _HandleData,addr @szBuffer,1500 ;这里不能按字符串的长度赋值,因为服务器返回的数据里面可能很多0,而一般字符串以0为结束符
invoke MultiByteToWideChar,65001,0,addr @szBuffer,1549,addr @Rec_szBuffer,4500 ;将接收到的utf-8编码的字符串转换为Unicode编码的字符串,CP_UTF8的值是65001
invoke GetWindowTextLength,hEdithwnd01 ;开始让数据紧跟着前面的数据显示在编辑框里面
mov @stCR.cpMin,eax
mov @stCR.cpMax,eax
invoke SendMessageW,hEdithwnd01,EM_EXSETSEL,0,addr @stCR
invoke SendMessageW,hEdithwnd01,EM_REPLACESEL,FALSE,addr @Rec_szBuffer
.endw
ret
_WorkThread endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL @stWsa:WSADATA
.if uMsg == WM_INITDIALOG
invoke LoadIcon,hInstance,ICO_MAIN
invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eax
invoke WSAStartup,101h,addr @stWsa
mov eax,hWnd
mov hMainhwnd,eax
invoke GetDlgItem,hMainhwnd,ID_EDIT01
mov hEdithwnd01,eax
invoke GetDlgItem,hMainhwnd,ID_EDIT02
mov hEdithwnd02,eax
.elseif uMsg == WM_COMMAND
mov ebx,wParam
.if bx == ID_BUTTON01
invoke CreateThread,NULL,0,offset _WorkThread,0,NULL,0 ;启动连接线程
.elseif bx == ID_BUTTON02
invoke SendMessageW,hEdithwnd01,WM_SETTEXT,0,0 ;清空编辑框
invoke lstrlen, addr szHello
invoke send, hSocket, addr szHello, eax, 0
.endif
.elseif uMsg == WM_CLOSE ;退出程序时记得清除套接字
.if ! hSocket ;如果socket创建失败,则清除它,否则先关闭,再清除
invoke WSACleanup
invoke EndDialog,hWnd,NULL
.else
invoke closesocket,hSocket
xor eax,eax
mov hSocket,eax
invoke WSACleanup
invoke EndDialog,hWnd,NULL
.endif
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgProc endp
end start
;下面为rc文件内容
#include "resource.h" //提示缺少该文件,可以在资源里下载
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#define MAINDIALOG 1
#define ICO_MAIN 1000 //图标
#define ID_BUTTON01 41
#define ID_BUTTON02 42
#define ID_EDIT01 11 //编辑框标识符
#define ID_EDIT02 12
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN ICON "Main.ico"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//定义对话框
MAINDIALOG DIALOG 10, 10, 210, 260
STYLE DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "对话框程序模版"
FONT 11, "方正姚体"
BEGIN
PUSHBUTTON "连接网站", ID_BUTTON01, 140,8,60,12
PUSHBUTTON "发送内容", ID_BUTTON02, 140,28,60,12
CONTROL "这里显示的是服务器返回的信息",ID_EDIT01,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|WS_VSCROLL,10, 50, 190, 200,WS_EX_CLIENTEDGE //设置成多行编辑框,按回车时加回车符
CONTROL "这里是提取到的链接地址",ID_EDIT02,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|WS_VSCROLL,10, 10, 120, 30,WS_EX_CLIENTEDGE
END