網站首頁 編程語言 正文
1.UDP群聊的功能
有新用戶登錄,其他在線的用戶可以收到登錄信息
有用戶群聊,其他在線的用戶可以收到群聊信息
有用戶退出,其他在線的用戶可以收到退出信息
服務器可以發(fā)送系統(tǒng)信息
2.寫項目的流程
畫流程圖
根據(jù)流程圖寫框架
一個功能一個功能實現(xiàn)
3.流程圖
4.代碼實現(xiàn)
4.1頭文件
#ifndef __MYHEAD_H__
#define __MYHEAD_H__
#include <head.h>
#define N 512
//聊天操作用的結構體
typedef struct _MSG{
char ch;//用來'l'聊天,'q'退出,'登錄d'
char name[128];//存名字
char text[N];//存聊天內容
}msg_t;
//用來保存每個用戶信息的結構體
typedef struct _Jilu{
struct sockaddr_in addr;
struct _Jilu *next;
}jilu_t;
int create_head(jilu_t **head);
int input_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len);
int wx_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len);
int tuichu_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len);
#endif
4.2函數(shù)
#include "myhead.h"
//創(chuàng)建一個單鏈表頭
int create_head(jilu_t **head)
{
if(head==NULL){
printf("傳送錯誤,請檢查\n");
return -1;
}
(*head)=(jilu_t *)malloc(sizeof(jilu_t));
(*head)->next=NULL;
return 0;
}
//記錄登錄的用戶的信息
int input_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len)
{
if(head==NULL){
printf("傳送錯誤,請檢查\n");
return -1;
}
//將這個用戶登錄的信息發(fā)送給所有人
snprintf(msg.text,sizeof(msg.text),"[%s]%s",msg.name,"登錄了");
//這個用來記錄頭的地址
jilu_t *jilu_head=head;
while(jilu_head->next!=NULL){
jilu_head=jilu_head->next;
if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&jilu_head->addr,clientaddr_len)==-1){
ERRLOG("將用戶登錄信息發(fā)給所有人失敗");
}
}
//創(chuàng)建一個新的節(jié)點,并且把新的用戶信息放入新得單列表
jilu_t *temp=NULL;
create_head(&temp);
temp->addr=clientaddr;
//用頭插法將用戶信息插入鏈表
temp->next=head->next;
head->next=temp;
return 0;
}
int wx_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len)
{
if(head==NULL){
printf("傳送錯誤,請檢查\n");
return -1;
}
//將接受到的消息發(fā)給除了自己以外的所有人
jilu_t *jilu_head=head;
while(jilu_head->next!=NULL){
jilu_head=jilu_head->next;
if(0!=memcmp(&(jilu_head->addr),&clientaddr,sizeof(clientaddr))){
if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&jilu_head->addr,clientaddr_len)==-1){
ERRLOG("將聊天內容發(fā)給所有人失敗");
}
}
}
return 0;
}
int tuichu_addr(jilu_t *head,msg_t msg,int sockfd,struct sockaddr_in clientaddr,socklen_t clientaddr_len)
{
if(head==NULL){
printf("傳送錯誤,請檢查\n");
return -1;
}
snprintf(msg.text,sizeof(msg.text),"%s%s",msg.name,"退出登錄");
jilu_t *jilu_head=head;
jilu_t *pdel=NULL;
while(jilu_head->next!=NULL){
if(0==memcmp(&(jilu_head->next->addr),&clientaddr,sizeof(clientaddr))){
pdel=jilu_head->next;
jilu_head->next=pdel->next;
free(pdel);
pdel=NULL;
}else{
jilu_head=jilu_head->next;
if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&jilu_head->addr,clientaddr_len)==-1){
ERRLOG("將這個退出的信息告訴所有人失敗");
}
}
}
return 0;
}
4.3服務器
#include "myhead.h"
int main(int argc, char const *argv[])
{
int sockfd=0;
pid_t pid=0;
msg_t msg;//用來進行各種操作
msg_t faso;//用來發(fā)系統(tǒng)消息
if((sockfd=socket(AF_INET,SOCK_DGRAM,0))==-1){
ERRLOG("創(chuàng)建服務器套接字失敗");
}
//將網絡信息結構體放入服務器中
struct sockaddr_in serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
socklen_t serveraddr_len=sizeof(serveraddr);
//將套接字與網絡信息結構體綁定
if(bind(sockfd,(struct sockaddr*)&serveraddr,serveraddr_len)==-1){
ERRLOG("將套接字與網絡信息結構體綁定失敗");
}
//創(chuàng)建一個新的網絡信息結構體來存客戶端的信息
struct sockaddr_in clientaddr;
clientaddr.sin_family=AF_INET;
socklen_t clientaddr_len=sizeof(clientaddr);
//創(chuàng)建進程
pid=fork();
if(pid==-1){
ERRLOG("服務器創(chuàng)建進程失敗");
}else if(pid==0){
//創(chuàng)建一個單列表保存網絡信息結構體
jilu_t *head;
create_head(&head);
memset(&msg,0,sizeof(msg));
while(1){
if(recvfrom(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&clientaddr,&clientaddr_len)==-1){
ERRLOG("接受客戶端傳來的信息失敗");
}
switch(msg.ch){
case 'd'://登錄信息
input_addr(head,msg,sockfd,clientaddr,clientaddr_len);
//head->next=NULL; //這個用來測試用的
break;
case 'l'://聊天信息
wx_addr(head,msg,sockfd,clientaddr,clientaddr_len);
break;
case 'q'://退出信息
tuichu_addr(head,msg,sockfd,clientaddr,clientaddr_len);
break;
}
}
}else{
while(1){
//發(fā)系統(tǒng)消息
memset(&faso,0,sizeof(faso));
fgets(faso.text,sizeof(faso.text),stdin);
faso.text[strlen(faso.text)-1]='\0';
faso.ch='l';
sprintf(faso.name,"%s","系統(tǒng)消息");
if(sendto(sockfd,&faso,sizeof(faso),0,(struct sockaddr*)&serveraddr,serveraddr_len)==-1){
ERRLOG("發(fā)送系統(tǒng)消息失敗");
}
}
}
return 0;
}
4.4客戶端
#include "myhead.h"
int main(int argc, char const *argv[])
{
//判斷輸入的對不對
if(argc!=3){
printf("輸入格式錯誤,./a.out ip port\n");
exit(EXIT_SUCCESS);
}
int sockfd=0;
pid_t pid=0;
msg_t msg;//創(chuàng)建發(fā)送用戶的信息
if((sockfd=socket(AF_INET,SOCK_DGRAM,0))==-1){
ERRLOG("創(chuàng)建客戶端套接字失敗");
}
//將客戶端網絡信息結構體進行綁定
struct sockaddr_in clientaddr;
memset(&clientaddr,0,sizeof(clientaddr));
clientaddr.sin_family=AF_INET;
clientaddr.sin_port=htons(atoi(argv[2]));
clientaddr.sin_addr.s_addr=inet_addr(argv[1]);
socklen_t clientaddr_len=sizeof(clientaddr);
//輸入用戶的姓名進行登錄操作
msg.ch='d';
printf("請輸入你用來登錄的姓名");
fgets(msg.name,sizeof(msg.name),stdin);
msg.name[strlen(msg.name)-1]='\0';
//給服務器發(fā)送用戶已經登錄上的操作
if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&clientaddr,clientaddr_len)==-1){
ERRLOG("客戶端給服務器發(fā)送的登錄信息失敗");
}
//創(chuàng)建進程,子進程用來接受,父進程用來發(fā)送
pid=fork();
if(pid==-1){
ERRLOG("客戶端創(chuàng)建進程失敗");
}else if(pid==0){
//用來接受服務器發(fā)來的消息
while(1){
memset(&msg,0,sizeof(msg));
if(recvfrom(sockfd,&msg,sizeof(msg),0,NULL,NULL)==-1){
ERRLOG("接受服務器發(fā)來的信息錯誤");
}
printf("[%s]>>(%s)\n",msg.name,msg.text);
}
}else{
//寫入要判聊天的內容
while(1){
memset(msg.text,0,sizeof(msg.text));
fgets(msg.text,sizeof(msg.text),stdin);
msg.text[strlen(msg.text)-1]='\0';
if(strncmp("quit",msg.text,5)==0){
msg.ch='q';
}else{
msg.ch='l';
}
//將寫好的內容發(fā)送給服務器
if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&clientaddr,clientaddr_len)==-1){
ERRLOG("將聊天內容發(fā)送給服務器失敗");
}
//當識別到停止的時候,關閉進程
if(strncmp("quit",msg.text,5)==0){
kill(pid,SIGKILL);
close(sockfd);
exit(EXIT_SUCCESS);
}
}
}
return 0;
}
原文鏈接:https://blog.csdn.net/a2998658795/article/details/125855644
相關推薦
- 2022-05-02 python中的Numpy二維數(shù)組遍歷與二維數(shù)組切片后遍歷效率比較_python
- 2022-06-16 .Net?Core解決WebAPI中返回時間格式帶T的問題_實用技巧
- 2022-06-12 PostgreSQL數(shù)據(jù)庫的基本查詢操作_PostgreSQL
- 2022-07-25 C++文件的操作及小實驗示例代碼詳解_C 語言
- 2022-04-09 C#實現(xiàn)簡單的計算器功能_C#教程
- 2022-04-05 python中時間轉換錯誤:時間戳轉換|帶有時區(qū)的轉換 如何解析ISO 8601格式的日期?
- 2022-05-07 LINQ教程之使用Lambda表達式_實用技巧
- 2022-09-02 使用Docker配置redis?sentinel哨兵的方法步驟_docker
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支