主线程往多队列里放new_fd
子线程从多队列里取new_fd
1、主线程收到连接后,将new_fd放入队列,发送signal
2、队列为空,子线程进行wait
3、子线程均去取new_fd,需要进行加解锁
head.h //服务器 | func.h //客户端 |
#ifndef __FUNC_H__
#define __FUNC_H__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <pthread.h>
#endif
|
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>
//传输数据使用的结构体
typedef struct{
int len;
char buf[1000];
}data,*pdata;
#endif
|
work_que.h | work_que.c |
#ifndef __WOEK_QUE_H__
#define __WOEK_QUE_H__
#include "head.h"
typedef struct node{
int new_fd;
struct node* pnext;
}Node,*pNode;
typedef struct{
pNode phead,ptail;
pthread_mutex_t mutex;
int size;
}que_t,*pque_t;
void que_init(pque_t); //初始化队列
void que_insert(pque_t,int); //把连接到服务器生成的new_fd加入队列
void que_get(pque_t,int*); //从队列中取到new_fd
#endif
|
#include "work_que.h"
void que_init(pque_t pq)
{
pq->phead=NULL;
pq->ptail=NULL;
pthread_mutex_init(&pq->mutex,NULL);
pq->size=0;
}
void que_insert(pque_t pq,int fd)
{
pNode pnew=(pNode)calloc(1,sizeof(Node));
pnew->new_fd=fd;
pthread_mutex_lock(&pq->mutex);
if(pq->phead==NULL)
{
pq->phead=pnew;
pq->ptail=pnew;
}else{
pq->ptail->pnext=pnew;
pq->ptail=pnew;
}
(pq->size)++;
pthread_mutex_unlock(&pq->mutex);
}
void que_get(pque_t pq,int* fd) //外面已经加锁,所以这里不用再加锁
{
pNode pcur;
pcur=pq->phead;
*fd=pcur->new_fd;
pq->phead=pcur->pnext;
free(pcur); //尽量把malloc和free放到锁外面
if(pq->phead == NULL)
{
pq->ptail=NULL;
}
(pq->size)--;
pcur=NULL;
}
|
factory.h | factory.c |
#ifndef __FACTORY_H__
#define __FACTORY_H__
#include "head.h"
#include "work_que.h"
#define FILENAME "hello.avi"
typedef void* (*pfunc)(void*); //typedef一个函数指针类型
typedef struct{
pthread_t* thread; //线程数组id首地址
int th_num; //线程数
pthread_cond_t cond; //同步条件变量
int capacity; //最多可同时链接的客户端
que_t que; //new_fd队列
pfunc func_sendfile; //线程函数指针
int flag; //防止重复启动服务器,重复建立线程
}factory,*pf;
typedef struct{
int len;
char buf[1000];
}data,*pdata;
void factory_init(pf,int,int,pfunc); //工厂数据结构初始化
void factory_start(pf); //工厂启动
void* child_thread(void*); //线程函数
#endif
|
#include "factory.h"
void factory_init(pf p,int num,int cap,pfunc func_sendfile)
{
p->thread=(pthread_t*)calloc(num,sizeof(pthread_t));
p->th_num=num;
pthread_cond_init(&p->cond,NULL);
p->capacity=cap;
que_init(&p->que);
p->func_sendfile=func_sendfile;
p->flag=0;
}
void factory_start(pf p)
{
int i;
if(p->flag ==0)
{
for(i=0;i<p->th_num;i++)
{
pthread_create(&p->thread[i],NULL,p->func_sendfile,(void*)p);
}
}
p->flag=1;
}
void* child_thread(void* p)
{
pf p1=(pf)p;
int new_fd;
while(1)
{
pthread_mutex_lock(&p1->que.mutex);
if(p1->que.size==0)
{
pthread_cond_wait(&p1->cond,&p1->que.mutex);
}
que_get(&p1->que,&new_fd);
pthread_mutex_unlock(&p1->que.mutex);
send_file(new_fd); //发送文件
}
}
|
send_file.c | pool_n.c |
#include "factory.h"
void send_file(int fdw)
{
data d;
memset(&d,0,sizeof(d));
d.len=strlen(FILENAME);
strcpy(d.buf,FILENAME);
int ret;
ret=send(fdw,&d,4+d.len,0); //发送文件名
if(-1==ret)
{
perror("send");
exit(-1);
}
int fd=open(FILENAME,O_RDONLY);
if(-1==fd)
{
perror("open");
exit(-1);
}
while(memset(&d,0,sizeof(d)),(d.len=read(fd,d.buf,sizeof(d.buf)))>0)
{
send_n(fdw,&d,4+d.len);
}
ret=0;
send_n(fdw,&ret,sizeof(int));//发送文件结束标志
close(fdw);
}
|
#include "head.h"
void send_n(int new_fd,char* buf,int len)
{
int ret;
int total=0;
while(total<len)
{
ret=send(new_fd,buf+total,len-total,0);
total=total+ret;
}
}
void recv_n(int new_fd,char* buf,int len)
{
int ret;
int total=0;
while(total<len)
{
ret=recv(new_fd,buf+total,len-total,0);
total=total+ret;
}
}
|
main.c | tcp_client.c |
#include "factory.h"
int main(int argc,char* argv[])
{
if(argc!=5)
{
printf("error args\n");
return -1;
}
int num=atoi(argv[3]);
int cap=atoi(argv[4]);
factory f;
factory_init(&f,num,cap,child_thread);
factory_start(&f);
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2])); //一定要用htons
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
//给sfd绑定IP地址和端口号
ret=bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
if(-1==ret)
{
perror("bind");
return -1;
}
listen(sfd,cap);
int new_fd;
while(1)
{
new_fd=accept(sfd,NULL,NULL);
if(-1==new_fd)
{
perror("accept");
return -1;
}
que_insert(&f.que,new_fd);
pthread_cond_signal(&f.cond); //唤醒一个子进程
}
}
|
#include "func.h"
int main(int argc,char** argv)
{
if(argc !=3)
{
printf("error args\n");
return -1;
}
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2])); //一定要用htons
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
ret=connect(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
if(-1==ret)
{
perror("connect");
return -1;
}
data d;
memset(&d,0,sizeof(d));
recv_n(sfd,&d.len,sizeof(int)); //读取要接收文件名字的长度
recv_n(sfd,d.buf,d.len); //读取文件名
int fd;
fd=open(d.buf,O_RDWR|O_CREAT,0666);
if(-1==fd)
{
perror("open");
return -1;
}
while(1)
{
memset(&d,0,sizeof(d));
recv_n(sfd,&d.len,sizeof(int));
if(d.len >0)
{
recv_n(sfd,d.buf,d.len);
write(fd,d.buf,d.len);
}else{
break;
}
}
close(sfd);
close(fd);
return 0;
}
|
threadserver:
//func.h
#ifndef __FUNC_H__
#define __FUNC_H__
#include<stdio.h>
#include<string.h>
#include<strings.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/stat.h>
#include<sys/epoll.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<unistd.h>
#include<pthread.h>
#endif
//pool_n.c
#include"thread_msg.h"
void send_n(int new_fd,char *buf,int len)
{
int ret;
int total = 0;
while(total<len)
{
ret = send(new_fd,buf+total,len-total,0); //socket缓冲区是有大小的,接收方和发送方各自的socket缓冲区都是64K,如果接收方接收速度不匹配,那么会导致接收数据方缓冲区满,发送方成功发送的数据不等于预定发送长度;
total += ret;
}
}
void recv_n(int new_fd,char *buf,int len)
{
int ret;
int total = 0;
while(total<len)
{
ret = recv(new_fd,buf+total,len-total,0);
total += ret;
}
}
|
//thread_msg.h
#ifndef __THREAD_MSG_H__
#define __THREAD_MSG_H__
#include"func.h"
#define FILENAME "meihao.mp4"
typedef struct Node
{
int new_fd;
struct Node *next;
}node,*pnode; //客户端链接产的的new_fd,保存起来,待会放到队列
typedef struct
{
pnode head;
pnode tail;
pthread_mutex_t mutex; //操作队列要保证同步,所以互斥锁放到这个结构体里面
int size;
}que,*pque; //队列链表,用来保存链接产生的new_fd
typedef void *(*thread_handle)(void *);
typedef struct
{
pthread_t *thread;
int num;
int capacity;
que que_t;
thread_handle func;
pthread_cond_t cond;
int flag; //这个结构体里面存放服务端启动所需要的所有信息,启动服务端初始化一次就好了,所以设置一个flag防止多次启动初始化;
}factory,*pfactory; //这个结构体让程序可以不用分开定义多个变量,还有子线程只要传入结构体指针就可以访问main所有信息,不用把这些信息全部设置为全局变量
typedef struct
{
int len;
char buf[1024];
}data,*pdata;
void factory_init(pfactory f,int num,int capacity,thread_handle thread_func);
void factory_start(pfactory f);
void que_init(pque que_t);
void que_insert(pque que_t,int new_fd);
void que_get(pfactory f,int *new_fd);
void *thread_func(void *p);
void send_n(int new_fd,char *buf,int len);
void recv_n(int new_fd,char *buf,int len);
void sendfile(int new_fd);
#endif
|
//thread_msg.c
#include"thread_msg.h"
void factory_init(pfactory f,int num,int capacity,thread_handle thread_func)
{
f->thread = (pthread_t *)calloc(num,sizeof(pthread_t));
f->num = num;
f->capacity = capacity;
que_init(&f->que_t);
f->func = thread_func;
pthread_cond_init(&f->cond,NULL);
f->flag = 0;
}
void que_init(pque que_t)
{
que_t->head = NULL;
que_t->tail = NULL;
pthread_mutex_init(&que_t->mutex,NULL);
que_t->size = 0;
}
void *thread_func(void *p)
{
pfactory f = (pfactory)p;
int new_fd;
while(1)
{
pthread_mutex_lock(&f->que_t.mutex);
if(f->que_t.size==0)
{
pthread_cond_wait(&f->cond,&f->que_t.mutex);
}
que_get(f,&new_fd);
pthread_mutex_unlock(&f->que_t.mutex);
sendfile(new_fd);
}
}
void factory_start(pfactory f)
{
int i ;
if(f->flag==0)
{
for(i=0;i<f->num;i++)
{
pthread_create(&f->thread[i],NULL,thread_func,(void*)f);
}
}
f->flag=1;
}
void que_insert(pque que_t,int new_fd)
{
pthread_mutex_lock(&que_t->mutex);
pnode pnew = (pnode)calloc(1,sizeof(node));
pnew->new_fd = new_fd;
if(que_t->head==NULL)
{
que_t->head = pnew;
que_t->tail = pnew;
}
que_t->tail->next = pnew;
que_t->tail = pnew;
(que_t->size)++;
pthread_mutex_unlock(&que_t->mutex);
}
void que_get(pfactory f,int *new_fd)
{
pnode pcur = f->que_t.head;
*new_fd = pcur->new_fd;
f->que_t.head = f->que_t.head->next;
if(f->que_t.head==NULL)
{
f->que_t.tail = NULL;
}
(f->que_t.size)--;
free(pcur);
pcur = NULL;
}
|
//send_file.c #include"thread_msg.h"
void sendfile(int new_fd)
{
data d;
memset(&d,0,sizeof(d));
d.len = strlen(FILENAME);
strcpy(d.buf,FILENAME);
int ret = send(new_fd,&d,4+d.len,0); //先发送文件名给客户端,客户端获得这个文件名建立一个新的相同名字的文件;文件名的长度存放在d.len里面,所以发送长度就为4+d.len;
if(-1==ret)
{
perror("send");
return ;
}
int fdr = open(FILENAME,O_RDONLY);
if(-1==fdr)
{
perror("open");
return ;
}
while(memset(&d,0,sizeof(d)),(d.len=read(fdr,d.buf,sizeof(d.buf)))>0)
{
send_n(new_fd,(char*)&d,4+d.len);
}
int flag = 0; //标志这边已经发送完文件里,客户端可以断开连接了
send(new_fd,&flag,sizeof(int),0);
close(new_fd);
close(fdr);
}
#include"thread_msg.h"
int main(int argc,char **argv)
{
if(5!=argc)
{
printf("error argcs\n");
return -1;
}
factory f;
factory_init(&f,atoi(argv[3]),atoi(argv[4]),thread_func);
factory_start(&f);
int sfd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in ser;
bzero(&ser,sizeof(ser));
ser.sin_port = htons(atoi(argv[2]));
ser.sin_addr.s_addr = inet_addr(argv[1]);
ser.sin_family = AF_INET;
int ret = bind(sfd,(struct sockaddr*)&ser,sizeof(ser));
if(-1==ret)
{
perror("bind");
return -1;
}
ret = listen(sfd,atoi(argv[4]));
if(-1==ret)
{
perror("listen");
return -1;
}
int new_fd;
while(1)
{
new_fd = accept(sfd,NULL,NULL);
if(-1==new_fd)
{
printf("accept error\n");
return -1;
}
que_insert(&f.que_t,new_fd);
pthread_cond_signal(&f.cond);
}
return 0;
}
|
threadclient:
//func.h
#ifndef __FUNC_H__
#define __FUNC_H__
#include<stdio.h>
#include<string.h>
#include<strings.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/stat.h>
#include<sys/epoll.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<unistd.h>
#include<pthread.h>
#endif
|
//thread_msg.h
#ifndef __THREAD_MSG_H__
#define __THREAD_MSG_H__
#include"func.h"
typedef struct
{
int len;
char buf[1024];
}data,*pdata;
void send_n(int new_fd,char *buf,int len);
void recv_n(int new_fd,char *buf,int len);
#endif
|
//pool_n.c
#include"thread_msg.h"
void send_n(int new_fd,char *buf,int len)
{
int ret;
int total = 0;
while(total<len)
{
ret = send(new_fd,buf+total,len-total,0); //socket缓冲区是有大小的,接收方和发送方各自的socket缓冲区都是64K,如果接收方接收速度不匹配,那么会导致接收数据方缓冲区满,发送方成功发送的数据不等于预定发送长度;
total += ret;
}
}
void recv_n(int new_fd,char *buf,int len)
{
int ret;
int total = 0;
while(total<len)
{
ret = recv(new_fd,buf+total,len-total,0);
total += ret;
}
}
先建立一个thread_msh.h文件,里面都是要用到的数据结构,因为线程共享所有进程的资源,所以把要用到的数据全部放到一个结构体里面,这样创建子线程的时候就可以直接传一个结构体指针,然后子线程就能得到所有数据信息,为了防止其它函数方法不小心再次定义初始化这些数,加了一个flag,整个结构体信息只用初始化一次,大项目要考虑这个。线程池和进程池不一样的就是进程指定子进程个数要和监听的数一样,不然如果进程都忙的话客户端就链接不上;但是线程池不一样,他可以链接多了,如果子线程都忙的话客户端最多在拿等待;所以可以创建n个子线程,完了链接num(num>n)个客户端;main线程accept到客户端之后把new_fd放到队列,子线程的任务就是一直在run,去队列取走new_fd发送文件.没有new_fd就睡眠,等待有新的new_fd,然后pthread_cond_signal唤醒;
|
//client.c
#include"thread_msg.h"
int main(int argc,char **argv)
{
if(3!=argc)
{
printf("error args\n");
return -1;
}
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
struct sockaddr_in ser;
bzero(&ser,sizeof(ser));
ser.sin_family = AF_INET;
ser.sin_port = htons(atoi(argv[2]));
ser.sin_addr.s_addr = inet_addr(argv[1]);
int ret = connect(sfd,(struct sockaddr *)&ser,sizeof(ser));
if(-1==ret)
{
perror("connect");
return -1;
}
data d;
bzero(&d,sizeof(d));
recv(sfd,&d.len,sizeof(int),0);
recv(sfd,&d.buf,d.len,0);
printf("file name is %s\n",d.buf);
int fdw = open(d.buf,O_WRONLY|O_CREAT,0666);
if(-1==fdw)
{
perror("open");
return -1;
}
while(1)
{
memset(&d,0,sizeof(d));
recv_n(sfd,(char*)&d.len,sizeof(int));
if(d.len>0)
{
recv_n(sfd,d.buf,d.len);
write(fdw,d.buf,d.len);
}
else
{
break;
}
}
memset(&d,0,sizeof(d));
ret = recv(sfd,&d.len,sizeof(int),0);
//printf("ret =%d\n",ret);
close(sfd);
close(fdw);
return 0;
}
|