想注册一个设计网站吗,北京知名大公司有哪些,qq推广功能在哪开,wordpress能建论坛吗基础版的服务端虽然基本实现了服务器的基本功能#xff0c;但是如果客户端的并发量比较大的话#xff0c;服务端的压力和性能就会大打折扣,为了提升服务端的并发性能#xff0c;可以通过fork子进程的方式#xff0c;为每一个连接成功的客户端fork一个子进程#xff0c;这样… 基础版的服务端虽然基本实现了服务器的基本功能但是如果客户端的并发量比较大的话服务端的压力和性能就会大打折扣,为了提升服务端的并发性能可以通过fork子进程的方式为每一个连接成功的客户端fork一个子进程这样既达到了并发的要求还能达到客户端隔离的效果。
一、fork
1.1、头文件
#include sys/types.h
#include unistd.h
1.2、原型
pid_t fork(void);fork() 是一个系统调用函数用于在 Unix-like 操作系统中创建一个新的进程。它会复制当前进程称为父进程并在新的进程称为子进程中继续执行。
fork() 函数返回的是一个 pid_t 类型的值其含义如下
在父进程中fork() 返回新创建的子进程的进程 IDPID。在子进程中fork() 返回 0。如果创建子进程失败fork() 返回 -1。 fork() 函数在创建子进程时会返回两次这是因为它是一个复制当前进程的系统调用。具体来说fork() 函数会创建一个新的进程子进程并将父进程的所有内容包括代码、数据、堆栈等复制到子进程中。
第一次返回
在父进程中fork() 返回新创建的子进程的进程 IDPID。如果创建子进程失败fork() 返回 -1。
第二次返回
在子进程中fork() 返回 0。
通过这两次返回父进程和子进程可以根据不同的返回值采取不同的逻辑分支。
在父进程中可以根据返回的子进程 PID 做一些与子进程相关的操作如记录子进程的 PID、等待子进程的终止等。
在子进程中由于 fork() 返回的是 0可以根据此特性来区分自己是子进程从而执行特定的子进程代码逻辑。
需要注意的是父进程和子进程会继续执行 fork() 调用之后的代码并且它们是在不同的进程上下文中运行的拥有各自独立的内存空间和资源。因此在使用 fork() 创建子进程时通常需要在父子进程中进行不同的处理以避免竞态条件和不必要的资源共享问题。
1.3、代码实现
#include iostream
//socket
#include sys/types.h
#include sys/socket.h
//close
#include unistd.h
//exit
#include stdlib.h
//perror
#include stdio.h
//memset
#include string.h
//htons
#include arpa/inet.h#define PORT 8596
#define MESSAGE_SIZE 1024int main(){int ret-1;int socket_fd-1;int accept_fd-1;int backlog10;int flag1;int pid;struct sockaddr_in local_addr,remote_addr;//create socketsocket_fdsocket(AF_INET,SOCK_STREAM,0);if(socket_fd -1){perror(create socket error);exit(1);}//set option of socketret setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, flag, sizeof(flag));if ( ret -1 ){perror(setsockopt error);}//set socket addresslocal_addr.sin_familyAF_INET;local_addr.sin_porthtons(PORT);local_addr.sin_addr.s_addrINADDR_ANY;bzero((local_addr.sin_zero),8);//bind socketretbind(socket_fd, (struct sockaddr *)local_addr,sizeof(struct sockaddr_in));if(ret -1){perror(bind socket error);exit(1);}retlisten(socket_fd, backlog);if(ret -1){perror(listen error);exit(1);}//loop to accept clientfor(;;){socklen_t addrlen sizeof(remote_addr);accept_fdaccept(socket_fd,( struct sockaddr *)remote_addr, addrlen);pidfork();//子进程if(pid0){char in_buf[MESSAGE_SIZE]{0,};for(;;){memset(in_buf,0,MESSAGE_SIZE);//read dataret recv(accept_fd, (void*)in_buf, MESSAGE_SIZE, 0);pid_t currentID getpid();std::cout Current Process ID: currentID std::endl;if(ret 0){break;}printf(receive data:%s\n,in_buf);send(accept_fd, (void *)in_buf, MESSAGE_SIZE, 0);}printf(close client connection......);close(accept_fd);}}if(pid !0){printf(quit server....);close(socket_fd);}return 0;
}
1.4、实现效果
1.4.1、客户端1 1.4.2、服务端接收客户端1 1.4.3、客户端2 1.4.4、服务端接收客户端2 可以看到服务端对于两个客户端的处理是在不同的子进程中进行的。
使用 fork() 函数创建子进程的服务器有以下优点和缺点:
优点
简单易用使用 fork() 函数创建子进程的服务器相对简单不需要使用复杂的多线程或多进程编程模型。通过复制父进程的内存空间子进程可以独立运行处理客户端请求。高并发处理每个客户端连接都可以创建一个独立的子进程这样服务器能够同时处理多个客户端请求实现高并发性能。数据共享父进程和子进程共享文件描述符可以轻松共享一些资源和状态信息例如打开的文件、缓冲区等。可靠性由于每个子进程是独立运行的一个子进程的崩溃或异常不会影响其他子进程或主服务器进程。
缺点
内存开销每个子进程都需要复制父进程的内存空间因此在大规模并发的情况下服务器的内存开销会比较大。进程切换开销由于每个客户端连接都需要创建子进程因此涉及到进程之间的切换开销包括上下文切换和进程间通信开销这可能对服务器性能产生一定的影响。可伸缩性由于每个客户端连接都需要创建子进程服务器的可伸缩性可能受到限制。在大规模并发情况下为每个连接创建子进程可能会导致系统资源耗尽。 进程间通信复杂性如果子进程之间需要进行通信或共享数据就需要使用进程间通信IPC机制如管道、共享内存等。这增加了编程的复杂性。 综上所述使用 fork() 函数创建子进程的服务器适用于简单的并发场景和较小规模的应用但在大规模高并发、资源消耗较大或需要更高可伸缩性的情况下可能需要考虑其他并发模型如多线程或事件驱动模型。