本篇叙述的TCP并发服务器模型如下图所示:
服务器创建并绑定套接字后fork出几个子进程,子进程中分别进行accept(该函数为阻塞函数)、recv、处理数据然后再次acept,这样循环下去。所有客户端发来的信息都是直接由子进程处理。
例程
代码如下,在处理客户端请求之前,服务器先fork了3个子进程,然后将客户端的请求直接交由子进程处理。
该例程中,服务器fork子进程后,子进程监听并接收客户端的信息,然后打印客户端发来的信息和自己的id(id代表自己是第几个子进程)
服务器端代码:
/**************************************
author:arvik
purpose:test the server simultaneity
email:1216601195@qq.com
csdn: http://blog.csdn.net/u012819339
**************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define BUFFLEN 1024
#define SERVER_PORT 8887
#define BACKLOG 5
#define PIDNUMB 3
static void handle_connect(int s_s, int id)
{
int s_c;
struct sockaddr_in from; //client addr
socklen_t len = sizeof(from);
while(1)
{
s_c = accept(s_s, (struct sockaddr*)&from, &len);
char buff[BUFFLEN];
memset(buff, 0, BUFFLEN);
int n = recv(s_c, buff, BUFFLEN, 0); //non block
if(n > 0)
{
printf("This process id is: %d \nreveive from client: %s\n", id, buff);
}
close(s_c);
}
}
void sig_int(int num)
{
exit(1);
}
int main(int argc, char **argv)
{
int s_s;
struct sockaddr_in local;
signal(SIGINT, sig_int);
s_s = socket(AF_INET, SOCK_STREAM, 0);
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_port = htons(SERVER_PORT);
bind(s_s, (struct sockaddr*)&local, sizeof(local));
listen(s_s, BACKLOG);
pid_t pid[PIDNUMB];
for(int i = 0; i<PIDNUMB; i++)
{
pid[i] = fork();
if(pid[i] == 0)
{
handle_connect(s_s, i);
}
}
sleep(100);
close(s_s);
return 0;
}
客户端代码:
/**************************************
author:arvik
purpose:test the server simultaneity
email:1216601195@qq.com
csdn: http://blog.csdn.net/u012819339
**************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#define BUFFLEN 24
#define SERVER_PORT 8887
int main()
{
int s_c;
struct sockaddr_in server;
char buff[] = "hello";
s_c = socket(AF_INET, SOCK_STREAM, 0);
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY); //any local address
server.sin_port = htons(SERVER_PORT);
connect(s_c, (struct sockaddr*)&server, sizeof(server));
send(s_c, buff, strlen(buff), 0);
sleep(1);
close(s_c);
return 0;
}
到此问个问题,
当一个客户端送来信息后,到底是哪个子进程接收并处理信息呢?服务器端3个子进程会由于争相接收理客户端的连接而都得到连接权(或都得不到连接权)呢?
不知其解,故展开验证:
1. 启动服务器后,我依次启动了客户端(每次都是等服务器处理完上个请求后才启动客户端的),发现三个子进程依次轮流来接收信息。
[root@F12 t1]# ./server
[root@F12 t1]# ./client
This process id is: 0
reveive from client: hello
[root@F12 t1]#
[root@F12 t1]# ./client
This process id is: 1
reveive from client: hello
[root@F12 t1]# ./client
This process id is: 2
reveive from client: hello
[root@F12 t1]# ./client
This process id is: 0
reveive from client: hello
- 同时启动两个客户端
[root@F12 t1]# ./client & ./client
[1] 20805
This process id is: 1
reveive from client: hello
This process id is: 2
reveive from client: hello
[root@F12 t1]#
[1]+ Done ./client
[root@F12 t1]#
- 同时启动四个客户端,这时发现处理消息的服务器子进程没那么规律了
[root@F12 t1]# ./client & ./client & ./client & ./client
[1] 20809
This process id is: 1
reveive from client: hello
This process id is: 1
reveive from client: hello
[2] 20810
[3] 20811
This process id is: 2
reveive from client: hello
This process id is: 1
reveive from client: hello
[1] Done ./client
[2]- Done ./client
[3]+ Done ./client
[root@F12 t1]#
[root@F12 t1]#
[root@F12 t1]# ./client & ./client & ./client & ./client
[1] 20832
[2] 20833
[3] 20834
This process id is: 0
reveive from client: hello
This process id is: 2
reveive from client: hello
This process id is: 1
reveive from client: hello
This process id is: 0
reveive from client: hello
[1] Done ./client
[root@F12 t1]#
[2]- Done ./client
[3]+ Done ./client
[root@F12 t1]#
由试验现象可对上述问题做个简单回答,操作系统在某一时刻会安排服务器子进程中的一个来处理客户端发来的连接,对于每一个客户端的连接,服务器子进程有且只有一个接收并进行处理。