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

来源:互联网 发布:北京和隆优化科技 编辑:程序博客网 时间:2024/05/20 23:33

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

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

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

服务端:

#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文件路径即可运行

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 租房签了一年合同想走怎么办 京东寄包裹在速递易里面该怎么办 翼码科技辅助码被删掉了怎么办 用别人的身份证注册的手机号怎么办 大v线做到假线了怎么办 有个摄像头网段不同搜不到怎么办 百度网盘离线下载有违规内容怎么办 抖音上传的照片与音乐不同步怎么办 社保卡里面的钱用完了怎么办 医保卡里面的钱用完了怎么办 手机通知栏变成了搜索栏怎么办 小米手机账号密码忘了手机号怎么办 暑假工没签合同押工资了的怎么办 在日本雅虎拍卖网站买到假货怎么办 没有百度糯米账号但是买票了怎么办 拼多多商家版登陆密码忘记了怎么办 安卓下载的软件闪退怎么办 老司机影视院开通会员看不了怎么办 梦幻西游手游苹果手机打不开怎么办 拼多多笔订单被判定虚假发货怎么办 苹果手机微信接收不到信息怎么办 内衣穿32太小穿34空杯怎么办 楼下的把下水管道堵死了怎么办?? 微信绑定的手机号码已停机怎么办 绑定微信的手机号停机了怎么办验证 绑定手机停机微信怎么验证码怎么办 五菱之光雨刷开关手柄断了怎么办 快手家长控制模式密码忘了怎么办 有陌生人要求你拉他入群怎么办 手机一天接到多个陌生电话怎么办 阳光惠生活登录密码忘了怎么办 一个身份证注册了两个支付宝怎么办 买手办拆了发现坏了怎么办 六个月宝宝喝贝因美过敏怎么办 超市盘点少的货扣员工工资怎么办 孩子东西在学校被偷了怎么办 想退出却不舍的感情不知怎么办? 我被老板摸全身失眠有阴影怎么办 不知道绑定支付宝的微博账号怎么办 银行卡丢了补办后支付宝账户怎么办 未激活的银行卡被锁定了怎么办