php 多进程socket通信代码中的那个for循环是怎么结束的

本帖子已过去太久远了,不再提供回复功能。linux(13)
TCP 迭代服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接。TCP 迭代服务器一次只能处理一个客户端的请求,只有在这个客户的所有请求满足后,服务器才可以继续后面的请求。如果有一个客户端占住服务器不放时,其它的客户机都不能工作了,因此,TCP 服务器一般很少用迭代服务器模型的。
tcp服务器端框架
1.创建tcp套接字
绑定套接字
监听套接字
调用accept()阻塞等待
处理客户端的请求
关闭连接套接字
关闭监听套接字
tcp客户端框架
1.创建tcp套接字
2.调用connect()连接服务器
3.处理服务器端返回的信息
由于客户端不需要固定的端?号,因此不必调?bind(),客户端的端?号由内核?动分配。注意, 客户端不是不允许调?bind(),只是没有必要调?bind()固定?个端?号,服务器也不是必须调?bind(),但如果服务器不调?bind(),内核会?动给服务器分配监听端?,每次启动服务器时端? 号都不?样,客户端要连接服务器就会遇到?烦。
#include&stdio.h&
#include&sys/socket.h&
#include&netinet/in.h&
#include&errno.h&
#include&unistd.h&
#include&string.h&
#include&sys/types.h&
#include&arpa/inet.h&
#include&netinet/in.h&
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock&0)
printf("socket()\n");
struct sockaddr_in server_
struct sockaddr_
bzero(&server_socket,sizeof(server_socket));
server_socket.sin_family=AF_INET;
server_socket.sin_addr.s_addr=htonl(INADDR_ANY);
server_socket.sin_port=htons(_PORT_);
if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))&0)
printf("bind()\n");
close(sock);
if(listen(sock,_BACKLOG_)&0)
printf("listen()\n");
close(sock);
printf("success\n");
socklen_t len=0;
int client_sock=accept(sock,(struct sockaddr*)&socket,&len);
if(client_sock&0)
printf("accept()\n");
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip,'\0',sizeof(buf_ip));
inet_ntop(AF_INET,&socket.sin_addr,buf_ip,sizeof(buf_ip));
printf("get connect\n");
char buf[1024];
memset(buf,'\0',sizeof(buf));
read(client_sock,buf,sizeof(buf));
printf("client:# %s\n",buf);
printf("server:$ ");
memset(buf,'\0',sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strncasecmp(buf,"quit",4)==0)
printf("quit\n");
write(client_sock,buf,strlen(buf)+1);
printf("wait...\n");
close(client_sock);
close(sock);
怎么将单进程的代码改为多进程的代码呢?
调用fork()函数,创建子进程,将所有的客户端的请求处理的内容都放在子进程中处理。
在 Linux 环境下多进程的应用很多,其中最主要的就是网络/客户服务器。多进程服务器是当客户有请求时,服务器用一个子进程来处理客户请求。父进程继续等待其它客户的请求。这种方法的优点是当客户有请求时,服务器能及时处理客户,特别是在客户服务器交互系统中。对于一个 TCP 服务器,客户与服务器的连接可能并不马上关闭,可能会等到客户提交某些数据后再关闭,这段时间服务器端的进程会阻塞,所以这时操作系统可能调度其它客户服务进程,这比起循环服务器大大提高了服务性能。
#include&stdio.h&
#include&sys/socket.h&
#include&netinet/in.h&
#include&errno.h&
#include&unistd.h&
#include&string.h&
#include&sys/types.h&
#include&arpa/inet.h&
#include&netinet/in.h&
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock&0)
printf("socket()\n");
struct sockaddr_in server_
struct sockaddr_
bzero(&server_socket,sizeof(server_socket));
server_socket.sin_family=AF_INET;
server_socket.sin_addr.s_addr=htonl(INADDR_ANY);
server_socket.sin_port=htons(_PORT_);
if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))&0)
printf("bind()\n");
close(sock);
if(listen(sock,_BACKLOG_)&0)
printf("listen()\n");
close(sock);
printf("success\n");
socklen_t len=0;
int client_sock=accept(sock,(struct sockaddr*)&socket,&len);
if(client_sock&0)
printf("accept()\n");
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip,'\0',sizeof(buf_ip));
inet_ntop(AF_INET,&socket.sin_addr,buf_ip,sizeof(buf_ip));
printf("get connect\n");
pid_t fd=fork();
printf("fork()\n");
close(sock);
printf("port=%d,ip=%s\n",ntohs(socket.sin_port),buf_ip);
char buf[1024];
memset(buf,'\0',sizeof(buf));
read(client_sock,buf,sizeof(buf));
printf("client:# %s\n",buf);
printf("server:$ ");
memset(buf,'\0',sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strncasecmp(buf,"quit",4)==0)
printf("quit\n");
write(client_sock,buf,strlen(buf)+1);
printf("wait...\n");
close(fd);
else if(fd&0)
close(fd);
close(sock);
多线程和多进程的处理方式类似,都是创建一个新的线程,客户端有请求时,用新创建的线程处理。
#include&stdio.h&
#include&sys/socket.h&
#include&netinet/in.h&
#include&errno.h&
#include&unistd.h&
#include&string.h&
#include&sys/types.h&
#include&arpa/inet.h&
#include&netinet/in.h&
#define _PORT_ 9999
#define _BACKLOG_ 10
void *fun(void* arg)
int client_sock = (int)
char buf[1024];
memset(buf,'\0',sizeof(buf));
read(client_sock,buf,sizeof(buf));
printf("client:# %s\n",buf);
printf("server:$ ");
memset(buf,'\0',sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strncasecmp(buf,"quit",4)==0)
printf("quit\n");
write(client_sock,buf,strlen(buf)+1);
printf("wait...\n");
close(client_sock);
int main()
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock&0)
printf("socket()\n");
struct sockaddr_in server_
struct sockaddr_
pthread_t thread_
bzero(&server_socket,sizeof(server_socket));
server_socket.sin_family=AF_INET;
server_socket.sin_addr.s_addr=htonl(INADDR_ANY);
server_socket.sin_port=htons(_PORT_);
if(bind(sock,(struct sockaddr*)&server_socket,sizeof(struct sockaddr_in))&0)
printf("bind()\n");
close(sock);
if(listen(sock,_BACKLOG_)&0)
printf("listen()\n");
close(sock);
printf("success\n");
socklen_t len=0;
int client_sock=accept(sock,(struct sockaddr*)&socket,&len);
if(client_sock&0)
printf("accept()\n");
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip,'\0',sizeof(buf_ip));
inet_ntop(AF_INET,&socket.sin_add,buf_ip,sizeof(buf_ip));
printf("get connect,ip is%s\n",buf_ip);
printf("port=%d\n",ntohs(socket.sin_port));
pthread_create(&thread_id, NULL, (void *)fun, (void *)client_sock);
pthread_detach(thread_id);
close(sock);
不管是哪一种的服务器,客户端都是一样的
#include&stdio.h&
#include&unistd.h&
#include&sys/socket.h&
#include&string.h&
#include&errno.h&
#include&netinet/in.h&
#include&arpa/inet.h&
#include&sys/types.h&
#define SERVER_PORT 9999
int main(int argc,char* argv[])
if(argc!=2)
printf("Usage:client IP\n");
char *str=argv[1];
char buf[1024];
memset(buf,'\0',sizeof(buf));
struct sockaddr_in server_
int sock = socket(AF_INET,SOCK_STREAM,0);
bzero(&server_sock,sizeof(server_sock));
server_sock.sin_family=AF_INET;
inet_pton(AF_INET,str,&server_sock.sin_addr);
server_sock.sin_port=htons(SERVER_PORT);
int ret=connect(sock,(struct sockaddr *)&server_sock,sizeof(server_sock));
printf("connect()\n");
printf("connect success\n");
printf("client:# ");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
write(sock,buf,sizeof(buf));
if(strncasecmp(buf,"quit",4)==0)
printf("quit\n");
printf("wait..\n");
read(sock,buf,sizeof(buf));
printf("server:$ %s\n",buf);
close(sock);
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:10325次
排名:千里之外
原创:33篇
(2)(8)(6)(1)(1)(1)(2)(4)(8)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'2013年6月 Linux/Unix社区大版内专家分月排行榜第二2013年5月 Linux/Unix社区大版内专家分月排行榜第二2013年3月 Linux/Unix社区大版内专家分月排行榜第二2013年1月 Linux/Unix社区大版内专家分月排行榜第二2012年12月 Linux/Unix社区大版内专家分月排行榜第二2012年8月 Linux/Unix社区大版内专家分月排行榜第二2011年12月 Linux/Unix社区大版内专家分月排行榜第二2011年10月 C/C++大版内专家分月排行榜第二2011年10月 Linux/Unix社区大版内专家分月排行榜第二
2012年6月 C/C++大版内专家分月排行榜第三2012年6月 PHP大版内专家分月排行榜第三2012年5月 C/C++大版内专家分月排行榜第三2012年3月 Linux/Unix社区大版内专家分月排行榜第三2012年2月 Linux/Unix社区大版内专家分月排行榜第三2011年11月 C/C++大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。在Go程序中实现服务器重启的方法
投稿:goldensun
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了在Go程序中实现服务器重启的方法,由于很多人盲目崇拜谷歌&亲爹&,Go语言在国内有着不寻常的人气,需要的朋友可以参考下
Go被设计为一种后台语言,它通常也被用于后端程序中。服务端程序是GO语言最常见的软件产品。在这我要解决的问题是:如何干净利落地升级正在运行的服务端程序。
&&& 不关闭现有连接:例如我们不希望关掉已部署的运行中的程序。但又想不受限制地随时升级服务。
&&& socket连接要随时响应用户请求:任何时刻socket的关闭可能使用户返回'连接被拒绝'的消息,而这是不可取的。
&&& 新的进程要能够启动并替换掉旧的。
在基于Unix的操作系统中,signal(信号)是与长时间运行的进程交互的常用方法.
&&& SIGTERM: 优雅地停止进程
&&& SIGHUP: 重启/重新加载进程 (例如: nginx, sshd, apache)
如果收到SIGHUP信号,优雅地重启进程需要以下几个步骤:
&&& 服务器要拒绝新的连接请求,但要保持已有的连接。
&&& 启用新版本的进程
&&& 将socket“交给”新进程,新进程开始接受新连接请求
&&& 旧进程处理完毕后立即停止。
停止接受连接请求
服务器程序的共同点:持有一个死循环来接受连接请求:
&&& 代码如下: for {
&&&&& conn, err := listener.Accept()
&&&&& // Handle connection
跳出这个循环的最简单方式是在socket监听器上设置一个超时,当调用listener.SetTimeout(time.Now())后,listener.Accept()会立即返回一个timeout err,你可以捕获并处理:
&&& 代码如下: for {
&&&&& conn, err := listener.Accept()
&&&&& if err != nil {
&&&&&&& if nerr, ok := err.(net.Err); ok && nerr.Timeout() {
&&&&&&&&&& fmt.Println(“Stop accepting connections”)
&&&&&&&&&& return
注意这个操作与关闭listener有所不同。这样进程仍在监听服务器端口,但连接请求会被操作系统的网络栈排队,等待一个进程接受它们。
启动新进程
Go提供了一个原始类型ForkExec来产生新进程.你可以与这个新进程共享某些消息,例如文件描述符或环境参数。
&&& 代码如下: execSpec := &syscall.ProcAttr{
&&&&& Env:&& os.Environ(),
&&&&& Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
&&& fork, err := syscall.ForkExec(os.Args[0], os.Args, execSpec)
&你会发现这个进程使用完全相同的参数os.Args启动了一个新进程。
发送socket到子进程并恢复它
正如你先前看到的,你可以将文件描述符传递到新进程,这需要一些UNIX魔法(一切都是文件),我们可以把socket发送到新进程中,这样新进程就能够使用它并接收及等待新的连接。
但fork-execed进程需要知道它必须从文件中得到socket而不是新建一个(有些兴许已经在使用了,因为我们还没断开已有的监听)。你可以按任何你希望的方法来,最常见的是通过环境变量或命令行标志。
代码如下:listenerFile, err := listener.File()
&&& if err != nil {
&&&&& log.Fatalln("Fail to get socket file descriptor:", err)
&&& listenerFd := listenerFile.Fd()
&&& // Set a flag for the new process start process
&&& os.Setenv("_GRACEFUL_RESTART", "true")
&&& execSpec := &syscall.ProcAttr{
&&&&& Env:&& os.Environ(),
&&&&& Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd(), listenerFd},
&&& // Fork exec the new version of your server
&&& fork, err := syscall.ForkExec(os.Args[0], os.Args, execSpec)
&然后在程序的开始处:
&&& 代码如下: var listener *net.TCPListener
&&& if os.Getenv("_GRACEFUL_RESTART") == "true" {
&&&&& // The second argument should be the filename of the file descriptor
&&&&& // however, a socker is not a named file but we should fit the interface
&&&&& // of the os.NewFile function.
&&&&& file := os.NewFile(3, "")
&&&&& listener, err := net.FileListener(file)
&&&&& if err != nil {
&&&&&&& // handle
&&&&& var bool ok
&&&&& listener, ok = listener.(*net.TCPListener)
&&&&& if !ok {
&&&&&&& // handle
&&& } else {
&&&&& listener, err = newListenerWithPort(12345)
文件描述没有被随机的选择为3,这是因为uintptr的切片已经发送了fork,监听获取了索引3。留意隐式声明问题。
最后一步,等待旧服务连接停止
到此为止,就这样,我们已经将其传到另一个正在正确运行的进程,对于旧服务器的最后操作是等其连接关闭。由于标准库里提供了sync.WaitGroup结构体,用go实现这个功能很简单。
每次接收一个连接,在WaitGroup上加1,然后,我们在它完成时将计数器减一:
&&& 代码如下: for {
&&&&& conn, err := listener.Accept()
&&&&& wg.Add(1)
&&&&& go func() {
&&&&&&& handle(conn)
&&&&&&& wg.Done()
至于等待连接的结束,你仅需要wg.Wait(),因为没有新的连接,我们等待wg.Done()已经被所有正在运行的handler调用。
Bonus: 不要无限制等待,给定限量的时间
&&& 代码如下: timeout := time.NewTimer(time.Minute)
&&& wait := make(chan struct{})
&&& go func() {
&&&&& wg.Wait()
&&&&& wait &- struct{}{}
&&& select {
&&& case &-timeout.C:
&&&&& return WaitTimeoutError
&&& case &-wait:
&&&&& return nil
完整的示例
这篇文章中的代码片段都是从这个完整的示例中提取的:/Scalingo/go-graceful-restart-example
socket传递配合ForkExec使用确实是一种无干扰更新进程的有效方式,在最大时间上,新的连接会等待几毫秒——用于服务的启动和恢复socket,但这个时间很短。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具相关文章推荐
实现基于TCP的C/S服务模型,服务器将客户端的输入回显给客户端。
服务器代码:
服务器#include
#include void Usage(char* proc)
今天介绍的是基于ipv4的socket网络编程,我们知道socket API是一层的抽象的网络编程接口,但各网络协议的地址却是各不相同的。
下图是sockaddr数据结构图:
ipv4和ipv...
*相关软件的版本:
gcc版本:gcc (GCC) 4.1.2
(Red Hat 4.1.2-52)
Linux版本:红帽企业版 5.5
1.概述:在之前的“Linux...
一、在前面讲过的最简单的回射客户/服务器程序中,一个客户端即一个进程,只会发起一个连接,只要稍微修改一下就可以让一个客户端发起多个连接,然后只利用其中一个连接发送数据。
先来认识一个函数getsoc...
他的最新文章
讲师:董晓杰
讲师:姚远
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 websocket 多进程 的文章

 

随机推荐