专做耐克阿迪鞋网站,wordpress分享视频,巴中建设机械网站,linux vps wordpress1. 前言
在tcp套接字编程这里#xff0c;我们将完成两份代码#xff0c;一份是基于tcp实现普通的对话#xff0c;另一份加上业务#xff0c;client输入要执行的命令#xff0c;server将执行结果返回给client
2. tcp_echo_server
与udp类似#xff0c;前两步#xff1…1. 前言
在tcp套接字编程这里我们将完成两份代码一份是基于tcp实现普通的对话另一份加上业务client输入要执行的命令server将执行结果返回给client
2. tcp_echo_server
与udp类似前两步创建socket文件与bindIP和port到内核不同的是socket函数第二个参数在tcp这里我们使用SOCK_STREAM表示提供面向字节流、可靠的传输 在udp中接下来我们就可以让client与server通信了但由于tcp是面向连接的因此需要等待client连接在连接之前需要将sockfd设置为监听状态
#include sys/types.h
#include sys/socket.hint listen(int sockfd, int backlog); # backlog 通常为4/8/16服务器启动后需要不断获取新连接
#include sys/types.h
#include sys/socket.hint accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);accept返回一个文件描述符与socket返回的文件描述符相比两者的关系就像餐馆里的服务员与餐馆外的迎宾员迎宾员通过各种说辞将客人(也就是连接)引致餐馆交给服务员后转头又去迎接下一位客人真正为客人提供服务的是餐馆里的服务员
这里的服务员就是accept返回的文件描述符它才是真正的sockfd迎宾员就是socket的返回值也称为监听套接字(listensockfd) 对两者加以区分后我们也将原来的命名加以修改且由于后续需要用到listensocfd将它设置为成员变量 服务器启动不断accept新连接当获取一个连接失败时进行获取下一个连接好比迎宾员的邀请收到了你的冷落它转头又去邀请下一位客人当有连接到来时交给sockfd给连接提供服务 在提供服务时server首先读到来自client发送的信息需要注意的时tcp的读写使用recv/sendudp的读写使用recvfrom/sendto
#include sys/types.h
#include sys/socket.hssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
# flags默认设为0在这里由于tcp是面向字节流的读到的数据不一定是一个完整的报文可能是半个也可能是多个需要我们进行处理具体的细节在后面的文章中会详讲这边就当作一个完整的数据
收到消息后再给client回复如果recv的返回值为0表示client退出了类似于管道当读端读到0表示写端关闭了 在client方面创建好socket文件之后应该要bind自身IP和port但上一篇文章说过不需要显示bindserver在等待连接client则需要去连接当连接成功时OS会自动bind好client的IP和port
#include sys/types.h
#include sys/socket.hint connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);连接成功后就能和server正常通信了 当然目前的代码缺点也很明显它只能为一个client提供服务由于client的服务是长服务而我们只有一个进程当第二个client来了server在服务第一个进程获取不上连接 2.1 多进程版本
当获取到新连接时创建子进程让子进程去提供服务父进程等待我们知道父子进程之间独立子进程会继承父进程的pcb包括文件描述符表子进程继承了父进程的listensockfd和sockfd 父进程获取新连接拿到的sockfd子进程会以写时拷贝的方式独有一份此时父进程继续去获取连接不会使用该sockfd二我们知道文件描述符的个数是有上限的父进程占着该文件描述符不用可能会导致文件描述符泄漏的问题因此父进程要关闭sockfd
子进程只需要从父进程继承下来的sockfd即可因此子进程最好关闭listensockfd 父进程在waitpid这里是阻塞等待我们希望父进程继续去获取下一个连接可以直接对SIGCHID进行忽略这样子进程退出自动回收
这里使用的方法是子进程再创建孙子进程让后退出父进程直接waitpid成功继续获取下一个连接孙子进程由于子进程先退变成孤儿进程被系统领养释放的工作由系统完成 2.2 多线程版本
当主线程获取到新连接时创建新线程让新线程去执行服务
这里的问题是
如何调用TcpServer中的Service函数如何将sockfd和addr传给Service函数
我们建立一个内部类存放TcpServer的指针和需要传递的变量使用该类创建实例传给线程函数在线程函数中调用
同时为了不让主线程对新线程join而阻塞将新线程分离 注意这里不能使用上面一种方式创建实例否则会因为释放两次ThreadData对象而导致client退出时server崩溃 2.3 线程池版本
利用之前的线程池将Service函数作为任务交给线程池 3. command_server
我们想实现当客户端发送一条指令给服务器时服务器执行该指令并将执行结果返回给客户端服务器只负责读取数据如何处理数据交给业务板块也就是做到IO与业务解耦
我们知道linux下的命令是创建子进程由子进程执行再将执行结果返回给bash这里也类似当服务器收到一条指令需要创建子进程由子进程处理
我们有现成的接口可以直接调用
#include stdio.hFILE *popen(const char *command, const char *type);
# 父进程向管道读type设为rpopen内部会创建管道和子进程由子进程执行命令再将执行结果写入管道我们服务器就只需要向管道读取即可返回值是一个封装的FILE*类型 我们还可以限制客户端能够执行的命令 为什么要让服务器帮我们执行命令我们直接在linux的命令行解释器下直接执行不就行了实际上在云服务器中我们的指令不是在我们本主机上执行的都是交给一个叫sshd的服务远程执行再将执行结果返回的 因此上面的代码能帮我们更好的了解云服务器的指令执行原理
4. 支持断线重连的客户端
有时因为网络问题导致与服务器的连接断开了每次都要我们重新启动客户端太麻烦了我们将客户端改成能够断线后重连当重连一定次数还是无法连接服务器此时才退出 [NetWork/Lesson2/2. command_server · baiyahua/Linux - 码云 - 开源中国](https://gitee.com/baiyahua/linux/tree/master/NetWork/Lesson2/2. command_server)