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
- linux下c语言编程,使用socket发送文件和数据
- LINUX C下socket编程第一次发送数据出错
- linux下使用C语言接收和发送udp组播数据分别怎么写?
- Linux下C语言socket编程
- Linux下C语言socket编程
- Linux下C语言socket网络编程
- Linux下C语言Socket编程
- linux下C语言 socket tcp编程
- Linux下发送与接收组播数据(socket编程)
- Linux下发送与接收组播数据(socket编程)
- Linux下发送与接收组播数据(socket编程)
- Linux C语言编程-Linux网络通信--Linux上使用套接字(socket)来发送信息---知识点总结+实例
- linux下c语言使用libcurl和实现文件上传
- C语言Linux下的Socket 编程(UDP)
- C语言Linux下的Socket 编程(TCP)
- linux下C语言socket网络编程简例
- linux下C语言socket网络编程简例
- linux下C语言socket网络编程简例
- 2015年终总结
- RedirectResult/RedirectToRouteResult类型
- android webview 篇
- ListView下拉刷新,上拉加载
- [Spring实战系列](9)装配集合
- linux下c语言编程,使用socket发送文件和数据
- 素数(质数)4
- vim使用教程(转自菜鸟教程)
- sdk(安卓)
- 反汇编---汇编基础学习
- 判断语言环境
- 学习笔记3 Supervised Neural Networks
- Android 三大图片缓存原理、特性对比
- 学习笔记------数据结构(C语言版)栈应用 表达式求值