1.并发服务器简介
前面学习流socket编程时,服务器每次只能处理一个客户端,这个访问完了之后再去处理下一个,而在上一节中,通过listen()函数建立一个访问队列,服务器挨个处理每个客户端的访问。这种服务器就叫迭代型服务器,为了同时处理多个客户端,就可以用并发型服务器。
-
迭代型服务器:服务器每次只处理一个客户端,只有当完全处理完一个客户端的请求后才去处理下一个客户端。
-
并发型服务器:这种类型的服务器能够同时处理多个客户端的请求。
2.预先创建进程的并发服务器
为了处理可能存在多个客户端访问,可以在服务器端预先创建多个子进程,每个子进程同时运行等待客户端的访问,只要同时访问的客户端小于提前创建的进程,那么服务器就可以同时处理客户端的访问。每个子进程处理完请求后,并不会终止,而是获取下一个客户端的访问,但这种模式会造成资源的浪费。
将上一节流socket服务器的代码稍微改变,用fork()创建16个进程,运行会打印16个"waiting..."信息,即16个进程同时运行,打开客户端程序,服务器其中一个子进程处理客户该访问后,继续处于等待访问状态。
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#define PORT_ID 6666
#define SIZE 100
int main(void)
{
int sockfd, client_sockfd, i;
struct sockaddr_in my_addr, client_addr;
int addr_len;
char welcome[SIZE] = "Welcome to connect to the sever!";
//初始化服务器的地址
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(PORT_ID);
my_addr.sin_addr.s_addr = INADDR_ANY; //可以接收任意客户端访问
//创建socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);//create a socket
//AF_INET:IPv4; SOCK_STREAM:byte stream
//绑定socket
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
//监听客户端访问,队列最大为10
listen(sockfd, 10);
//10 : listening queue length is 10
addr_len = sizeof(struct sockaddr);
for(i = 0; i < 4; i++)
fork();
while(1)
{
printf("Server is waiting for client to connect:\n");
//允许并接收客户端的访问
client_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);
printf("Client IP address = %s\n", inet_ntoa(client_addr.sin_addr));
send(client_sockfd, welcome, SIZE, 0);//发送数据
printf("Disconnect the client request.\n");
close(client_sockfd);
}
close(sockfd);
return 0;
}
3.预先创建线程的并发服务器
前面介绍了在服务器上预先创建进程,虽然它很快,但是无论是否有客户端访问,所有进程都一直运行着,这必然造成资源浪费,除了这种方式,还可以预先创建线程,我主进程一直运行等待客户端的访问,当有访问时,我创建一个进程去处理这个访问,访问结束后线程也结束,下一个访问来我再创建重新创建线程。
将上一节的流服务器修改如下,当主进程用accept()函数接收客户端的访问后,线程pthread去处理该访问,它接着等待下一个访问,线程处理完访问后,直接关闭线程。
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <unistd.h>
#define PORT_ID 6666
#define SIZE 100
void *thread_function(void *arg);
struct sockaddr_in client_addr;
int client_sockfd;
char welcome[SIZE] = "Welcome to connect to the sever!";
int main(void)
{
int sockfd;
struct sockaddr_in my_addr;
int addr_len;
pthread_t pthread;
//init my_addr
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(PORT_ID);
my_addr.sin_addr.s_addr = INADDR_ANY; //any ip addr
sockfd = socket(AF_INET, SOCK_STREAM, 0);//create a socket
//AF_INET:IPv4; SOCK_STREAM:byte stream
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
listen(sockfd, 10);
//10 : listening queue length is 10
addr_len = sizeof(struct sockaddr);
while(1)
{
printf("Server is waiting for client to connect:\n");
client_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len);
pthread_create(&pthread, NULL, thread_function, NULL);
}
close(sockfd);
return 0;
}
void *thread_function(void *arg)
{
printf("Client IP address = %s\n", inet_ntoa(client_addr.sin_addr));
send(client_sockfd, welcome, SIZE, 0);
printf("Disconnect the client request.\n");
close(client_sockfd);
pthread_exit(NULL); //over pthread
return NULL;
}