最近學習了linux關于行程間通信的相關知識,所以決定借助行程和共享記憶體,并按照生產者消費者模型來創建一個簡易聊天程式,下面簡單的說一下程式的思路,
首先是服務端程式,服務端會創建兩個行程,行程1負責接收客戶端傳送過來的訊息,并存盤起來,行程2負責讀取行程1存取的訊息,這里使用到了生產者和消費者編程模型,并宣告了如下的訊息存放結構體,
struct message { int target_id; char buf[100]; }; //訊息倉庫 struct canku { struct message recv_message[10]; int read_pos,write_pos; }messages;
可以看到,我在這里強制訊息倉庫最多只能存放10條訊息(其實只能存放9條,和生產者消費者模型有關),
生產者和消費者模型:利用read_pos和write_pos來分別標記讀取和寫入的位置,每次讀取和寫入后,read_pos或write_pos都會遞增1,當read_pos或write_pos到達陣列末尾時,就將其重置為0,類似于一個環形鏈表,可這會出現一個問題:如何判斷鏈表是否為空,或鏈表是否已經被寫滿了?
利用read_pos(訊息讀取的位置)和write_pos(訊息寫入的位置)來判斷訊息倉庫是否為空和已滿,訊息倉庫在空和滿兩種狀態時,read_pos與write_pos都相等,這樣明顯不滿足我們的要求,所以,空出一個訊息位,來利用如下條件判斷訊息是否慷訓滿,
- 訊息為空時
if((messages_ptr->read_pos)%10==messages_ptr->write_pos) { //printf("buff is empty\n"); shmdt(messages_ptr); continue; }
即當read_pos和write_pos值相等時,則表示訊息倉庫為空狀態, - 訊息倉庫已滿時
if((messages_ptr->write_pos+1)%10==messages_ptr->read_pos) { shmdt(messages_ptr); continue; }
即當write_pos下一個位置為read_pos時,此時倉庫已滿,可以看到,該訊息空間無法存盤資料,被浪費了,這樣的方法就解決了如何判斷訊息倉庫空狀態和滿狀態的問題,但是卻浪費了一個訊息空間,
同時,我們還借助于共享記憶體,將訊息倉庫映射到共享記憶體上,這樣,行程1和行程2都可以訪問訊息倉庫,
客戶端比服務端簡單,我創建了一個子行程用來讀取服務端轉發的其他客戶端發來的訊息,父行程則用來讀取客戶輸入,將訊息發送到服務器,
代碼如下:
服務端:
#include <stdio.h> #include <pthread.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <netinet/in.h> #include <errno.h> #define MAX_LISTEN 10 struct message { int target_id; char buf[100]; }; //訊息倉庫 struct canku { struct message recv_message[10]; int read_pos,write_pos; }messages; //messages初始化 void init() { messages.read_pos=0; messages.write_pos=0; } //messages銷毀 void finish() { messages.read_pos=0; messages.write_pos=0; } int sd; int main() { init(); struct sockaddr_in server_ip,customer_ip; int err; sd=socket(AF_INET,SOCK_STREAM,0); if(sd==-1) { printf("socket failed\n"); close(sd); return -1; } //server_ip初始化 server_ip.sin_family=AF_INET; server_ip.sin_port=htons(5678); server_ip.sin_addr.s_addr=htonl(INADDR_ANY); memset(server_ip.sin_zero,0,8); err=bind(sd,(struct sockaddr *)(&server_ip),sizeof(struct sockaddr)); if(err==-1) { printf("bind failed\n"); close(sd); return -1; } err=listen(sd,MAX_LISTEN); if(err==-1) { printf("listen failed\n"); close(sd); return -1; } int length=sizeof(customer_ip); //初始化共享變數,大小等于canku by luke int shmid=shmget(IPC_PRIVATE,sizeof(struct canku),IPC_CREAT|0777); if(shmid<0) { printf("shmget failed\n"); return -1;; } struct canku * messages_ptr=&messages; while(1) { int temp_cd=accept(sd,(struct sockaddr *)(&customer_ip),&length); if(temp_cd==-1) { printf("accept failed,ereno: %d\n",temp_cd); close(sd); return -1; } printf("user %d online\n",temp_cd); pid_t pid=fork(); if(pid==0)//子行程 by luke { while(1) { messages_ptr=shmat(shmid,NULL,0); if((messages_ptr->write_pos+1)%10==messages_ptr->read_pos) { shmdt(messages_ptr); continue; } struct message temp_message; err=recv(temp_cd,&temp_message,sizeof(struct message),0); //err=read(temp_cd,&(recv_message[count-1]),sizeof(struct message)); if(err!=-1) { messages_ptr->recv_message[messages_ptr->write_pos].target_id=temp_message.target_id; strcpy(messages_ptr->recv_message[messages_ptr->write_pos].buf,temp_message.buf); printf("recv: read_pos: %d, write_pos: %d, target_id: %d, buf: %s\n",messages_ptr->read_pos,messages_ptr->write_pos+1,messages_ptr->recv_message[messages_ptr->write_pos].target_id,messages_ptr->recv_message[messages_ptr->write_pos].buf); messages_ptr->write_pos++; if(messages_ptr->write_pos==9) messages_ptr->write_pos=0; } shmdt(messages_ptr); } } //防止主執行緒被while住,無法接受新的連接請求, pid_t pid1=fork(); if(pid1==0) { while(1) { messages_ptr=shmat(shmid,NULL,0); if((messages_ptr->read_pos)%10==messages_ptr->write_pos) { //printf("buff is empty\n"); shmdt(messages_ptr); continue; } //strcpy(messages_ptr->recv_message[messages_ptr->read_pos].buf,"hello"); err=send(messages_ptr->recv_message[messages_ptr->read_pos].target_id,messages_ptr->recv_message[messages_ptr->read_pos].buf,100,0); if(err==-1) { //printf("send failed\n"); } else { printf("send: read_pos: %d, write_pos: %d ,message.target_id: %d, message.buf: %s\n",messages_ptr->read_pos+1,messages_ptr->write_pos,messages_ptr->recv_message[messages_ptr->read_pos].target_id,messages_ptr->recv_message[messages_ptr->read_pos].buf); messages_ptr->read_pos++; if(messages_ptr->read_pos==9) messages_ptr->read_pos=0; } shmdt(messages_ptr); } } } close(sd); shmctl(shmid,IPC_RMID,NULL); finish(); return 0; }
客戶端:
#include <stdio.h> #include <pthread.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> struct message { int target_id; char buf[100]; }; int sd; struct message send_message; void * read_message(void * argv) { while(1) { //讀服務器發來的訊息 char revBuf[100]; read(sd,revBuf,100); printf("recevice from server: %s",revBuf); } } void * write_message(void * argv) { while(1) { printf("input message: \n"); memset(send_message.buf,0,128); send_message.target_id=-1; scanf("%d %s",&send_message.target_id,send_message.buf); write(sd,&send_message,sizeof(send_message)); sleep(3); } } int main() { struct sockaddr_in server_ip,customer_ip; int err; sd=socket(AF_INET,SOCK_STREAM,0); if(sd==-1) { printf("socket failed\n"); close(sd); return -1; } //server_ip初始化 server_ip.sin_family=AF_INET; server_ip.sin_port=htons(5678); server_ip.sin_addr.s_addr=htonl(INADDR_ANY); //err=inet_aton("115.157.201.179",&server_ip.sin_addr.s_addr); memset(server_ip.sin_zero,0,8); err=connect(sd,(struct sockaddr *)(&server_ip),sizeof(server_ip)); if(err==-1) { printf("connect failed\n"); close(sd); return -1; } pid_t pid=fork(); if(pid==0) { while(1) { //讀服務器發來的訊息 //printf("read message: \n"); char revBuf[100]; recv(sd,revBuf,100,0); //read(sd,revBuf,100); printf("recevice from server: %s\n",revBuf); } } while(1) { printf("input message: \n"); memset(send_message.buf,0,128); send_message.target_id=-1; scanf("%d %s",&send_message.target_id,send_message.buf); if(send_message.target_id!=-1&&(strcmp(send_message.buf,"")!=0)) { err=send(sd,&send_message,sizeof(send_message),0); if(err==-1) { printf("send failed\n"); } //write(sd,&send_message,sizeof(send_message)); send_message.target_id=-1; memset(send_message.buf,0,sizeof(send_message.buf)); } sleep(3); } close(sd); return 0; }
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/154251.html
標籤:Linux
