1. 程式人生 > >服務端客戶端通訊,註冊,上線,下線,私聊,廣播(UDP)

服務端客戶端通訊,註冊,上線,下線,私聊,廣播(UDP)

框架:C/S
Client:向伺服器傳送請求訊息
     1、註冊
     2、驗證上線
     3、傳送訊息:廣播訊息/私人訊息(******)
     4、退出訊息(****)
     多程序開發:父程序接收資訊   子程序:傳送訊息
Server:
    1、接收使用者請求
            註冊
           上線
          接收發送訊息請求並轉發 
            退出        
         總結:
         UDP:面向無連線,不安全,不可靠,無狀態的傳輸協議

 

  伺服器實現註冊和上線的協議功能 :

#include<iostream>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<map>
#include<arpa/inet.h>
using namespace std;
#define PATH_MSG "./passwd"
//功能1:新增節點(上線成功)
//功能2:刪除節點(下線)
//功能3:驗證上線
//功能4:判斷訊息功能
//功能5:註冊
//定義結構體:註冊
struct reg
{
	char name[10];
	char pwd[10];
};
//定義結構體:
struct msg
{
	char type;//訊息型別  L--上線 Q:下線  B:廣播訊息  C:聊天(私有) 註冊R:
	long ilen;//內容的長度
};
//前置宣告 
void list(map<string,sockaddr_in> &online);
//儲存到檔案中
bool saveMsg(struct reg cm)
{
	bool state=false;
//1開啟
	int fd=open(PATH_MSG,O_CREAT|O_RDWR,0644);
//2操作
	if(fd<0)
	{
		perror("open fail");
		return state;
	}
	//遍歷查詢
	struct reg  r;
	state=true;
	while(read(fd,&r,sizeof(r))>0)
	{
		if(strcmp(r.name,cm.name)==0)
		{
			state=false;	
			break;
		}
	}
	//查詢不成功:則新增 
	if(state)
	{
		if(write(fd,&cm,sizeof(cm))!=sizeof(cm))
			state=false;
	}
//3關閉
	close(fd);
	return state;
}
//註冊函式
void reg(int sock,struct sockaddr_in caddr)
{
	//接收客戶端傳送的註冊資訊
	struct reg r;
	if(recv(sock,&r,sizeof(r),0)==sizeof(r))
	{
		if(saveMsg(r))
		{
			sendto( 
			sock,"ok",2,0,\
		       (sockaddr*)&caddr,\
		       sizeof(caddr)
		      );	
		}
	}
}
//驗證---上線之前必須驗證
bool check(struct reg m)
{
	bool state=false;
//1對檔案遍歷
	int fd=open(PATH_MSG,O_RDONLY);
	if(fd<0)//開啟失敗
		return state;
	struct reg r;
	while(read(fd,&r,sizeof(r))>0)
	{
		if(!strcmp(r.name,m.name) && !strcmp(r.pwd,m.pwd))
		{
			state=true;
			break;
		}
	}
//3關閉檔案
	close(fd);
	return state;		
}
//上線
bool addOnline(struct reg m,struct sockaddr_in caddr,int sock,map<string,sockaddr_in> &online)
{
	bool state=false;
	//判斷驗證是否通過
	if(check(m))
	{
		//將資料插入到線上列表中
		online.insert(pair<string,struct sockaddr_in> (string(m.name),caddr));
		//返回資訊
		sendto(sock,"ok",2,0,(sockaddr*)&caddr,sizeof(caddr));
		state=true;
	}
	//臨時列印線上人數
	cout<<"線上人數:"<<online.size()<<endl;
	//顯示
	list(online);

	return state;
}
//顯示線上列表:
void list(map<string,sockaddr_in> &online)
{
	map<string,sockaddr_in> ::iterator it;
	it=online.begin();
	while(it!=online.end())
	{
		cout<<"線上:"<<it->first<<endl;
		it++;
	}
}
int main()
{
//1建立套接字
	int sock=-1,pid=-1;
	sock=socket(AF_INET,SOCK_DGRAM,0);	
	if(sock<0)
	{
		perror("socket fail");
		return -1;
	}
//2繫結套接字
	struct sockaddr_in addr;
	addr.sin_family		=AF_INET;	//地址型別
	addr.sin_port		=htons(7979);	//埠
	addr.sin_addr.s_addr	=INADDR_ANY;	//本機地址
	if(bind(sock,(struct sockaddr*)&addr,sizeof(addr))<0)
	{
		perror("bind error");
		return -1;
	}
//4建立子程序
	pid=fork();
	if(pid>0)//父程序--接收訊息(並處理)
	{
		struct msg m; //type=L ilen=20  3="abc"
		int ilen;
		struct sockaddr_in caddr;
		//關聯容器---線上列表
		map<string,struct sockaddr_in> online;
		socklen_t clen=sizeof(caddr);
		while(1) 
		{
			//接收內容:不一定此msg的內容  abc  
			ilen=recvfrom( 					\
				      sock,&m,sizeof(m),0,		\
				      (struct sockaddr*)&caddr,&clen 	\
				      );
			if(ilen<0)//網路出問題
			{
				perror("recvfrom fail");
				break;
			}
			else if(ilen!=sizeof(m))
				continue;
			//判斷訊息的型別
			switch(m.type)
			{
				case 'L'://上線
				{
					cout<<"上線"<<endl;
					//接收發送的驗證資訊
					struct reg r;
					if(recv(sock,&r,sizeof(r),0)==sizeof(r))
					{
						addOnline(r,caddr,sock,online);
					}
				}
				break;
				case 'Q'://下線
					cout<<"下線"<<endl;
				break;
				case 'B'://廣播
					cout<<"廣播"<<endl;
				break;
				case 'C'://私信
					cout<<"私信"<<endl;
				break;
				case 'R'://註冊
				{
					cout<<"註冊"<<endl;
					//註冊函式
					reg(sock,caddr);
				}
				break;
				default:  ;
			}
		}
	}	
	else if(pid==0)//子程序
	{
	}
	else 
	{
		perror("fork fail");
		return -1;
	}
//4關閉套接字
	close(sock);
}

   實現客戶端向伺服器傳送訊息 :

#include<iostream>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
using namespace std;
struct reg
{
	char name[10];
	char pwd[10];
};
struct msg
{
	char type;
	long ilen;
};
int main()
{
//1建立套接字
	int sock=socket(AF_INET,SOCK_DGRAM,0);
	int pid;
	pid=fork();
	if(pid>0)
	{
	//2傳送  msg
		struct sockaddr_in addr;
		addr.sin_family		=AF_INET;
		addr.sin_port		=htons(7979);
		addr.sin_addr.s_addr	=inet_addr("127.0.0.1");
		//傳送註冊資訊
		struct msg m={'L',10};	    //將L 改為 R 則為註冊
		sendto(sock,&m,sizeof(m),0,(struct sockaddr*)&addr,sizeof(addr));
		struct reg r={"lxw","123456"};
		sendto(sock,&r,sizeof(r),0,(struct sockaddr*)&addr,sizeof(addr));
		//等待子程序結束 
		wait(NULL);
	}
	else if(pid==0)
	{
		char buf[100]="";
		int ilen=0;
		while((ilen=recv(sock,buf,100,0))>0)
		{
			buf[ilen]='\0';
			cout<<buf<<endl;
		}
	}
//3關閉
	close(sock);
}

 

   在上面的基礎上 伺服器實現註冊 , 上線 , 下線 , 私聊 的協議功能 : 

#include<iostream>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
using namespace std;
//註冊資訊
struct reg
{
	char name[10];
	char pwd[10];
};
//訊息型別
struct msg
{
	char type;
	long ilen;
};
struct chart
{
	char name[10];		//目的名
	char content[100];
};
int main()
{
//1建立套接字
	int sock=socket(AF_INET,SOCK_DGRAM,0);
	int pid;
	pid=fork();
	if(pid>0)
	{
//2傳送 msg
	struct sockaddr_in addr;
	addr.sin_family			=AF_INET;
	addr.sin_port			=htons(7979);
	addr.sin_addr.s_addr		=inet_addr("192.168.8.209");

	//傳送註冊資訊
	//struct msg m={'L',10};
	//sendto(sock,&m,sizeof(m),0,(struct sockaddr*)&addr,sizeof(addr));
	//struct reg r={"lxw","123456"};
	//sendto(sock,&r,sizeof(r),0,(struct sockaddr*)&addr,sizeof(addr));
	struct msg m1={'C',110};
	sendto(sock,&m1,sizeof(m1),0,(struct sockaddr*)&addr,sizeof(addr));
	//char name[10]="lxw";
	//struct reg r1={"lxw","123456"};
	//sendto(sock,&r1,sizeof(r1),0,(struct sockaddr*)&addr,sizeof(addr));
	struct chart ct={"lxw","你好阿!"};
	sendto(sock,&ct,sizeof(ct),0,(struct sockaddr*)&addr,sizeof(addr));

	wait(NULL);
	}
	else if(pid==0)
	{
		char buf[100]="";
		int ilen=0;
		while((ilen=recv(sock,buf,100,0))>0)
		{
			buf[ilen]='\0';
			cout<<buf<<endl;
		}
	}
//關閉
	close(sock);
}

   同樣的客戶端的實現 : 

#include<iostream>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
using namespace std;
//註冊資訊
struct reg
{
	char name[10];
	char pwd[10];
};
//訊息型別
struct msg
{
	char type;
	long ilen;
};
struct chart
{
	char name[10];		//目的名
	char content[100];
};

int main()
{
//1建立套接字
	int sock=socket(AF_INET,SOCK_DGRAM,0);
	int pid;
	pid=fork();
	if(pid>0)
	{
//2傳送 msg
	struct sockaddr_in addr;
	addr.sin_family			=AF_INET;
	addr.sin_port			=htons(7979);
	addr.sin_addr.s_addr		=inet_addr("192.168.8.209");

	//傳送註冊資訊
	//struct msg m={'L',10};
	//sendto(sock,&m,sizeof(m),0,(struct sockaddr*)&addr,sizeof(addr));
	//struct reg r={"lxw","123456"};
	//sendto(sock,&r,sizeof(r),0,(struct sockaddr*)&addr,sizeof(addr));
	struct msg m1={'C',110};
	sendto(sock,&m1,sizeof(m1),0,(struct sockaddr*)&addr,sizeof(addr));
	//char name[10]="lxw";
	//struct reg r1={"lxw","123456"};
	//sendto(sock,&r1,sizeof(r1),0,(struct sockaddr*)&addr,sizeof(addr));
	struct chart ct={"lxw","你好阿!"};
	sendto(sock,&ct,sizeof(ct),0,(struct sockaddr*)&addr,sizeof(addr));

	wait(NULL);
	}
	else if(pid==0)
	{
		char buf[100]="";
		int ilen=0;
		while((ilen=recv(sock,buf,100,0))>0)
		{
			buf[ilen]='\0';
			cout<<buf<<endl;
		}
	}
//關閉
	close(sock);
}