-
-
[旧帖] [原创]罗云彬《32位汇编语言》16章中对TCP聊天室程序的扩展3 0.00雪花
-
发表于: 2009-9-19 21:52 1265
-
第三步就是实现限制用户重复登录的功能。
1. 在Server.asm中,定义了一个48个字节的全局变量szusernames,每个用户占12个字节,所以最多只能同时登录4个用户。当收到客户端发过来的“登录数据包”时,首先判断此时的dwThreadCounter的值,如果为1则说明这是第一个登录的用户,不用和谁能行比较了,把这个登录的用户名放到szusernames的前12个字节中,并发回一个“登录成功”数据包(CMD_LOGIN_RESP数据包,dbResult字段为0);如果dwThreadCounter>=5,说明这是第五个登录的用户,而我这个服务器最多只支持4个用户登录,返回一个“登录用户太多”数据包(dbResult字段为2,这是我自定义的);如果dwThreadCounter>1并且<5,说明已经有用户登录过了,于是就要判断这个刚登录的用户的用户名有没有被使用过。如果没有被使用过,就把这个登录的用户的用户名放进szusername的某个12个字节中,发回“登录成功”数据包,如果被使用过了,就直接发回一个“登录失败(deResult字段为1)”数据包了。
①
.data
szusernames db 48 dup (?) ;canmeng萌3
②
当服务器收到用户登录数据包时,首先进行判断:
;********************************************************************
; 用户名和密码检测,为了简化程序,现在可以使用任意用户名和密码
;********************************************************************
invoke _RecvPacket,_hSocket,esi,sizeof @szBuffer
or eax,eax
jnz _Ret
.if [esi].MsgHead.dwCmdId != CMD_LOGIN
jmp _Ret
.else
invoke lstrcpy,addr [edi].szUserName,addr [esi].Login.szUserName
;如果登录的只有我自己一个用户,那当然不用和谁进行比较啦
.if dwThreadCounter<=1 ;canmeng萌3
jmp _register ;canmeng萌3
.elseif dwThreadCounter>=5 ;canmeng萌3
jmp _usertoomuch ;canmeng萌3
.else ;canmeng萌3
mov ecx,dwThreadCounter ;canmeng萌3
sub ecx,1 ;canmeng萌3
.repeat ;canmeng萌3 nop ;canmeng萌3
sub ecx,1 ;canmeng萌3
mov eax,12 ;canmeng萌3
mul ecx ;canmeng萌3
mov edx,offset szusernames ;canmeng萌3
add edx,eax ;canmeng萌3
push ecx ;canmeng萌3
push edx ;canmeng萌3
invoke lstrcmp,edx,addr [esi].Login.szUserName ;canmeng萌3
.if eax==0 ;canmeng萌3
pop edx ;canmeng萌3
pop ecx ;canmeng萌3
jmp _shibai ;canmeng萌3
.endif ;canmeng萌3
pop edx ;canmeng萌3
pop ecx ;canmeng萌3
.until cx==0 ;canmeng萌3
.endif ;canmeng萌3
;********************************************************************
;把这个登录的用户名放到用户名表中
_register: mov ecx,dwThreadCounter ;canmeng萌3
sub ecx,1 ;canmeng萌3
mov eax,12 ;canmeng萌3
mul ecx ;canmeng萌3
mov edx,offset szusernames ;canmeng萌3
add edx,eax ;canmeng萌3
invoke lstrcpy,edx,addr [esi].Login.szUserName ;canmeng萌3
mov [esi].LoginResp.dbResult,0
jmp fanhui ;canmeng萌3
_shibai: mov [esi].LoginResp.dbResult,1 ;canmeng萌3
jmp fanhui ;canmeng萌3
_usertoomuch: mov [esi].LoginResp.dbResult,2 ;canmeng萌3
.endif
fanhui: ;canmeng萌3
mov [esi].MsgHead.dwCmdId,CMD_LOGIN_RESP
mov [esi].MsgHead.dwLength,sizeof MSG_HEAD+sizeof MSG_LOGIN_RESP
在这个程序中的push ecx,push edx和pop ecx,pop edx实在是无奈之举,这是因为lstrcmp函数会影响他们的值。我也不知道这是为什么。
③
当有客户退出聊天室的时候,要在szusernames中把该用户名清除,并把后面的用户名统一向前移动12个字节。
;********************************************************************
; 广播:xxx 退出了聊天室
;********************************************************************
invoke lstrcpy,esi,addr [edi].szUserName
invoke lstrcat,esi,addr szUserLogout
invoke _InsertMsgQueue,addr szSysInfo,addr @szBuffer
;************************************************************* ;canmeng萌3
;清除这个线程对应的用户名,并把后面的所有用户名统一向前面移动12个字节
;************************************************************* ;canmeng萌3
invoke WaitForSingleObject,hEvent,INFINITE ;canmeng萌3,等待事件对象置位
mov edx,offset szusernames ;canmeng萌3
mov ecx,1 ;canmeng萌3
.while TRUE ;canmeng萌3
push edx ;canmeng萌3
push ecx ;canmeng萌 3
invoke lstrcmp,edx,addr [edi].szUserName ;canmeng萌3
pop ecx ;canmeng萌3
pop edx ;canmeng萌3
.break .if eax==0 ;canmeng萌3
add edx,12 ;canmeng萌3
add ecx,1 ;canmeng萌3
.endw ;canmeng萌3
mov edi,edx ;canmeng萌3
mov esi,edx ;canmeng萌3
add esi,12 ;canmeng萌3
mov eax,dwThreadCounter ;canmeng萌3
sub eax,ecx ;canmeng萌3
mov ecx,eax ;canmeng萌3
mov eax,12 ;canmeng萌3
mul ecx ;canmeng萌3
mov ecx,eax ;canmeng萌3
cld ;canmeng萌3
rep movsb ;canmeng萌3
;********************************************************************
; 关闭 socket
;********************************************************************
_Ret:
invoke closesocket,_hSocket
dec dwThreadCounter
invoke SetEvent,hEvent ;canmeng萌3,置位事件对象
④
在编程中由于涉及到了多线程的同步问题,所以我创建了一个事件对象。在.data段中定义了一个全局变量hEvent,用于存放事件对象的句柄。
hEvent dd ? ;canmeng萌3
⑤
主窗口程序中当初始化对话框时创建一个事件对象
.if eax == WM_INITDIALOG
……
invoke CreateEvent,NULL,FALSE,TRUE,NULL ;canmeng萌3,创建一个事件对象
mov hEvent,eax ;canmeng萌3
⑥
关闭对话框时就关闭事件对象句柄
.elseif eax == WM_CLOSE
……
invoke CloseHandle,hEvent ;canmeng萌3,关闭事件对象的句柄
⑦
在程序中总共有两处用到了WaitForSingleObject,SetEvent这一对。第一次是_ServiceThread的开始处,第二次是广播“***退出了聊天室”之后。
2.在Client1.asm中
①
在.const段中定义一个全局变量sztoomuch,当用户作为第五个用户时就跳出这个提示。
.const
sztoomuch db '无法登录到服务器,最多只能登录4个用户,您是第五个!',0
②
当客户端收到服务器发来的“登录回应”数据包时,判断里面的dbResult字段。
invoke_RecvPacket,hSocket,addr @stMsg,sizeof @stMsg
or eax,eax
jnz @F
cmp @stMsg.MsgHead.dwCmdId,CMD_LOGIN_RESP
jnz @F
.if @stMsg.LoginResp.dbResult==1 ;canmeng萌3
@@:
invoke MessageBox,hWinMain,addr szErrLogin,NULL,MB_OK or MB_ICONSTOP
jmp _Ret
.elseif @stMsg.LoginResp.dbResult==2 ;canmeng萌3,如果值为2代表登录人数太多
invoke MessageBox,hWinMain,addr sztoomuch,NULL,MB_OK ;canmeng萌3
jmp _Ret
.endif
这样,第三个问题限制用户重复登录也解决了。
附件是源代码。
1. 在Server.asm中,定义了一个48个字节的全局变量szusernames,每个用户占12个字节,所以最多只能同时登录4个用户。当收到客户端发过来的“登录数据包”时,首先判断此时的dwThreadCounter的值,如果为1则说明这是第一个登录的用户,不用和谁能行比较了,把这个登录的用户名放到szusernames的前12个字节中,并发回一个“登录成功”数据包(CMD_LOGIN_RESP数据包,dbResult字段为0);如果dwThreadCounter>=5,说明这是第五个登录的用户,而我这个服务器最多只支持4个用户登录,返回一个“登录用户太多”数据包(dbResult字段为2,这是我自定义的);如果dwThreadCounter>1并且<5,说明已经有用户登录过了,于是就要判断这个刚登录的用户的用户名有没有被使用过。如果没有被使用过,就把这个登录的用户的用户名放进szusername的某个12个字节中,发回“登录成功”数据包,如果被使用过了,就直接发回一个“登录失败(deResult字段为1)”数据包了。
①
.data
szusernames db 48 dup (?) ;canmeng萌3
②
当服务器收到用户登录数据包时,首先进行判断:
;********************************************************************
; 用户名和密码检测,为了简化程序,现在可以使用任意用户名和密码
;********************************************************************
invoke _RecvPacket,_hSocket,esi,sizeof @szBuffer
or eax,eax
jnz _Ret
.if [esi].MsgHead.dwCmdId != CMD_LOGIN
jmp _Ret
.else
invoke lstrcpy,addr [edi].szUserName,addr [esi].Login.szUserName
;如果登录的只有我自己一个用户,那当然不用和谁进行比较啦
.if dwThreadCounter<=1 ;canmeng萌3
jmp _register ;canmeng萌3
.elseif dwThreadCounter>=5 ;canmeng萌3
jmp _usertoomuch ;canmeng萌3
.else ;canmeng萌3
mov ecx,dwThreadCounter ;canmeng萌3
sub ecx,1 ;canmeng萌3
.repeat ;canmeng萌3 nop ;canmeng萌3
sub ecx,1 ;canmeng萌3
mov eax,12 ;canmeng萌3
mul ecx ;canmeng萌3
mov edx,offset szusernames ;canmeng萌3
add edx,eax ;canmeng萌3
push ecx ;canmeng萌3
push edx ;canmeng萌3
invoke lstrcmp,edx,addr [esi].Login.szUserName ;canmeng萌3
.if eax==0 ;canmeng萌3
pop edx ;canmeng萌3
pop ecx ;canmeng萌3
jmp _shibai ;canmeng萌3
.endif ;canmeng萌3
pop edx ;canmeng萌3
pop ecx ;canmeng萌3
.until cx==0 ;canmeng萌3
.endif ;canmeng萌3
;********************************************************************
;把这个登录的用户名放到用户名表中
_register: mov ecx,dwThreadCounter ;canmeng萌3
sub ecx,1 ;canmeng萌3
mov eax,12 ;canmeng萌3
mul ecx ;canmeng萌3
mov edx,offset szusernames ;canmeng萌3
add edx,eax ;canmeng萌3
invoke lstrcpy,edx,addr [esi].Login.szUserName ;canmeng萌3
mov [esi].LoginResp.dbResult,0
jmp fanhui ;canmeng萌3
_shibai: mov [esi].LoginResp.dbResult,1 ;canmeng萌3
jmp fanhui ;canmeng萌3
_usertoomuch: mov [esi].LoginResp.dbResult,2 ;canmeng萌3
.endif
fanhui: ;canmeng萌3
mov [esi].MsgHead.dwCmdId,CMD_LOGIN_RESP
mov [esi].MsgHead.dwLength,sizeof MSG_HEAD+sizeof MSG_LOGIN_RESP
在这个程序中的push ecx,push edx和pop ecx,pop edx实在是无奈之举,这是因为lstrcmp函数会影响他们的值。我也不知道这是为什么。
③
当有客户退出聊天室的时候,要在szusernames中把该用户名清除,并把后面的用户名统一向前移动12个字节。
;********************************************************************
; 广播:xxx 退出了聊天室
;********************************************************************
invoke lstrcpy,esi,addr [edi].szUserName
invoke lstrcat,esi,addr szUserLogout
invoke _InsertMsgQueue,addr szSysInfo,addr @szBuffer
;************************************************************* ;canmeng萌3
;清除这个线程对应的用户名,并把后面的所有用户名统一向前面移动12个字节
;************************************************************* ;canmeng萌3
invoke WaitForSingleObject,hEvent,INFINITE ;canmeng萌3,等待事件对象置位
mov edx,offset szusernames ;canmeng萌3
mov ecx,1 ;canmeng萌3
.while TRUE ;canmeng萌3
push edx ;canmeng萌3
push ecx ;canmeng萌 3
invoke lstrcmp,edx,addr [edi].szUserName ;canmeng萌3
pop ecx ;canmeng萌3
pop edx ;canmeng萌3
.break .if eax==0 ;canmeng萌3
add edx,12 ;canmeng萌3
add ecx,1 ;canmeng萌3
.endw ;canmeng萌3
mov edi,edx ;canmeng萌3
mov esi,edx ;canmeng萌3
add esi,12 ;canmeng萌3
mov eax,dwThreadCounter ;canmeng萌3
sub eax,ecx ;canmeng萌3
mov ecx,eax ;canmeng萌3
mov eax,12 ;canmeng萌3
mul ecx ;canmeng萌3
mov ecx,eax ;canmeng萌3
cld ;canmeng萌3
rep movsb ;canmeng萌3
;********************************************************************
; 关闭 socket
;********************************************************************
_Ret:
invoke closesocket,_hSocket
dec dwThreadCounter
invoke SetEvent,hEvent ;canmeng萌3,置位事件对象
④
在编程中由于涉及到了多线程的同步问题,所以我创建了一个事件对象。在.data段中定义了一个全局变量hEvent,用于存放事件对象的句柄。
hEvent dd ? ;canmeng萌3
⑤
主窗口程序中当初始化对话框时创建一个事件对象
.if eax == WM_INITDIALOG
……
invoke CreateEvent,NULL,FALSE,TRUE,NULL ;canmeng萌3,创建一个事件对象
mov hEvent,eax ;canmeng萌3
⑥
关闭对话框时就关闭事件对象句柄
.elseif eax == WM_CLOSE
……
invoke CloseHandle,hEvent ;canmeng萌3,关闭事件对象的句柄
⑦
在程序中总共有两处用到了WaitForSingleObject,SetEvent这一对。第一次是_ServiceThread的开始处,第二次是广播“***退出了聊天室”之后。
2.在Client1.asm中
①
在.const段中定义一个全局变量sztoomuch,当用户作为第五个用户时就跳出这个提示。
.const
sztoomuch db '无法登录到服务器,最多只能登录4个用户,您是第五个!',0
②
当客户端收到服务器发来的“登录回应”数据包时,判断里面的dbResult字段。
invoke_RecvPacket,hSocket,addr @stMsg,sizeof @stMsg
or eax,eax
jnz @F
cmp @stMsg.MsgHead.dwCmdId,CMD_LOGIN_RESP
jnz @F
.if @stMsg.LoginResp.dbResult==1 ;canmeng萌3
@@:
invoke MessageBox,hWinMain,addr szErrLogin,NULL,MB_OK or MB_ICONSTOP
jmp _Ret
.elseif @stMsg.LoginResp.dbResult==2 ;canmeng萌3,如果值为2代表登录人数太多
invoke MessageBox,hWinMain,addr sztoomuch,NULL,MB_OK ;canmeng萌3
jmp _Ret
.endif
这样,第三个问题限制用户重复登录也解决了。
附件是源代码。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
他的文章
看原图
赞赏
雪币:
留言: