您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 企业财务 > 第十八章SOCKET类的实现
第十八章SOCKET类的实现这几天反复思考,到底是从上到下、还是从底层开始往上设计?最后、还是决定从上层建筑开始。APO追求的是简单、再简单!强大、再强大!高速、高效!“天下武功、无坚不破、唯快不破!”。APO的socket也不外是一种内存文件吧,但socket描述符和其它类型的文件描述符还是略有区别的。APO中的一个用户进程最多可打开64K个非socket类型的文件描述符,而APO系统只是最多可以打开16M个v节点,需要256个64K位的位图变量、刚好1个位图变量数据块;中模式管理。socket描述符也是在16M个文件描述符中分配,APO中的socket描述符、描述的是一个连接、可以说是连接号,与UNIX可能会有一点不同;理论上、APO可以在一个服务端口上最大支持1千6百多万个连接。APO的网络编程是非常简单的,如服务端程序、只是在初始化方法中open一个socket,余下的只是对各种连接消息的处理方法的编写。即使有1千万个连接,那又能怎样?对于它们的请求消息的处理方法都是一样的。用户无须关心打开了多少socket描述符,有多少连接,关闭连接,TCP/UDP、管理socket描述符等等,那都是用户系统网络进程的事情。对于用户的网络编程,我们就是要越简单越好。服务端程序只是关心、来了一个带请求的连接消息,需要处理该请求消息,使用消息中的socket描述符就可操作该连接对应的接收、或发送流容器;至于、该连接是如何建立起来的、当前是否有1千万个连接等等,不用管的。LINUX、WINSOCKET中的bind()、socket()、listen()、accept()、connect()、write()、read()、recvfrom()、sendto()、upds_respon()、recv()、send()、recvmsg()、sendmsg()、close、shutdoen()、循环服务器、并发服务器、多路I/O复用、原始套接字等等、我都看晕了,这难道是给应用程序员使用的网络编程方法库?我只能感叹!真垃圾!这是底层程序员使用的、不是应用程序员啊。发展了几十年,应该是精华的浓缩了;为何我总看不顺眼?总感觉这样做、复杂、效率低,为何聪明人总是把问题复杂化啊?我也很想兼容和适应它们,但总是有心无力;自己笨、没法把握他们的思路。不管怎样,服务端支持千万个连接的功能我是加定了;这样,1000个游戏服务器就可支持100亿人同时在线了。在APO中,网络编程是分为三层:第一层SOCKET应用层,第二层是TCP/UDP(用户CPU线路的系统网络服务进程)、第三层是最底层IP/ICMP(内核CPU线路的实时线程)。层之间是使用消息交互、句柄提交、事件驱动。后2层是系统实现,用户主要是使用第一层编程。SOCKET是一种内存文件,网络编程分为服务端和客户端,网络协议主要就是TCP/UDP;在open文件SOCKET时、就需指明。open、close本来就是文件系统的基本方法,本文只是描述与socket有关的内容。数据报接收是2、3层自动实现的,包含数据报内容的句柄就是相应socket连接的文件号sockfd,以消息方式提交给用户进程;用户只需使用sockfd.成员的赋值指令,就可对接收的数据报进行读、写。不同的文件号sockfd句柄、对应不同的连接,有不同的内存容器。只要本地内存空间够大,支持千万个连接就没问题。内存分配是无须用户去考虑,只要给出规划就行了。为何?当我们编写一个客户端程序时,无法预先知道你请求之后、服务器返回的数据报大小,可能2GB;也就无法一开始就声明接收流容器的本地内存空间大小。但接收到服务器返回的数据报后,底层知道;所以、就可由底层申请分配内存,并将句柄关联到相应的sockfd。那接收就似乎无须用户规划了,但你发送的请求数据报是可以预设大小的。如果客户端,请求一个4GB大小的文件,难道服务器就要即时申请一个连续4GB大小的内存空间?用户编写的服务端程序无须这样麻烦,只是向2层发一个消息就行了(sendto())、一切2层搞掂。或许APO在SOCKET层就3个方法了:open()、close()、sendto()。前2个本来就是文件系统的基本方法,open()方法的参数有所不一样吧;所以改为opens()。用户程序主要是编写消息处理;实现HTTP协议、FTP等等。其实、即使这些协议的实现;APO操作系统都包含进去了。用户主要就是编写游戏等服务端的消息处理吧。当是客户端时,TCP/UDP的端口可以对应到sockfd、进程号、或线程号。但如是服务端,就不一样了;虽然能对应到进程号,但可能有几百万个连接、sockfd文件号。所以,服务端应设置流标签(低24位)=sockfd文件号(连接号);这样、下次客户端的带有相同流标签(连接号)的请求到来时,服务端的底层就可迅速定位到相应的连接v节点(当然还需要比对一下)。一、服务端程序APO的进程就是一个消息恒循环,用户代码入口main();就是执行用户编写的进程消息处理代码。我们只需要在进程共用部分的初始化方法中Process_init();加入以服务端方式、TCP或UDP、最大连接数BACKLOG(单位为256个连接)、客户端发来的请求报文最大长度RECVLEN(单位行E)、服务端的发送流容器变量、的opens()方法。那么,余下的就是第二层TCP/UDP、用户CPU线路的系统网络服务进程的事情了。每当有一个新的属于服务进程的TCP/UDP数据报到来,第二层会给服务进程的动态优先级加码、以便服务进程可以更快的得到CPU;同时给服务进程发出一个消息(包含连接号sockfd、状态码等等)。当服务进程获得CPU时,可能已经有n个消息了;1、系统读入一个消息到H2行寄存器,服务进程处理该消息;2、判断、如果是第二层的系统网络服务进程发出,那么是网络数据报消息,用sockfd.成员,提取数据报、分析是什么请求、处理该请求,最后回发一个服务器数据报。3、循环回到1,如果没有需处理的消息、服务进程让步挂起返回。至于是如何构建一个连接,有上千万个连接、数据报的内存分配、IP包组装等等;都不是应用层需关心的,那是底层的事情。所以,APO的服务端程序没料啊,就一个opens()和回发服务器数据报时、可能使用到的sendto()(该方法与LINUX、UINX、WINDOWS的有区别)、或关闭一个连接close()。那些、bind()、socket()、listen()、accept()、connect()、write()、read()、recvfrom()、upds_respon()、recv()、send()、recvmsg()、sendmsg()、shutdoen()、sigio、select()等等、通通清除;还应用程序员一个干净的世界。要注意的是:如果同一个连接的客户端发了一个数据报,在服务进程还没处理完毕时,又发来一个数据报;客户端将受到流量抑制。如果前面的数据报达到1GB以上,客户端将立刻受到流量抑制、不允许再发数据报;直到服务进程处理完上一个数据报。至于、黑客回溯追踪,恶意TCP/UDP包判断,连接处理,重发等定时器,IP黑名单,攻击报警、包过滤等等都是底层的事情,用户不必关注。我知道UDP、可能上千万个连接问题不大,但TCP就需另外对待了;就那几种定时器就不好处理,难道要设计几千万个定时器?这不可能;有待研究。二、客户端编程用户可以在进程、或线程内设置客户端TCP/UDP程序。还是那3个方法,但opens方法的参数设置不一样;也不是在初始化方法出现;在线程、或进程打开一个连接opens,那通常是需要用户关闭连接close。BUxEmsg;//在主程序、声明报文发送流容器(x行)。Thread_client(){//在线程内的客户端方法、线程入口。sockfd=opens(FLAG.PORT);//获得socket文件号,PORT=XX指定的客户端口,0由系统指定端口;//FLAG=0xA000(TCP)、0x8000(UDP);由第二层建立连接。//设置需连接的服务器端IP地址、端口等sockfd.vnode.MDA=?;//服务端的以太网目标地址sockfd.vnode.DLADD=?;//服务端目标链路地址,DLADD+MD目标IP地址sockfd.vnode.DPORT=?;//服务端的目的端口msg=?;//填写第一个消息报文。sendto(sockfd,flags,msg,len);//发送第一个数据报。PRET//分支定点等待消息、阻塞返回;新的线程入口在下一条指令。msgpro();//服务器返回的数据报处理方法;消息在H2,线程新入口。//收到一个服务器数据报、由二层返回一个消息到相应进程,而进程将启动相//应的线程msgpro()方法来处理。IfFLAG.关闭标志=1thengototclt;msg=?;//填写消息报文。sendto(sockfd,flags,msg,len);//由第二层发送一个新的请求数据报。WRET//等待消息、阻塞返回,准备处理下一个数据报消息。tclt:close(sockfd);//关闭连接TRET//线程终止返回//msg也可以是关联到进程、或线程打开的一个普通文件号的流容器;这样,客户端也就可以上传一个文件(文件大小可到4GB);APO的数据报可以大到4GB。三、opens()、sendto()、close()方法opens()方法只是把应用层的参数传入系统层(第二层),并返回一个文件号;客户端与服务端的参数是不一样的。对于返回的文件号sockfd,客户端才使用;而服务端是使用消息中的由第二层给出的新的连接文件号。R0参数FLAG.PORT:高16位R0H标志FLAG、低16位R0LTCP/UDP的指定端口PORT。APO的TCP服务端、oxE000,TCP客户端、oxA000;APO的UDP服务端、oxC000,UDP客户端、ox8000。//FLAG.15DOMAIN;协议域:1、INET_TYPE,0、其它。//FLAG.14NETMODEL;网络模式:1、serve服务端,0、client客户端。//FLAG.13PROTOCOL;协议:1、TCP(SOCK_STREAM),0、UDP(SOCK_DGRAM)。//FLAG.12-8STADE;5位连接状态标志。//FLAG.7-0TYPE;协议类型://PF_INET、IPAPOinternet协议,PF_INET、IPV4internet协议,//PF_INET6、IPV6internet协议,PF_IPX、NOVELL协议,//PF_NETLINK、内核用户界面协议,PF_X25、X.25/ISO-8208协议,//PF_AX25、AMATEURRADIOAX2.5协议,PF_ATMPVC、原始ATMPVC访问,//PF_APPLETALK、APPLETALK协议,PF_PACKET、底层包访问协议,socket文件是在内存建立的、和其它类型的文件不一样,无须i节点、目录项,也不需要文件打开表项;只有不一样的v节点,描述了一个连接。4E大小的v节点,前面2E是发送IP数据包的包头(包括MAC头、IP头、TCP/UDP头);后2E是连接描述,TCP/UDP的连接参数;下一章会详细说明。1、服务端模式:sockfd=opens(FLAG.PORT,BACKLOG,RECVLEN);BU16BACKLOG;//最大连接数、单位为256个连接。R1LBU32RECVLEN;//低24位,允许的最大报文长度、单位E。R22、客户端模式:sockfd=opens(FLAG.PORT);需要使用返回的文件号来设置v节点中服务端的IP地址、端口号。sockfd.vnode.MDA=?;//服务端的以太网目标地址sockfd.vnode.DLADD=?;//服务端的目标链路地址,DLADD+MDA目标IP地址sockfd.vnode.DPORT=?;//服务端的目的端口3、sendto()
本文标题:第十八章SOCKET类的实现
链接地址:https://www.777doc.com/doc-2090680 .html