linux下c语言编程,使用socket发送文件和数据

这个程序使用的是多线程机制+缓冲机制。

服务端会创建一个子线程来对应一个客户端,这样就可以实现多个客户端同时在线,客户端会有一个队列,用于循环读取的文件的缓冲机制。

这个程序默认是把接受的文件放在当前目录下的。

服务端:

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<memory.h>
#include<time.h>
#include<string.h>
#include<sys/wait.h>
#include<signal.h>
#include<time.h>
#include<sys/time.h>
#include<pthread.h>
#include<semaphore.h>
#define SERV_PORT 8888
#define SOCK_COUNT 30
#define MAXSIZE 1000
#define BUF_COUNT 10
#define MSG_FILENAME 1
#define MSG_CONTINUE 2
#define MSG_ACK 3
#define MSG_DONE 4
#define MSG_EXCEPTION 5
//消息结构体
struct msg
{
    int  type;
    int len;
    char data[];
};


int main(int argc,char *argv[])
{
    int sockfd,new_fd;
    struct sockaddr_in servaddr,cliaddr;
    int sin_size,numbytes;
    pid_t pid;
    struct timeval start;
    struct timeval end;
    char strptr[16];
    //创建socket
    if((sockfd = socket(AF_INET,SOCK_STREAM,0))<0)
    {
        perror("socket error");
        exit(-1);
    }
    //初始化socket结构体
    memset(&servaddr,0x00,sizeof(servaddr));
    memset(&cliaddr,0x00,sizeof(cliaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    //避免僵死进程
    void sig_child(int signo)
    {
        pid_t pid;
        int stat;
        while((pid = waitpid(-1,&stat,WNOHANG))>0)
        {
            printf("chlid %d terminated\n",pid);
        }
        return;
    }
    //绑定套接口
    if(bind(sockfd,(struct sockaddr *)&servaddr,sizeof(struct sockaddr)) == -1)
    {
        perror("bind error");
        exit(-1);
    }
    //创建监听套接口
    if(listen(sockfd,SOCK_COUNT) == -1)
    {
        perror("listen error");
        exit(-1);
    }
    //等待连接
    //printf("0-1\n");
    while(1)
    {
        sin_size = sizeof(struct sockaddr_in) ;
        if((new_fd = accept(sockfd,(struct sockaddr *)&cliaddr,(socklen_t *)&sin_size)) == -1)
        {
            perror("accept error");
            exit(-1);
        }
        //创建子进程
        if((pid=fork()) == 0)
        {
            int head=0;//队头指针
            int rear=0;//队尾指针
            struct msg *buf_ptr[BUF_COUNT];//缓冲区队列
            sem_t count;//已经使用的缓冲区
            sem_t empty_count;//未使用的缓冲区
            int i;
            close(sockfd);
            struct msg *sm,*rm;
            pthread_t ptid1,ptid2;
            rm=(struct msg *)malloc(MAXSIZE+sizeof(struct msg));
            sm=(struct msg *)malloc(MAXSIZE+sizeof(struct msg));
            //初始化确认信息
            sm->type = MSG_ACK;
            sm->len = 0;
            gettimeofday(&start,NULL);
            //将消息结构体,拷贝进缓冲区
            void cpy_sm_to_buf(struct msg *sm)
            {

                sem_wait(&empty_count);
                memset(buf_ptr[rear],0x00,MAXSIZE);
                memcpy(buf_ptr[rear],sm,sizeof(struct msg)+sm->len);
                //更新队列状态
                rear = (rear+1)%BUF_COUNT;
                //控制线程2退出
                sem_post(&count);
            }
            //生产者,接收数据并放入缓存区
            void* producer(void *arg)
            {
                int th_new_fd= *(int *)arg;
                int  producer_flag = MAXSIZE;
                while( producer_flag != 0)
                {
                    if((numbytes = recv(th_new_fd,(void *)rm,sizeof(struct msg),0)) == -1)
                    {
                        perror("recv error");
                        exit(-1);
                    }
                    if(numbytes == 0)//判断客户端是否断开
                    {
                        printf("客户端已断开!\n");
                        exit(-1);
                    }
                    producer_flag = rm->len;

                    if((rm->type == MSG_DONE)||(rm->type == MSG_EXCEPTION))
                    {
                        cpy_sm_to_buf(rm);
                        break;
                    }
                    memset(rm->data,0x00,MAXSIZE);
                    if((numbytes = recv(th_new_fd,rm->data,rm->len,0)) == -1)
                    {
                        perror("recv error");
                        exit(-1);
                    }
                    cpy_sm_to_buf(rm);
                }
                free(rm);
                pthread_exit(NULL);
            }
            //消费者,将缓存区的内容写入文件
            void* consumer()
            {
                FILE *fp;
                int consume_flag = MAXSIZE;
                while(consume_flag != 0)
                {
                    sem_wait(&count);
                    consume_flag = buf_ptr[head]->len;
                    if(buf_ptr[head]->type == MSG_FILENAME)
                    {
                        if(buf_ptr[head]->len>256)
                        {
                            printf("文件名过长!\n");
                            exit(-1);
                        }
                        char file_name[256];
                        memset(file_name,0x00,256);
                        memcpy(file_name,buf_ptr[head]->data,buf_ptr[head]->len);
                        if((fp = fopen(file_name,"w+")) == NULL)
                        {
                            perror("fopen error");
                            exit(-1);
                        }
                    }
                    else if(buf_ptr[head]->type == MSG_CONTINUE)
                    {
                        if(fwrite(buf_ptr[head]->data,1,buf_ptr[head]->len,fp)<0)
                        {
                            printf("fwrite error\n");
                            exit(-1);
                        }
                    }
                    else if(buf_ptr[head]->type == MSG_DONE)
                    {
                        printf("send MSG_DONE!\n");
                        break;
                    }
                    else if(buf_ptr[head]->type == MSG_EXCEPTION)
                    {
                        printf("read the file failed!%d\n",buf_ptr[head]->type);
                        exit(-1);
                    }
                    else
                    {
                        printf("文件类型错误!rm->type=%d\n",buf_ptr[head]->type);
                        exit(-1);
                    }
                    //更新队列状态
                    head = (head+1)%BUF_COUNT;
                    sem_post(&empty_count);
                }
                fclose(fp);
                pthread_exit(NULL);
            }
            //为存放数据的缓冲去分配内存
            for(i=0; i<BUF_COUNT; i++)
            {
                buf_ptr[i]=(struct msg *)malloc(MAXSIZE+sizeof(struct msg));
            }
            if(sem_init(&count,0,0) == -1)
            {
                perror("sem_init error");
                exit(-1);
            }
            if(sem_init(&empty_count,0,BUF_COUNT) == -1)
            {
                perror("sem_init error");
                exit(-1);
            }
            gettimeofday(&start,NULL);
            pthread_create(&ptid1,NULL,producer,&new_fd);
            pthread_create(&ptid2,NULL,consumer,NULL);
            pthread_join(ptid1,NULL);
            pthread_join(ptid2,NULL);
            gettimeofday(&end,NULL);
            sem_destroy(&count);
            sem_destroy(&empty_count);

            printf("the time of receiving the file is %ld\n",(end.tv_sec - start.tv_sec)*1000000+(end.tv_usec - start.tv_usec));
            close(new_fd);
            free(sm);
            //释放缓冲队列
            for(i=0; i<BUF_COUNT; i++)
            {
                free(buf_ptr[i]);
            }
            exit(0);
        }
        //通过信号,防止僵尸进程
        signal(SIGCHLD,sig_child);
        //服务器端打印客户端的网址、端口号
        if(inet_ntop(AF_INET,&cliaddr.sin_addr,strptr,(socklen_t)sizeof(strptr)) == NULL)
        {
            perror("convert error");
            exit(-1);
        }
        printf("connect from %s,port is %d,pid is %d\n",strptr,ntohs(cliaddr.sin_port),pid);
        close(new_fd);
    }
    close(sockfd);
    return 0;
}


客户端:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include<string.h>
#include <unistd.h>
#include <errno.h>
#include<time.h>
#include<pthread.h>
#include<sys/time.h>
#include<semaphore.h>
#define SERV_PORT 8888//服务端口号
#define MAXSIZE 1000//缓存队列可以存储的最大数据
#define BUF_COUNT 2//缓存去的个数
#define MSG_FILENAME 1
#define MSG_CONTINUE 2
#define MSG_ACK 3
#define MSG_DONE 4
#define MSG_EXCEPTION 5
//消息结构体
struct msg
{
    int type;
    int len;
    char data[];
};
sem_t count;//已经使用的缓冲区
sem_t empty_count;//未使用的缓冲区
int my_count;
int head;//队头指针
int rear;//队尾指针
struct msg *buf_ptr[BUF_COUNT];//缓冲区队列
//将消息结构体,拷贝进缓冲区
void cpy_sm_to_buf(struct msg *sm)
{
    sem_wait(&empty_count);
    memset(buf_ptr[rear],0x00,MAXSIZE);
    memcpy(buf_ptr[rear],sm,sizeof(struct msg)+sm->len);
    //更新队列状态
    rear = (rear+1)%BUF_COUNT;
    sem_post(&count);
}
//生产者,读取文件并放入缓存区
void* producer(void *arg)
{
    char *filepath=(char *)arg;
    FILE *fp;
    int i,j;
    int thread_exit_flag=0;
    int datalen;
    int produce_flag;
    struct msg *sm;
    sm = (struct msg*)malloc(MAXSIZE+sizeof(struct msg));
    //由文件路径获取文件名
    char file_name[256];
    memset(file_name,0x00,256);
    for(i=strlen(filepath)-1; i>=0; i--)
    {
        if(filepath[i] == '/')
        {
            break;
        }
    }
    for(j=i+1; j<strlen(filepath); j++)
    {
        file_name[j-i-1]=filepath[j];
    }
    sm->type = MSG_FILENAME;
    sm->len = strlen(file_name);
    memset(sm->data,0x00,MAXSIZE);
    memcpy(sm->data,file_name,sm->len);//将文件名拷贝到消息中
    cpy_sm_to_buf(sm);
//打开文件
    if((fp=fopen(filepath,"r")) == NULL)
    {
        perror("fopen error");
        exit(-1);
    }
//循环读取文件
    produce_flag = MAXSIZE;
    while(produce_flag == MAXSIZE)
    {
        sm->type = MSG_CONTINUE;
        memset(sm->data,0x00,MAXSIZE);
        if((datalen=fread(sm->data,1,MAXSIZE,fp)) == 0)
        {
            if(feof(fp))
            {
                printf("注意,文件为空!\n");
            }
            else
            {
                sm->type = MSG_EXCEPTION;
                sm->len = 0;
                memset(sm->data,0x00,MAXSIZE);
                cpy_sm_to_buf(sm);
                produce_flag = datalen;
                printf("fread error\n");
                exit(-1);
            }
        }
        sm->len = datalen;
        cpy_sm_to_buf(sm);
        produce_flag = datalen;
    }
    sm->type = MSG_DONE;
    sm->len = 0;
    memset(sm->data,0x00,MAXSIZE);
    cpy_sm_to_buf(sm);
    fclose(fp);
    free(sm);
    pthread_exit(NULL);
}
//消费者,将缓存区的内容发送到服务端
void* consumer(void *arg)
{
    int consufd = *(int *)arg;
    int i;
    int consume_flag = MAXSIZE;
    while(consume_flag != 0)
    {
        sem_wait(&count);
        //向服务端发送文件名消息
        consume_flag = buf_ptr[head]->len;
        if((send(consufd,buf_ptr[head],sizeof(struct msg)+buf_ptr[head]->len,0)) == -1)
        {
            perror("send error");
            exit(-1);
        }
        //更新队列状态
        head = (head+1)%BUF_COUNT;
        sem_post(&empty_count);
    }
    pthread_exit(NULL);
}
int main(int argc,char *argv[])
{
    if(argc!=3)
    {
        printf("%s: input IP & filemane\n",argv[0]);
        return 1;
    }
    int i;
    head = 0;
    rear = 0;
    struct sockaddr_in their_addr;
    pthread_t ptid1,ptid2;
    int sockfd,numbytes;
    struct timeval start;
    struct timeval end;
    struct hostent *he;
    struct msg *rm;
    rm = (struct msg*)malloc(sizeof(struct msg)+MAXSIZE);
    //为存放数据的缓冲去分配内存
    for(i=0; i<BUF_COUNT; i++)
    {
        buf_ptr[i]=(struct msg *)malloc(MAXSIZE+sizeof(struct msg));
    }
    //将基本名字和地址转换
    he = gethostbyname(argv[1]);
    //建立一个TCP套接口
    if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        perror("socket");
        exit(1);
    }
    //初始化结构体
    their_addr.sin_family = AF_INET;
    their_addr.sin_port = htons(SERV_PORT);
    their_addr.sin_addr =*((struct in_addr *)he->h_addr);
    bzero(&(their_addr.sin_zero),8);
    //和服务端建立连接
    if(connect(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr))==-1)
    {
        perror("connect");
        printf("%s\n",strerror(errno));
        exit(1);
    }
    if(sem_init(&count,0,0) == -1)
    {
        perror("sem_init error");
        exit(-1);
    }
    if(sem_init(&empty_count,0,BUF_COUNT) == -1)
    {
        perror("sem_init error");
        exit(-1);
    }
    gettimeofday(&start,NULL);
    pthread_create(&ptid1,NULL,producer,argv[2]);
    pthread_create(&ptid2,NULL,consumer,&sockfd);
    pthread_join(ptid1,NULL);
    pthread_join(ptid2,NULL);
    gettimeofday(&end,NULL);
    sem_destroy(&count);
    sem_destroy(&empty_count);
    printf("the time of sending the file is %ld\n",(end.tv_sec - start.tv_sec)*1000000+(end.tv_usec - start.tv_usec));
    //接受服务端发送的确认消息
    if((numbytes = recv(sockfd,(void *)rm,sizeof(struct msg),0)) == -1)
    {
        perror("recv error");
        exit(-1);
    }
    printf("send MSG_DONE!\n");
    close(sockfd);
    //释放缓冲队列
    for(i=0; i<BUF_COUNT; i++)
    {
        free(buf_ptr[i]);
    }
    free(rm);
    return 0;
}



由于使用到了多线程,编译的时候使用:gcc xxx.c -o xxx -lpthread

编译后使用:./server和./client 127.0.0.1文件路径即可运行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值