
tcp面向连接保证可靠性比udp通信做更多工作udp服务端创建套接字绑定服务端需要被找到绑定固定的端口号直接收和发客户端创建套接字直接发echoserver最基础的功能本质是为了了解接口tcp建立连接使用listen系统调用由于udp不保证可靠性如果网络拥挤直接丢包但是tcp面向连接保证可靠性backlog参数的含义是当有多个客户端申请建立连接最长底层链接等待队列长度一般设为16或32等其实长度是backlog1EchoTcpServer.hpp不需要显式包含ipip地址复用socket第二个参数是SOCK_STREAM因为采用的是tcp流式套接字面向字节流第三个是协议字段指明协议名称使用0即可前面SOCK_STREAM以及指明是TCP1.建立连接版accept系统调用返回值是文件描述符socket获得的文件描述符是用于监听称为listen_socket也就是监听套接字获取新链接具体和客户端建立连接底层不断获取新连接通信用的是accept返回的文件描述符也就是IOsocket网络通信本质是在做IO就是进程间通信网络通信就是进程间通信也就是服务socket代表一条连接提供io service参数addr和addrlen是客户端的信息与udp中recvfrom中获取客户端信息的参数作用类似类似饭店门口有一位迎宾也就是listen socket接收到客人后也就是客户端发起请求进行一次accept也就是指派一个服务员(IOsocket)服务客人addr是输出型参数addrlen是输入输出型参数tcp面向字节流读可以直接用read写可以直接用write当然也可以使用recv接收send发flag一般设为0阻塞式发送2.Echo版2.1 串行版EchoTcpClient.cc客户端的ip和端口只需要在客户端机器保证唯一即可客户端的socket肯定要绑定ip和端口但是不需要显式绑定因为如果绑定的端口是已经被使用的端口就绑定失败但是用户很难知道哪些端口使用了所以由OS随机绑定EchoTcpServer.hpp创建套接字和建立连接如果一次不成就退出了但是如果网络不佳其实系统会自动重连直到重连次数超过多少还未成功才放弃重连重连伪代码客户端和服务端在一台机器上可以看到貌似有两条连接因为客户端和服务端在一台机器上客户端和服务端在两台机器上tcp客户端和服务端双方都为维护连接建立了数据结构无实际工程意义2.2 多进程版如果在父进程直接阻塞等待那还是串行因为父进程要等待子进程结束回收资源之后才能继续获取新连接如果使用WNOHANG非阻塞等待有问题比如在创建1个进程之后调用waitpid发现没有子进程退出父进程继续accept但是没有新连接到来父进程就一直阻塞在accept当子进程结束父进程没办法回收如果此时创建的进程过多父进程就要长时间忍受多个僵尸进程而且有可能同时退出很多进程父进程一次只能回收一个进程来不及这样就要进行循环等待可以捕捉SIGCHLD信号在自定义信号捕捉函数中进行非阻塞轮询检测最佳实践是父进程忽略子进程退出时发出的信号自动释放资源有用且有限比如内存文件描述符算力GPU等telnet可以远程以客户端的形式向客户端发送数据telnet 公网IP 端口号回车执行Ctrl]回车就可以正常输入数据了Ctrl] 回到命令行输入quit退出但是每来一个连接就创建一个进程创建PCB地址空间页表代价比较大选择创建线程2.3 多线程版使用自己封装的线程问题是提供的是长服务客户端不主动断开连接服务端一直提供服务典型的是打游戏短则十几分钟长则一个小时但其实无论是多线程还是多线程都不适合处理长连接只能应对中小型其实是小型应用长连接使用多路转接技术而且多线程应用于长服务还有问题是如果恶意想搞挂掉进程就发送大量的tcp连接普通创建几十进程几千线程服务器就卡的不像样子增加CPU切换调度成本如果10个线程每个线程时间片1ms每个线程调度周期就是10ms100个就是100ms1000个就是1s我们日常用的登录注册等网络服务一般都是短服务建立连接之后服务器读一次写一次就关闭了再需要服务再建立连接即可这样即使有很多客户端发起请求但是服务的时间很短压力也不是特别大每当有客户端建立连接就要创建线程负担还是有些重所以就采用线程池先把线程创建好来了直接用没有创建线程的开销有可能创建线程的开销还要大于短服务的开销如果接入进程池还要考虑进程通信使用封装的线程池好处在于即使有很多客户端发起请求服务器来不及处理去任务队列排队线程数目固定不会陡增3.ssh应用版上面只是简单的回显可以做一个简单的应用处理用户输入的命令进行解析结果返回给用户如果再加上登录注册就是简单的xshell也可以处理用户输入的代码forkexec*对代码进行编译forkexec对代码进行执行结果返回给用户就是OJ平台可以在从线程也就是服务线程中创建管道fork父进程关闭写端子进程关闭读端子进程通过地址空间拿到父进程的cmd进行解析之后把结果写回管道父进程读取结果popen做的就是这个类似于打开文件type是打开方式可以是r或w用完之后使用pclose关闭文件Command.hppSSH_Server.cc加了注册逻辑TcpServer.hpp采用长连接长服务返回提示符TCPServer.hpp修改版本sevice对HandlerIO的返回值也就是读到的结果做判断其实有些不严谨没有读到数据不代表客户端断开连接如果n0默认客户端断开连接再循环进行发送和读取就不合理直接breakClient先读取客户端发来的字符串Command.hpp提供回调也就是获取命令行提示符方法提供的是长服务线程池中线程的数量就是可允许登录用户的最大数量下面测试可以看到inet_ntoa第一次调用的结果被第二次调用覆盖了inet_ntoa的结果是一个字符串是作为静态变量存储在inet_ntoa函数内部类似fopen的返回值是FILE*那FILE在哪里在函数内部new/malloc了FILE结构体对象但是inet_ntoa的结果可能被覆盖TCP/UDP存在使用sockaddr_in结构体对象构建InetAddr的时候会使用inet_ntoa并且之后封装成任务处理IO的时候都会用到InetAddr如果出现数据覆盖前面线程的字符串类型的IP地址可能就会出现问题inet_ntoa是不可重入函数可能存在线程安全问题但多线程问题不容易复现但只要有可能存在就不使用官方手册也说返回的是静态开辟的缓冲区类似static char isptr[]以下有关于数据包封装与分用描述错误的是A.应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header)称为封装(Encapsulation)B.不同的协议层对数据包有不同的称谓在传输层叫做数据报 (datagram)在网络层叫做段(segment)在链路层叫做帧(frame)C.在网络层会增加IP首部在对端的网络层会去掉IP首部并解析首部内容之后呈递给传输层D.在tcp协议分用的过程当中传输层会去掉tcp头部将应用数据呈递给上层B当一个UDP报文到达目的主机时操作系统使用 选择正确的socketA.源IP地址B.源端口号C.目的端口号D.目的IP地址C以下有关于端口号的说法错误的是A.tcp的最大端口号是65535B.端口号是一个2字节16位的整数C.IP地址 端口号能够标识网络上的某一台主机的某一个进程D.一个进程至多只能绑定一个端口A多选socket编程中经常需要进行字节序列的转换下列哪几个函数是将网络字节序列转换为主机字节序列A.htonsB.ntohsC.htonlD.ntohlBD在网络字节序中所谓”小端”(little endian)说法正确的是( )A.高字节数据存放在低地址处低字节数据存放在高地址处B.低字节位数据存放在内存低地址处, 高字节位数据存放在内存高地址处C.和编译器相关D.上述答案都不正确B多选Socket即套接字是一个对 TCP / IP协议进行封装 的编程调用接口socket的使用类型主要有A.基于TCP协议采用流方式提供可靠的字节流服务B.基于IP协议采用流数据提供数据网络发送服务C.基于HTTP协议采用数据包方式提供可靠的数据包装服务D.基于UDP协议采用数据报方式提供数据打包发送服务AD下列有关Socket的说法错误的是A.Socket用于描述IP地址和端口是一个通信链的句柄B.Socket通信必须建立连接C.Socket客户端的端口是不固定的D.Socket服务端的端口是固定的B