1.多进程并发服务器
子进程来源于父进程,通俗的来讲,它是父进程的一个复制品。子进程完全复制了父进程的资源,包括进程上下文、内存信息、工作目录等信息。同时操作系统也会给子进程分配内存空间和调度资源。父进程和子进程的区分主要是靠fork()函数的返回值来进行。
-
1.return 0 ;成功创建子进程,进入到子进程中相关操作
-
2.return-1;创建子进程失败
-
3.return (子进程)pid; 也即是(>0)进入到父进程,返回值为子进程的pid;此时可进入到父进程相关操作
在Linux中实现多进程并发服务器,值得注意的一个问题就是子进程的回收问题,为了避免僵尸进程的出现,占用大量的系统资源,此处用signal()信号中断处理函数进行回收。
#include <sys/socket.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<sys/types.h>
#include<strings.h>
#include<sys/wait.h>
#include"wrap.h"
#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666
//"192.168.32.30"
void *wait_child(int signo)//子进程回收函数
{
while (waitpid(0,NULL,WNOHANG) > 0);
return ;
}
int main(void)
{
pid_t pid;
int sfd,cfd;
struct sockaddr_in serv_addr , clie_addr;
socklen_t clie_addr_len,serv_addr_len;
memset(&serv_addr,0,sizeof(serv_addr));
memset(&clie_addr,0,sizeof(clie_addr));
char buf[BUFSIZ];
char clie_IP[BUFSIZ];
int n;
int i;
sfd = Socket(AF_INET,SOCK_STREAM,0);
serv_addr.sin_family = AF_INET;//ipv4
serv_addr.sin_port = htons(SERV_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//inet_pton(AF_INET,"192.168.32.30",&serv_addr.sin_addr.s_addr);
serv_addr_len = sizeof(serv_addr);
Bind(sfd, (struct sockaddr*)&serv_addr, serv_addr_len);
Listen(sfd,128);
printf("wait for client connect");
while(1)//创建子进程
{
clie_addr_len = sizeof(clie_addr);
cfd = Accept(sfd,(struct sockaddr*)&clie_addr,&clie_addr_len);
printf("cfd = %d\n",cfd);
printf("cliet IP:%s,cliet port:%d\n",
inet_ntop(AF_INET,
&clie_addr.sin_addr.s_addr,
clie_IP,
sizeof(clie_IP)),
ntohs(clie_addr.sin_port));
pid = fork();
if (pid <0)
{
perror("fork error");
exit(1);
}
else if(pid ==0)
{
close(sfd);
break;
}else{
close(cfd);
signal(SIGCHLD,wait_child);
}
}
if(pid ==0)//子进程操作
{
while(1)
{
n = Read(cfd,buf,sizeof(buf));
if(n == 0)
{
close(cfd);
return 0;
}else if(n == -1)
{
perror("read error");
exit(1);
{
perror("read error");
exit(1);
}else
{
for(i = 0;i < n;i++)
{
buf[i] = toupper(buf[i]);
}
write(cfd,buf,n);
write(STDOUT_FILENO,buf,n);
}
}
}
return 0;
}
2.多线程并发服务器
多线程和多进程之间的区别在不过多讲述,一句话“进程是资源分配的最小单位,线程是CPU调度的最小单位”,就表明了最大的区别在与系统资源利用效率的不同。多进程占用内存多,切换复杂,CPU利用率低,但是它编程相对来比较简单,容易调试,同时可靠性也比多线程高,毕竟多个进程之间是不会产生影响的;而多线程占用内存少,切换简单,CPU利用率高,是开发高性能并发系统的首选。
Linux 多线程主要是用 pthread 函数来实现的。主要用到:
1.pthread_create()函数,其原型如下:
pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参数依次为:线程id,线程参数,入口函数,线程入口函数的参数名字
特别注意的是(void *)类型指针,表示无类型指针,在传递的时候需要强转。
#include <sys/socket.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
#include<pthread.h>
#include<fcntl.h>
#include"wrap.h"
#define MAXSIAE 10240
#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666
//"192.168.32.30"
/*自定义一个结构体,将sockaddr_in与cfd捆绑*/
struct s_info{
struct sockaddr_in clie_addr;
int connectfd;
};
void *do_work(void *arg)//子线程处理函数
{
int n,i;
char buf[MAXSIAE];
char str[INET_ADDRSTRLEN];//INET_ADDRSTRLEN 16
struct s_info *ts = (struct s_info*)arg;
while(1)
{
n = Read(ts->connectfd,buf,MAXSIAE);//读客户端
if(n == 0)
{
printf("the cline %d closed!",ts->connectfd);
break;
}
for(i = 0;i < n;i++)
{
buf[i] = toupper(buf[i]);
}
write(ts->connectfd,buf,n);
write(STDOUT_FILENO,buf,n);
}
Close(ts->connectfd);
int main(void)
{
struct sockaddr_in serv_addr , clie_addr;
int sfd,cfd;
socklen_t clie_addr_len,serv_addr_len;
char clie_IP[BUFSIZ];
int i = 0;
pthread_t tid;//创建线程
struct s_info ts[256];//最大线程数创建结构体数组
sfd = Socket(AF_INET,SOCK_STREAM,0);
bzero(&serv_addr,sizeof(serv_addr));//地址结构初始化
serv_addr.sin_family = AF_INET;//ipv4
serv_addr.sin_port = htons(SERV_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//inet_pton(AF_INET,"192.168.32.30",&serv_addr.sin_addr.s_addr);
serv_addr_len = sizeof(serv_addr);
Bind(sfd, (struct sockaddr*)&serv_addr, serv_addr_len);
//printf("wait for client connect:::");
Listen(sfd,128);
printf("wait for client connect::\n");
while(1)
{
clie_addr_len = sizeof(clie_addr);
cfd = Accept(sfd,(struct sockaddr*)&clie_addr,&clie_addr_len);//阻塞监听
printf("cfd = %d\n",cfd);
ts[i].clie_addr = clie_addr;//取出接收到的客户端addr结构体
ts[i].connectfd = cfd;//文件描述符
//传递到pthead_create 子线程执行
//线程管理,当线程达到最大数时候进行处理
pthread_create(&tid,NULL,do_work,(void*)&ts[i]);
pthread_detach(tid);//子线程分离,防止僵尸线程产生
i++;
}
return 0;
}
注意:本简易服务器另外采用wrap错误处理函数,为了使得主要代码逻辑更加清晰