前言
本文接着上一章,上一章结尾我们说过,还有很多欠缺,比如文件只能读取40960字节,并且只允许请求一次主进程便退出了,这次我们来完成多进程服务器设计,那人家都是多线程,为什么你要多进程呢?
原因是以现在的功力,还没办法在nasm中掌握线程的使用,虽然我已经找到了相关文章,但还需要些时间,这如果使用MASN,那么情况可能会变得更好。
fork
fork函数用来在linux中创建一个进程,子进程和父进程运行在不同的内存中,并且两个内存空间内容是一样的,但是编写他的时候,难于理解的是调用一次他,却返回两次结果,这两次分别在子进程和父进程中。
看一下整体代码
%include "/home/HouXinLin/project/nasm/include/io.inc"
SECTION .data
headers db 'HTTP/1.1 200 OK', 0Dh, 0Ah, 'Content-Type: application/octet-stream', 0Dh, 0Ah, 0Dh, 0Ah
root db '/home/HouXinLin/test', 0h
msg db 'accept',0h
ok db 'ok',0h
fail db 'fail',0h
SO_REUSEADDR db 1,0h
SECTION .bss
fileContents resb 40960
responseBuffer resb 40960
requestBuffer resb 4096
fullPath resb 1024
requestPath resb 1024
socketbuf resb 4
SECTION .text
global CMAIN
CMAIN:
mov ebp, esp
xor eax, eax
xor ebx, ebx
xor edi, edi
xor esi, esi
socket:
push byte 6
push byte 1
push byte 2
mov ecx, esp
mov ebx, 1
mov eax, 102
int 80h ;;创建Socket
bind:
push eax
mov edi,4
mov esi,SO_REUSEADDR
mov edx,2
mov ecx,1
mov ebx,eax
mov eax,366
int 80h
pop eax
mov edi, eax ;;edi存放server socket描述副
push dword 0x00000000
push word 0x901f ;;端口8080
push word 2
mov ecx, esp
push byte 16
push ecx
push edi
mov ecx, esp
mov ebx, 2
mov eax, 102
int 80h ;;绑定8080端口
cmp eax,0
je listen
jmp exit
listen:
push byte 1
push edi
mov ecx, esp
mov ebx, 4
mov eax, 102
int 80h ;监听
accept:
push byte 0
push byte 0
push edi
mov ecx, esp
mov ebx, 5
mov eax, 102
int 80h
mov esi, eax ;;将客户端描述符保存到esi中
mov eax, 2
int 80h
cmp eax, 0
jz read
jmp accept
read:
mov edx,6
mov ecx,msg
mov ebx,1
mov eax,4
int 80h
mov edx, 4096 ;;读取客户端内容
mov ecx, requestBuffer
mov ebx, esi
mov eax, 3
int 80h
getRequestResourcePath: ;;获取请求资源路径
mov eax,requestBuffer
mov ebx,eax
nextResourceChar:
cmp byte[ebx],32 ;;如果是空格
jz record ;;开始记录
inc ebx ;;下一个字符
jmp nextResourceChar
ret
record:
mov edx,ebx ;;ebx是第一个空格后的位置
sub edx,eax ;存放开始索引
mov ecx, requestBuffer
add ecx,edx ;;从ecx后的位置开始查看地一个空格
mov edx,0
hasEnd:
inc ecx
cmp byte[ecx],32 ;;如果下一个也是空格
jz finishSearch
mov eax, dword[ecx] ;;获取当前字符
mov dword[requestPath+edx],eax
inc edx
jmp hasEnd
finishSearch:
push esi
mov esi,root
mov edi,fullPath
mov ecx,20
rep movsb ;;复制root
mov esi,requestPath
mov edi,fullPath
add edi,20
mov ecx,edx
rep movsb
pop esi
write:
push esi
mov edi,responseBuffer ;;字符复制目的地址
mov esi,headers ;;字符复制原地址
mov ecx,59 ;;复制59个字节到响应buffer中
rep movsb
call readFile ;;读取文件
mov edi,responseBuffer+59 ;;偏移59个字节拼接body
mov esi,fileContents
mov ecx,eax
rep movsb ;;在复制n个字节,eax是读取到的字节数量,不固定
pop esi
mov edx, eax ;;文件内容长度
add edx,59 ;;加上头部长度
mov eax,edx
mov ecx, responseBuffer ;;输出
mov ebx, esi
mov eax, 4
int 80h
;;关闭客户端socket
push 2
push esi
mov ecx, esp
mov ebx, 13
mov eax, 102
int 80h
cmp eax,0
jz exit
mov edx,4
mov ecx,fail
mov ebx,1
mov eax,4
int 80h
jmp exit
readFile:
mov ecx, 4 ;;打开文件
mov ebx, fullPath
mov eax, 5
int 80h
mov edx, 40960 ;;尝试读取40960个字节到fileContents
mov ecx, fileContents
mov ebx, eax
mov eax, 3
int 80h ;读取
ret
exit:
mov ebx,0
mov eax,1
int 80h
这比上一章并没有多出多少,主要的下面这几句,linux作为一个多进程的系统,把创建进程的系统调用号设置为2也是很好理解,也体现了重要性。
accept:
mov eax, 2
int 80h
cmp eax, 0
jz read
jmp accept
当int 80h执行后,此时会有两个进程执行下面的语句,一个是在子进程,一个是在父进程,如果eax中是0,那么表示当前环境在子进程中,所以要跳转到读数据的代码下,如果是一个非0的正整数,那么表示还是在父进程中,所以要跳转到accept下重新等待客户端连接,这样就基本实现了一个多进程服务器。