内容详细的简单群聊

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HaloTrriger/article/details/80906005

一、本文知识点

基于TCP协议的套接字编程,使用多线程处理多用户。

二、大体框架

服务器:创建套接字->绑定IP->套接字监听->accept用户,给用户创建线程处理程序->分离该线程->关闭套接字。
客户端:创建套接字->连接PIP->读取用户消息->将消息发送给服务端->从服务器接收消息->显示消息给用户
这里写图片描述

三、具体思路

 1. 定义一个头文件chat.h,在文件内定义了多个宏和结构体。
 PIP表示客户端使用的ip,IP表示服务器绑定的ip,PORT客户端和服务器相同的端口号。
 NAMESIZE表示用户名字最大长度,MSGSIZE表示消息最大长度,以便于在客户端的buf数组格式化存储用户的名字和消息。
 用户的结构体,该结构体保存用户的名字和消息。
 带头双向链表结构体,保存前一个、后一个节点和自己的套接字变量。
2. 创建DLNode.c文件,内含带头双链表的创建节点,增加和删除节点功能。
3. 客户端创建套接字,并定义一个套接字结构体,使用该结构体调用connect函数和该套接字连接。
 定义buf缓冲区,定义user用户结构体变量。
 将标准输入改为非阻塞态,从标准输入读取内容。读到内容,将用户名和内容格式化保存在user结构体变量内发给服务器,然后非阻塞式接收消息,读到消息格式化取出并打印。
3. 服务器bind绑定IP和PORT,然后置为listen监听态。
 每当accept接收到一个用户,就保存该用户套接字,头插在链表内部。
  创建一个线程去处理该用户,并将该线程置为分离态,无需父进程回收,避免主服务器进程一直阻塞在回收线程上。
  在线程内部,阻塞式接收用户消息,接收到后依次遍历链表,转发消息给每一个用户。当接收到内容长度为0,意味着用户已经退出,则把该套接字从链表内删除并关闭该套接字,最后该线程退出,被系统回收。

三、使用方面

服务器已经绑定了端口和IP无需手动输入,只需执行程序后台转发消息即可。
客户端:内置代码也绑定了IP。第一次执行,需要附带自己的用户名然后执行,之后只需输入消息即可。
注意:本代码中,PIP和IP是我服务器的。如需本地使用,需将PIP和IP都改为自己当前环境的IP的值。只需在一个局域网内,就可实现多人群聊。

四、程序代码

chat.h

#ifndef __CHAT_H__
#define __CHAT_H__

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<pthread.h>

#define IP  "172.27.0.14"//内网ip
#define PIP   "118.24.188.96"//公网ip
#define PORT 8080//端口 
#define MSGSIZE 128//消息大小
#define NAMESIZE 15//姓名长度

typedef struct User
{
        char name[NAMESIZE];//姓名
        char msg[MSGSIZE];//消息
}User;//用户信息

typedef struct DoubleListNode
{
        int value;
        struct DoubleListNode*prev;
        struct DoubleListNode*next;
}DLNode;//带头双向链表

DLNode*CreateNode(int value);//创建节点
void PushFront(DLNode*phead,int value);//头插节点
void Erase(DLNode*phead,int value);//删除节点

#endif//__CHAT_H__

DLNode.c

#include"chat.h"

DLNode*CreateNode(int value)
{
        DLNode*node=(DLNode*)malloc(sizeof(DLNode));
        node->value=value;
        node->next=NULL;
        node->prev=NULL;
        return node;
}

void PushFront(DLNode*phead,int value)
{
        DLNode*node=CreateNode(value);
        if(phead->next==NULL)
        {
                phead->next=node;
                node->prev=phead;
        }
        else
        {
                node->next=phead->next;
                node->next->prev=node;
                phead->next=node;
                node->prev=phead;
        }
}

void Erase(DLNode*phead,int value)
{
        DLNode*cur=phead->next;
        while(cur!=NULL)
        {
                if(cur->value==value)
                {
                        DLNode*prev=cur->prev;
                        prev->next=cur->next;
                        if(cur->next!=NULL)
                                cur->next->prev=prev;
                        else
                                prev->next=NULL;
                        free(cur);
                        cur=NULL;
                }
                else
                {
                        cur=cur->next;
                }
        }
}

client.c

#include"chat.h"

int SockDeal()//创建、连接套接字
{
        int sock=socket(AF_INET,SOCK_STREAM,0);
        if(sock<0)
        {
                perror("socket error");
                exit(1);
        }
        struct sockaddr_in server;
        server.sin_family=AF_INET;
        server.sin_port=htons(PORT);
        server.sin_addr.s_addr=inet_addr(IP);

        if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0)
        {
                perror("connet error");
                exit(2);
        }
        return sock;
}

void MsgDeal(int sock,char*name)//处理消息
{
        char buf[NAMESIZE+MSGSIZE]={0};
        User user;

        while(1)//消息循环收发
        {
                fflush(stdout);
                memset(buf,0,NAMESIZE+MSGSIZE);
                memset(user.msg,0,MSGSIZE);
                fcntl(0,F_SETFL,FNDELAY);//sock置为非阻塞
                int read_size=read(0,user.msg,MSGSIZE);
                if(read_size>0)
                {
                        strcpy(buf,name);  
                        strcpy(buf+NAMESIZE,user.msg);   
                        if(send(sock,buf,NAMESIZE+MSGSIZE,MSG_DONTWAIT)<0)
                        {
                                perror("send error");
                                exit(1);
                        }
                }
                int recv_size=recv(sock,buf,NAMESIZE+MSGSIZE,MSG_DONTWAIT);
                if(recv_size>0)
                {
                memset(user.name,0,NAMESIZE);
                memset(user.msg,0,MSGSIZE);
                        strcpy(user.name,buf);
                        strcpy(user.msg,buf+NAMESIZE);
                        printf("%s 说: %s",user.name,user.msg);
                }
        } 
        close(sock);

}


int main(int argc,char *argv[])
{
        if(argc!=2)
        {
                perror("input format error");
                exit(1);
        }
        int sock=SockDeal();

        MsgDeal(sock,argv[1]);

        close(sock);
        return 0;
}

server.c

#include"chat.h"

DLNode* phead=NULL;

int ServerPrepare()
{
        int sock=socket(AF_INET,SOCK_STREAM,0);
        if(sock<0)
        {
                perror("socker error");
                exit(1);
        }
        struct sockaddr_in host;//服务器主机
        host.sin_family=AF_INET;
        host.sin_port=htons(PORT);
        host.sin_addr.s_addr=inet_addr(PIP);

        if(bind(sock,(struct sockaddr*)&host,sizeof(host))<0)
        {
                perror("bind error");
                exit(1);
        }
        if(listen(sock,5)<0)
        {
                perror("listen error");
                exit(1);
        }

        return sock;
}


void thread_fun(void* argument)
{
        int sock=(int)argument;
        char buf[NAMESIZE+MSGSIZE]={0};
        while(1)
        {
                int recv_size=recv(sock,buf,MSGSIZE+NAMESIZE,0);
                if(recv_size<=0)
                {
                        Erase(phead,sock);
                        break;
                }
                else
                {
                        DLNode*cur=phead->next;
                        while(cur!=NULL)
                        {
                                send(cur->value,buf,NAMESIZE+MSGSIZE,MSG_DONTWAIT);
                                cur=cur->next;
                        }
                }
        }
        close(sock);
}

void ServerWork(int sock)
{
        while(1)
        {
                int new_sock=accept(sock,NULL,NULL);
                if(new_sock<0)
                {
                        perror("accept error");
                        return;
                }
                PushFront(phead,new_sock);
                //开始线程处理
                pthread_t thread;
                pthread_create(&thread,NULL,(void*)thread_fun,(void*)new_sock);
                pthread_detach(thread);
        }
}

int main()
{
        int sock=ServerPrepare();
        //将头初始化为sock并没有实在意义(比起随便初始化为一个值,不如初始化为已存在的)
        phead=CreateNode(sock);
        ServerWork(sock);
        close(sock);
        return 0;
}

五、实测,无图言卵系列

这里写图片描述

最后,欢迎大家留言,提问题给我,谢谢

猜你喜欢

转载自blog.csdn.net/HaloTrriger/article/details/80906005