完成网络聊天室编写
- #define ERR_MSG(msg) do{\
- fprintf(stderr,"__%d__",__LINE__);\
- perror(msg);\
- }while(0)
- #define IP "127.0.0.1"
- #define PORT 6666
- //创建链表
- Linklistptr list_create();
- Linklistptr node_buy(datatype e);
- int list_insert_head(Linklistptr L,datatype e);
- int chat_login(Linklistptr L,int sfd,msg_t msg,datatype e);
- int chat_enter(Linklistptr l,int sfd,msg_t msg,datatype e);
- int chat_exit(Linklistptr L,int sfd,msg_t msg,datatype e);
- typedef struct sockaddr_in datatype;//类型重命名
- //创建信息结构体
- typedef struct msg
- {
- char type;//操作码 'L'登录 'C'群聊 'Q'退出
- char name[20];
- char text[128];
- }msg_t;
- //创建链表保存地址信息
- typedef struct Node
- {
- union
- {
- datatype res_addr;//数据域
- int len;//头结点数据域
- };
- struct Node *next;//指针域
- }Node, *Linklistptr;
- int main(int argc, const char *argv[])
- {
- //创建报式套接字
- int sfd = socket(AF_INET, SOCK_DGRAM, 0);
- if(sfd < 0)
- {
- ERR_MSG("socket");
- return -1;
- }
- printf("socket create success sfd=%d\n",sfd);
- //填充接收方的地址信息结构体,给bind函数使用
- datatype sin;
- sin.sin_family = AF_INET;
- sin.sin_port = htons(PORT);
- sin.sin_addr.s_addr = inet_addr(IP);
- //绑定地址信息结构体
- if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
- {
- ERR_MSG("bind");
- return -1;
- }
- printf("bind success");
- //创建接收地址信息结构体
- datatype cin;
- socklen_t addrlen = sizeof(cin);
- pthread_t tid;
- msg_t msg;
- //创建一个链表
- Linklistptr L = list_create();
- if(NULL == L)
- {
- return -1;
- }
- while(1)
- {
- //主线程负责接收并处理
- if(recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&cin, &addrlen) < 0)
- {
- ERR_MSG("recvfrom");
- return -1;
- }
- printf("[%s:%d] : %s\n", msg.name, ntohs(cin.sin_port), msg.text);
- switch(msg.type)
- {
- case 'L'://登录
- chat_login(L,sfd,msg,e);
- break;
- case 'C'://群聊
- chat_enter(L,sfd,msg,e);
- break;
- case 'Q'://退出
- chat_exit(L,sfd,msg,e);
- break;
- default :
- printf("输入错误\n");
- break;
- }
- info.sfd=sfd;
- info.sin=sin;
- //分支线程只负责发送系统信息
- if(pthread_create(&tid, NULL, task,(void*)&info) != 0)
- {
- fprintf(stderr, "pthread_create failed__%d__\n",__LINE__);
- return -1;
- }
- pthread_detach(tid);
- }
- //关闭文件描述符
- if(close(sfd) < 0)
- {
- ERR_MSG("close");
- return -1;
- }
- return 0;
- }
- void *task(void *arg)
- {
- int sfd = ((struct Climsg*)arg)->sfd;
- datatype sin = ((struct Climsg*)arg)->sin;
- msg_t msg;
- msg.type = 'C';
-
- while(1)
- {
-
- //从终端获取消息文本
- fgets(msg.text, sizeof(msg.text), stdin);
- msg.text[strlen(msg.text)-1] = '\0';
- //将信息包名定为服务器
- strcpy(msg.name,"servce");
- if(sendto(sfd , &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
- {
- ERR_MSG("sendto");
- }
- }
- close(sfd);
- pthread_exit(NULL);
- }
- //创建链表
- Linklistptr list_create()
- {
- //从堆区申请一个头结点类型
- Linklistptr L = (Linklistptr)malloc(sizeof(Node));
- if(NULL == L)
- {
- printf("创建失败\n");
- return NULL;
- }
- //创建成功后,对节点进行初始化工作
- L->len = 0;
- L->next = NULL;
- return L;
- }
- //申请节点封装地址信息
- Linklistptr node_buy(datatype e)
- {
- //在堆区申请节点
- Linklistptr p = (Linklistptr)malloc(sizeof(Node));
- if(NULL == p)
- {
- printf("申请失败]n");
- return NULL;
- }
- //节点申请成功,封装数据
- p->res_addr = e;
- p->next = NULL;
- return p;
- }
- //头插
- int list_insert_head(Linklistptr L,datatype e)
- {
- //判断逻辑
- if(NULL == L)
- {
- printf("所给链表不合法\n");
- return -1;
- }
- //调用节点封装数据
-
- Linklistptr p = node_buy(e);
- if(NULL == p)
- {
- return -1;
- }
- //头插
- p->next = L->next;
- L->next = p;
- //表长变化
- L->len++;
- return 0;
- }
- int chat_login(Linklistptr L,int sfd,msg_t msg,datatype e)
- {
- Linklistptr p=L;//定义一个遍历指针
- while(p->next!=NULL)
- {
- p=p->next;
- if(sendto(sfd,&msg,sizeof(msg_t),0,(struct sockaddr *)&(p->res_addr),sizeof(p->res_addr))<0)
- {
- ERR_MSG("sendto");
- return -1;
- }
- }
- //头插,将自己的地址存入链表中
- list_insert_head(L,e);
- return 0;
- }
- //群聊
- int chat_enter(Linklistptr l,int sfd,msg_t msg,datatype e)
- {
- //定义一个遍历指针
- Linklistptr p=L;
- while(p->next!=NULL)
- {
- p=p->next;
- //判断链表客户端信息
- if(memcmp(&(p->res_addr),&e,sizeof(e))==0)
- {
- if(sendto(sfd,&msg,sizeof(msg_t),0,(struct sockaddr*)&(p->res_addr),sizeof(p->res_addr))<0)
- {
- ERR_MSG("sendto");
- return -1;
- }
- }
- }
- return 0;
- }
- //退出
- int chat_exit(Linklistptr L,int sfd,msg_t msg,datatype e)
- {
- Linklistptr p=L;
- while(p->next!=NULL)
- {
- p=p->next;
- //判断链表客户端信息
- if(memcmp(&(p->res_addr),&e,sizeof(e))==0)
- {
- if(sendto(sfd,&msg,sizeof(msg_t),0,(struct sockaddr*)&(p->res_addr),sizeof(p->res_addr))<0)
- {
- ERR_MSG("sendto");
- return -1;
- }
- }
- else//此时当前节点的下一个节点保存的就是要退出的成员的信息
- {
- Linklistptr q=p->next;
- if(sendto(sfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&(q->resin), sizeof(q->resin)) < 0)
- {
- ERR_MSG("sendto");
- return -1;
- }
- p->next=q->next;
- free(q);
- q=NULL;
- }
- }
- return 0;
- }
cli
- #include
- #define ERR_MSG(msg) do{\
- fprintf(stderr,"__%d__",__LINE__);\
- perror(msg);\
- }while(0)
- #define IP "127.0.0.1"
- #define PORT 4399
- typedef struct sockaddr_in datatype;//类型重命名
- //创建信息结构体
- typedef struct msg
- {
- char type;//操作码 'L'登录 'C'群聊 'Q'退出
- char name[20];
- char text[128];
- }msg_t;
-
- struct Climsg
- {
- int cfd;
- datatype cin;
- msg_t msg;
- };
- void *task(void *arg);
- int main(int argc, const char *argv[])
- {
- //创建报式套接字
- int cfd = socket(AF_INET, SOCK_DGRAM, 0);
- if(cfd < 0)
- {
- ERR_MSG("socket");
- return -1;
- }
- printf("socket create success cfd=%d\n",cfd);
- //填充接收方的地址信息结构体,给sendto函数使用
- datatype sin;
- sin.sin_family = AF_INET;
- sin.sin_port = htons(PORT);
- sin.sin_addr.s_addr = inet_addr(IP);
- //创建接收地址信息结构体
- datatype cin;
- socklen_t c_addrlen = sizeof(cin);
- pthread_t tid;
- msg_t msg;
- memset(&msg, 0, sizeof(msg_t));
- struct Climsg info;
- printf("请输入登录名:");
- fgets(msg.name, sizeof(msg.name), stdin);
- msg.name[strlen(msg.name)-1] = '\0';
- msg_t msg1=msg;
- msg.type = 'L';
- strcpy(msg.text,"已进入群聊");
- //发送登录请求包
- if(sendto(cfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
- {
- ERR_MSG("sendto");
- return -1;
- }
- printf("登陆成功\n");
- info.cfd = cfd;
- info.cin = sin;
- info.msg = msg;
- while(1)
- {
-
- memset(&msg, 0, sizeof(msg));
- if(recvfrom(cfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&cin, &c_addrlen) < 0)
- {
- ERR_MSG("recvfrom");
- return -1;
- }
- if(strcmp(msg.name, msg1.name) == 0)
- {
- break;
- }
-
- printf("[%s] : %s\n", msg.name, msg.text);
-
-
- if(pthread_create(&tid, NULL, task, (void *)&info) != 0)
- {
- ERR_MSG("pthread_create");
- return -1;
- }
-
- //将分支线程设置为分离态
- pthread_detach(tid);
- }
-
- //关闭套接字描述符
- close(cfd);
- return 0;
- }
- //线程体函数
- void *task(void *arg)
- {
-
- int cfd = ((struct Climsg*)arg)->cfd;
- datatype sin = ((struct Climsg*)arg)->cin;
- msg_t msg = ((struct Climsg*)arg)->msg;
-
- while(1)
- {
- //清空文本内容
- bzero(msg.text, sizeof(msg.text));
- //从终端获取数据
- fgets(msg.text, sizeof(msg.text), stdin);
- msg.text[strlen(msg.text)-1] = '\0';
-
- msg.type = 'C';
- if(strcmp(msg.text,"quit") == 0)
- {
- msg.type = 'Q';
- strcpy(msg.text, "已下线");
- }
- if(sendto(cfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
- {
- ERR_MSG("sendto");
- }
- if(strcmp(msg.text, "已下线") == 0)
- {
- break;
- }
- }
- close(cfd);
- pthread_exit(NULL);
- }