内容发布更新时间 : 2025/2/11 17:57:02星期一 下面是文章的全部内容请认真阅读。
计算机网络实验指导书
include kernel32.inc ;32位内核库 includelib kernel32.lib include wsock32.inc ;32位1.1版本的套接字库 includelib wsock32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; equ 数据
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;对应的窗口资源 DLG_MAIN equ 2000 IDC_INFO equ 2001 IDC_COUNT equ 2002
;通知消息使用的ID,必须选择大于WM_USER的值,本程序选择比WM_USER大100的值作为ID
WM_SOCKET equ WM_USER + 100 TCP_PORT equ 9999 ;用于建立套接字的TCP端口号 MAX_SOCKET equ 100 ;聊天室最大用户容量
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
;窗口句柄及建立的套接字 hWinMain dd ? hSocket dd ? ;当前连接的客户计数 dwCount dd ?
;recv原语收到的TCP数据包缓冲地址 szReadBuffer db 32768 dup (?) ;发送缓冲区
szBuffer db 32768 dup (?)
;在线客户列表,用于客户的添加与删除 stTable dd MAX_SOCKET dup (?) .const ;出错提示信息 szErrBind db '无法绑定到TCP端口9999,请检查是否有其它程序在使用!',0 ;在主窗口中显示客户信息的区分标题
szFormat db '【客户端#x】- %s',0dh,0ah,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
第 56 页 共 64 页
计算机网络实验指导书
>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 在客户端列表中加上一个 socket
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;线程1:在客户端列表中加上一个 socket _AddClient proc _hSocket ;设置套接字_hSocket在窗口hWinMain的工作模式,明确使用的消息ID WM_SOCKET和响应的通知消息类型FD_READ or FD_CLOSE
invoke WSAAsyncSelect,_hSocket,hWinMain,WM_SOCKET,FD_READ or FD_CLOSE
;获得客户列表地址
xor ebx,ebx mov esi,offset stTable .while ebx < MAX_SOCKET ;搜索客户列表,找到空地址,把新客户添加进去 .if ! dword ptr [esi] push _hSocket pop [esi] inc dwCount ;客户数增加1 ;窗口上客户数IDC_COUNT增加1,且不允许修改 invoke SetDlgItemInt,hWinMain,IDC_COUNT,dwCount,FALSE ret .endif ;调整指针,指向下一个客户的套接字 inc ebx add esi,4 .endw ;关闭套接字_hSocket invoke closesocket,_hSocket ret
_AddClient endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 从客户端列表中去掉一个 socket
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_RemoveClient proc _hSocket
;线程2:从客户端列表中去掉一个 socket ;获得客户列表地址 xor ebx,ebx mov esi,offset stTable
第 57 页 共 64 页
计算机网络实验指导书
mov edi,_hSocket .while ebx < MAX_SOCKET ;找到该套接字则关闭之 .if [esi] == edi invoke closesocket,[esi] mov dword ptr [esi],0 ;连接客户的数量减1 dec dwCount ;更新窗口中连接客户数,禁止修改 invoke SetDlgItemInt,hWinMain,IDC_COUNT,dwCount,FALSE ret .endif ;调整指针,指向下一个客户的套接字 inc ebx add esi,4 .endw ret
_RemoveClient endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 处理接收到的TCP包
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;线程3:处理接收到的TCP包 _RecvData proc _hSocket
;局部变量@dwRecv,保存收包成功后的状态值 local @dwRecv ;接收缓冲区szReadBuffer清空 invoke RtlZeroMemory,addr szReadBuffer,sizeof szReadBuffer ;接收TCP数据包,存入szReadBuffer invoke recv,_hSocket,addr szReadBuffer,sizeof szReadBuffer,NULL .if eax != SOCKET_ERROR ;保存状态值 mov @dwRecv,eax ;把消息从szReadBuffer复制到szBuffer invoke wsprintf,addr szBuffer,addr szFormat,_hSocket,addr szReadBuffer ;******************************************************************** ; 按照客户端列表逐一发送
;******************************************************************** ;获得窗口IDC_INFO中已经显示的客户信息
invoke GetDlgItem,hWinMain,IDC_INFO
mov ebx,eax ;计算信息长度 invoke GetWindowTextLength,ebx
第 58 页 共 64 页
计算机网络实验指导书
;添加空行 invoke SendMessage,ebx,EM_SETSEL,eax,eax ;插入信息 invoke SendMessage,ebx,EM_REPLACESEL,FALSE,addr szBuffer ;获得客户列表地址 mov esi,offset stTable xor ebx,ebx ;把消息向列表中客户逐一发送 .while ebx < MAX_SOCKET mov edi,[esi] .if edi invoke lstrlen,addr szBuffer ;计算信息长度 invoke send,edi,addr szBuffer,eax,0 ;发送信息 .endif ;调整指针,指向下一个客户的套接字 add esi,4 inc ebx .endw .endif ret
_RecvData endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 初始化 Socket,绑定到服务TCP端口并监听
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;初始化函数_Init _Init proc ;局部变量,保存WinSock库初始化后的信息 local @stWsa:WSADATA ;局部变量,用于组合IP地址和端口号 local @stSin:sockaddr_in ;加载并初始化WinSock库,版本号为1.1,返回信息在@stWsa中 invoke WSAStartup,101h,addr @stWsa ;建立套接字hSocket,类型为流套接字,协议类型为TCP invoke socket,AF_INET,SOCK_STREAM,0 mov hSocket,eax ;设置套接字hSocket在窗口hWinMain的工作模式,明确使用的消息ID WM_SOCKET和响应的通知消息类型FD_ACCEPT
invoke WSAAsyncSelect,hSocket,hWinMain,WM_SOCKET,FD_ACCEPT ;变量@stSin清零 invoke RtlZeroMemory,addr @stSin,sizeof @stSin ;组合IP地址和端口号,得到结构体@stSin invoke htons,TCP_PORT ;转换十进制的TCP端口号
第 59 页 共 64 页
计算机网络实验指导书
mov @stSin.sin_port,ax ;TCP端口 mov @stSin.sin_family,AF_INET ;地址类型,WINDOWS必须为AF_INET mov @stSin.sin_addr,INADDR_ANY ;32位IP地址 ;把套接字hSocket与@stSin绑定
invoke bind,hSocket,addr @stSin,sizeof @stSin .if eax == SOCKET_ERROR ; 弹出“错误”对话框,显示出错信息szErrBind invoke MessageBox,hWinMain,addr szErrBind,NULL,\\ MB_OK or MB_ICONWARNING ;绑定出错,向hWinMain发送WM_CLOSE消息 invoke SendMessage,hWinMain,WM_CLOSE,0,0 .else ;绑定成功,服务器进入监听状态
;监听队列允许保存的最大未处理连接数量为5
invoke listen,hSocket,5 .endif ret _Init endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 主窗口程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;主窗口程序
;窗口句柄hWnd,消息类型wMsg,参数类型wParam,参数值lParam _ProcDlgMain proc uses ebx edi esi hWnd,wMsg,wParam,lParam mov eax,wMsg
;******************************************************************** ; 处理 Socket 消息
;******************************************************************** .if eax == WM_SOCKET mov eax,lParam .if ax == FD_ACCEPT ;监听中的流套接字检测到有连接进入,启动添加客户线程 invoke accept,wParam,0,0 invoke _AddClient,eax .elseif ax == FD_READ ;套接字收到对端发送来的数据包,启动TCP数据包接收线程
invoke _RecvData,wParam
.elseif ax == FD_CLOSE ;检测到套接字对应连接被关闭,断开其与服务器的连接
invoke _RemoveClient,wParam
.endif
;********************************************************************
第 60 页 共 64 页