標籤:

小任務的伺服器

小任務的伺服器

#include"allheader.h"int main(int argc, char *argv[]){ //首先需要建立套接字 //綁定套接字相關地址信息 //監聽套接字 int server_sock, client_sock; //char readbuff[MAXSIZE]; int shm_id, str_len = 0, ret, i = 0; int connpid;//存放進行通信的進程的PID int filerecv; char myaddr[] = "127.0.0.1"; pid_t pid;//保存子進程id int intbuff[INTSIZE];//存放子進程的id的數組 char buff[MAXSIZE];//接收消息用的緩存 char str[30];//用來存放複製文件的文件名提示字元串 char filename[NAMESIZE];//用來存放複製文件的文件名 pthread_t pthtid1,pthtid2, pthtid3; char mypid_str[20];//用於存儲個人進程id的字元串 char mq_send_name[30];//用於存儲發送消息的消息隊列的名稱 char mq_recv_name[30];//用於存儲接收消息的消息隊列的名稱 char mq_file_name[30];//用於存儲接收文件消息的消息隊列 mqd_t recv_mqd, send_mqd, filerecv_mqd, filesend_mqd;//用於保存消息隊列的描述符 struct msgpck rcvfrcli, sedtocli, filestr;//子伺服器中的收發線程線程使用的結構體 int child_pid[CHILDNUM];//子進程的id數組 int *ptr = NULL;//共享內存映射地址 char *nameptr = NULL;//文件名的共享內存映射地址 ssize_t length;//保存讀取消息的長度 //sem_t *mutex; struct sockaddr_in server_addr, client_addr; socklen_t client_addr_sz; if(argc != 2) { printf("usage : %s <port>
", argv[0]); exit(0); } //清空初始化緩存 memset(&rcvfrcli.buff, 0, MAXOFMSG); memset(&sedtocli.buff, 0, MAXOFMSG); memset(&filestr.buff, 0, MAXOFMSG); //使用共享內存共享一個父進程伺服器的子進程伺服器pid,從而方便相互的通信操作 shm_id = shm_open(SHMNAME, MY_OFLAG, MY_MODE); ret = ftruncate(shm_id, sizeof(child_pid)); printf("ret = %d
", ret); ptr = mmap(NULL, sizeof(child_pid), MY_MMPROT, MAP_SHARED, shm_id, 0); if(ptr == MAP_FAILED) err_handle("mmap error!
"); close(shm_id); //初始化一個信號量,考慮是否在子進程中使用 //mutex = sem_open("/tengzhiqiang", O_CREAT , MY_MODE, 1); server_sock = socket(PF_INET, SOCK_STREAM, 0); //此處省略了第三個協議參數 if(server_sock == -1) err_handle("socket() error"); memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(atoi(argv[1])); server_addr.sin_addr.s_addr = inet_addr(myaddr); //這裡使用atoi函數會導致參數不正確,即地址轉化不正確,會導致失敗 if(bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) err_handle("bind() error"); if(listen(server_sock, 5) == -1) err_handle("listen() error"); printf("server addr is %s , port is %s
", inet_ntoa(server_addr.sin_addr), argv[1]); //設置循環監聽,實現服務多客戶同時連接 while(1) { client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_addr_sz); if(client_sock == -1) err_handle("accept() error"); rcvfrcli.socket = client_sock; sedtocli.socket = client_sock; filestr.socket = client_sock; pid = fork(); //一旦運行到這裡,就說明伺服器接收到了一個連接,就可以進行創建子進程 if(pid < 0) err_handle("fork() error:"); if(pid == 0) { //首先讀取一個父進程寫的文件,中間包括有哪些客戶在線的信息 close(server_sock); //創建一個存儲待接收的文件名的消息隊列 memset(&mq_file_name, 0, 30); sprintf(mypid_str, "%d", getpid()); strcpy(mq_file_name,PREFIX_MQ); strcat(mq_file_name, mypid_str); filerecv_mqd = mq_open(mq_file_name, MQ_OFLAG, MY_MODE, NULL); //memset(&mq_file_name, 0, 30); filestr.mqd = filerecv_mqd; //創建一個線程,用以讀取待發送的文件名,然後將文件發送給客戶端 ret = pthread_create(&pthtid3, NULL, sendfile, (void *)&filestr); /* 可以將所有功能進行編號處理,利用不同編號進行不同功能的實現 1 獲取可以通信的相關客戶端的pid進程編號 2 進行聊天模式,進入後鍵入相關客戶端的pid 3 文件傳輸功能,進入該模式後鍵入需要傳輸的文件名,然後開始傳輸文件 */ while(1)//循環進行功能的選擇 { length = recv(client_sock, buff, MAXSIZE, 0); //printf("the length of the msg be gotten is %d", length); if(length == 2) { if(buff[0] == 0) { printf("進程%d 已斷開連接!!",getpid()); mq_unlink(mq_file_name); break; } switch (buff[0]) { case 1 : for(i = 0; ptr[i] != 0; i++) { intbuff[i] = ptr[i]; if(intbuff[i] == getpid()) { intbuff[i] = 1;//1代表本身的聯繫方式 } //printf("the useful number is %d
", intbuff[i]); str_len += 4; } //printf("the length of number is %d
", length); send(client_sock, intbuff, str_len, 0); break; case 2 : { length = recv(client_sock, buff, MAXSIZE, 0); connpid = atoi(buff); printf("my pid is %d, my connection pid is %d
", getpid(), connpid); printf("後續會進行通信操作,請稍後
"); //得到通訊的進程pid,建立相應的通信方式 /* 這裡需要注意消息隊列的名稱需要有標識性,使用兩者通信的進程的id命名 發送消息隊列 "/mq_"+ "sendpid"+"_"+"recvpid" 接收消息隊列 "/mq_"+ "recvpid"+"_"+"sendpid" */ buff[length-1] = ; sprintf(mypid_str, "%d", getpid()); strcpy(mq_send_name, PREFIX_MQ); strcat(mq_send_name, mypid_str); strcat(mq_send_name, "_"); strcat(mq_send_name, buff); //printf("the name of mqueue is %s
", mq_send_name); sprintf(mypid_str, "%d", getpid()); strcpy(mq_recv_name, PREFIX_MQ); strcat(mq_recv_name, buff); strcat(mq_recv_name, "_"); strcat(mq_recv_name, mypid_str); //printf("the name of mqueue is %s
", mq_recv_name); //構建發送和接收消息的消息隊列 //如果已經創建消息隊列,不能講消息隊列清空,因為其中可能有消息數據,所以可以使用讀寫打開消息隊列 暫時宏定義為清空消息隊列 send_mqd = mq_open(mq_send_name , MQ_OFLAG, MY_MODE, NULL); if(send_mqd == -1) { printf("send_mqd =1 is %d
", send_mqd); send_mqd = mq_open(mq_send_name, O_WRONLY); printf("send_mqd =1 is %d
", send_mqd); if(send_mqd == -1) perror("send mqueue open error:
"); printf("send_mqd =1 is %d
", send_mqd); //mq_unlink(mq_send_name); } recv_mqd = mq_open(mq_recv_name, MQ_OFLAG, MY_MODE, NULL); if(recv_mqd == -1) { printf("recv_mqd =2 is %d
", recv_mqd); recv_mqd = mq_open(mq_recv_name, O_RDONLY); if(recv_mqd == -1) perror("recv mqueue open error:
"); printf("recv_mqd =2 is %d
", recv_mqd); } rcvfrcli.mqd = send_mqd; sedtocli.mqd = recv_mqd; memset(&buff,0,MAXSIZE); /* 對於接收客戶端發送的數據,和接收消息隊列中的數據,使用的是同一個緩存區,這就會 產生線程不安全,需要加鎖或者使用兩個緩存區 */ ret = pthread_create(&pthtid1, NULL, csend_qrecv, (void*)&rcvfrcli); if(ret != 0) err_handle("recv pthread create fail!
"); ret = pthread_create(&pthtid2, NULL, qsend_crecv, (void*)&sedtocli); if(ret != 0) err_handle("recv pthread create fail!
"); pthread_join(pthtid1, NULL); pthread_join(pthtid2, NULL); mq_unlink(mq_send_name); mq_unlink(mq_recv_name); printf("用戶已離開聊天室!
"); //sleep(2);//測試確保所有線程退出 發送的命令可以安全到達while後的語句 } break; case 3 : /* 這裡的功能是實現文件的傳輸,文件傳輸的方案選擇: sleep */ memset(&str, 0, 30); memset(&filename, 0, NAMESIZE); strcpy(str, "請輸入文件名:
"); ret = send(client_sock, str, strlen(str) + 1, 0);//發送提醒字元串 if(ret == -1) perror("s發送提醒字元失敗"); length = recv(client_sock, filename, NAMESIZE, 0);//接收文件的文件名 filename[length - 1] = ; memset(&str, 0, 30); strcpy(str, "請輸入要發送文件的通信號:"); send(client_sock, str, strlen(str), 0);//發送提醒字元串 length = recv(client_sock, str, 30, 0);//接收通信號 printf("接收到通信號 %s
", str); str[length - 1] = ; strcat(filename, "_from_");//將接收的文件名加上一個通信後綴 strcat(filename, str); filerecv = open(filename, MY_OFLAG, MY_MODE); printf("空文件已經創建,等待讀取文件......
"); //接收文件: while(1) { printf("準備從客戶端獲取文件!
"); length = recv(client_sock, buff, MAXSIZE, 0); printf("從客戶端獲取文件ing......
"); if(length == 3) if((buff[0] == #) && (buff[1] == &))//讀取到結束符,結束文件讀寫 { printf("從客戶端接收文件完畢!
"); break; } write(filerecv, buff, length); } /* 接收完文件應該創建共享內存,將文件名共享出去,方便 接收進程通過文件絕對路徑打開文件,然後進行文件讀寫 */ close(filerecv); //shm_id = shm_open(SHMNAME_FILE, MY_OFLAG, MY_MODE); //ret = ftruncate(shm_id, sizeof(filename)); //printf("ret2 = %d
", ret); //nameptr = mmap(NULL, sizeof(filename), MY_MMPROT, MAP_SHARED, shm_id, 0); //if(nameptr == MAP_FAILED) // err_handle("mmap error!
"); //close(shm_id); //strcpy(nameptr, filename); strcpy(mq_file_name, PREFIX_MQ); strcat(mq_file_name, str); filesend_mqd = mq_open(mq_file_name, O_WRONLY); ret = mq_send(filesend_mqd, filename, strlen(filename) + 1, 0); break; default : break; } } } } else { /* 這裡是父進程的操作 父進程需要的是進行數據的轉發 對於不同的進程使用管道轉發相關的數據 同時需要建立一個鏈表或者數組,存儲不同客戶端的標誌符 利用不同的標識符進行數據轉發,從而滿足不同客戶端進行通信的 這裡需要一個共享內存,用來共享父進程中的不同客戶端的埠號,不同客戶端 之間傳遞的文件信息。 */ ptr[i] = pid; printf("this is parent, my child is %d, i = %d
", ptr[i], i); i++; close(client_sock); } } //printf("server have accepted the client!
"); close(server_sock);}//從客戶端接收數據發送到消息隊列//int csend_qrecv(char *buff, int client_sock, mqd_t send_mqd)int csend_qrecv(void *arg){ int length, ret; unsigned int msgtype ; unsigned int msgsize; char filesendstring[FILEBUFF];//保存文件信息 struct msgpck *source = (void *)arg; memset(&(source->buff), 0, MAXOFMSG); printf("the source1 socket is %d
", source->socket); printf("the source1 mqd is %d
", source->mqd); while(1) { memset(&(source->buff), 0, MAXOFMSG); length = recv(source->socket, source->buff, MAXOFMSG, 0); if(length == -1) //err_handle("recv1 failed!
"); perror("recv1 failed!
"); printf("收到的信息長度為:%d,內容為:%s
", length, source->buff); //write(STDOUT_FILENO, source->buff, length); if(length == 3) { if((source->buff[0] == #) && (source->buff[1] == *)) { printf("1退出聊天室!
"); mq_send(source->mqd, source->buff, length, 0); memset(&filesendstring, 0, FILEBUFF); msgtype = CMDTYPE; msgsize = length + 8; memcpy(&filesendstring[0], &msgtype, sizeof(msgtype)); memcpy(&filesendstring[4], &msgsize, sizeof(msgsize)); memcpy(&filesendstring[8], &source->buff, length); send(source->socket, source->buff, msgsize, 0);//提供結束字元串給客戶端的線程 pthread_exit(NULL); } } ret = mq_send(source->mqd, source->buff, length, 0); if(ret == -1) err_handle("send to queue failed!
"); } }//子伺服器從消息隊列讀取消息並發送給客戶端//int qsend_crecv(char *buff, int client_sock, mqd_t recv_mqd)int qsend_crecv(void *arg){ int ret, length; struct msgpck *source = (void *)arg; unsigned int msgtype ; unsigned int msgsize; char filesendstring[FILEBUFF];//保存文件信息 memset(&(source->buff), 0, MAXOFMSG); printf("the source2 socket is %d
", source->socket); printf("the source2 mqd is %d
", source->mqd); while(1) { //sleep(2); memset(&(source->buff), 0, MAXOFMSG); length = mq_receive(source->mqd, source->buff, MAXOFMSG, NULL); if(length == -1) perror("recv2 failed!
"); printf("the source2 mqd is %d
", source->mqd); if(length == 3) { if((source->buff[0] == #) && (source->buff[1] == *)) { printf("2退出聊天室!
"); pthread_exit(NULL); //break; } } memset(&filesendstring, 0, FILEBUFF); msgtype = CHATDATA; msgsize = length + 8; memcpy(&filesendstring[0], &msgtype, sizeof(msgtype)); memcpy(&filesendstring[4], &msgsize, sizeof(msgsize)); memcpy(&filesendstring[8], &source->buff, length); ret = send(source->socket, filesendstring, msgsize, 0); if(ret == -1) err_handle("write to stdout failed!
"); } }int sendfile(void *arg){ /* 首先需要的是讀取消息隊列中的數據,當消息隊列不為空的時候退出 */ int ret, length, filerecv; unsigned int msgtype ; unsigned int msgsize; long filelength;//用來保存文件的長度 struct msgpck *source = (void *)arg; char namebuf[30]; char numstring[30];//用來填充數據包中的數據 char filesendstring[FILEBUFF];//保存文件信息 char beginofsend[] = BEGINOFSEND; char endofsend[] = ENDOFSEND; memset(&(source->buff), 0, MAXOFMSG); memset(&namebuf, 0, 30); while(1) { length = mq_receive(source->mqd, namebuf, MAXOFMSG, NULL); if(length != -1) break; } filerecv = open(namebuf, O_RDONLY); /* 傳輸消息這裡需要封裝自己的消息數據 */ length = sizeof(beginofsend); memset(&filesendstring, 0, FILEBUFF); msgtype = CMDTYPE; msgsize = sizeof(beginofsend) + 8; memcpy(&filesendstring[0], &msgtype, sizeof(msgtype)); memcpy(&filesendstring[4], &msgsize, sizeof(msgsize)); memcpy(&filesendstring[8], &beginofsend, length); send(source->socket, filesendstring, msgsize, 0);//·"#$" //length = recv(source->socket, source->buff, MAXOFMSG, 0); //write(STDOUT_FILENO, source->buff, length); sleep(2); while(1) { memset(&(source->buff), 0, MAXOFMSG); msgtype = FILETYPE; length = read(filerecv, source->buff, ONERD_FILE); msgsize = length + 8; memset(&filesendstring, 0, FILEBUFF); memcpy(&filesendstring[0], &msgtype, sizeof(msgtype)); memcpy(&filesendstring[4], &msgsize, sizeof(msgsize)); memcpy(&filesendstring[8], &source->buff, length); send(source->socket, filesendstring, msgsize, 0); if(length <= 0) { if(length < 0) err_handle("read recvfile error:
"); else { printf("文件發送完畢!
"); sleep(2); memset(&filesendstring, 0, FILEBUFF); msgtype = CMDTYPE; msgsize = sizeof(endofsend) + 8; memcpy(&filesendstring[0], &msgtype, sizeof(msgtype)); memcpy(&filesendstring[4], &msgsize, sizeof(msgsize)); memcpy(&filesendstring[8], &endofsend, length); send(source->socket, filesendstring, msgsize, 0);//"#&" break; } } } }

推薦閱讀:

如何配置基於伺服器的python和windows下的matlab環境
伺服器為什麼託管到機房更好?
如何查看自己的伺服器的日記?
伺服器維護應該注意要點有哪些?

TAG:伺服器 |