C语言基于socket的网络群聊室

服务端:


#include <pthread.h>
#include<stdio.h>
#include<WinSock2.h>//调用套接字的头函数 
#include <stdlib.h>
#define maxsize 20 //最大客户量 

int cIntSock [maxsize]={} ;//用来存放客户 
 
//群发函数 
void *sendall(char *buf,int *fd)
{
    int k=*(int*)fd; //将fd强制转换为int型指针 ,然后将其值赋给K 
    int i;
    for(i=0;i<maxsize;i++)
    {
        if(-1!=cIntSock[i]&&k!=cIntSock[i])//若有客户 且不为发送消息本人 !!!现在客户端自己的消息不会发给自己了!!!!
         
        {
            send(cIntSock[i], buf, strlen(buf) + 1, 0);//则发送信息 
        }
    }
    printf("群发命令执行成功!\n");
    printf("\n");
}

//服务端接收函数 
void  *server(void *a)
 {
     int fd=*(int*)a; //将指针a强制转换为整型指针 (类似于int *a),然后将其值赋给fd(即fd=*a) ,所以fd就是一个变量罢了 
     printf("accept返回值:%d\n",fd);//打印fd的值出来看看,其根本是accept的返回值 
     char buf[maxsize];//存储消息 
    char name[maxsize]={"未知客户"};//存储ID ,需要先赋值,否则客户未注册直接关闭了客户端,会有小错误解 
    char ts[maxsize];//存放需要群发的信息 
    
    //故障记录,若客户端连接后正常注册登录,就执行下面 
    int l=recv(fd,name,sizeof(name),0); 
    if(l>0)
    {
        printf("%s进入群聊\n",name);
         printf("\n");
        sprintf(ts,"%s进入群聊!!!\n",name);//把数据写到ts里面 
                printf("执行群发命令\n");
                sendall(ts,&fd);//进群消息群发给所有客户端 
            

    while (1) {
        int m;
        m=recv(fd, buf, sizeof(buf), 0);//第一个参数为建立的通信(客户端发来的信息),将其复制到数组中(第二个类似于缓存区存放来自客户端的数据) 
        
        //找到退出群聊的人,置为-1 ,或者有人断开连接 
        if(m<=0||strcmp(buf,"quit")==0) 
        {
            sprintf(ts,"%s离开群聊\n",name);
            int index=0;
            for(;index<maxsize;index++)
            {
                if(cIntSock[index]==fd)
                {
                    cIntSock[index]=-1; 
                    break; 
                }
            }
            printf("执行群发命令\n");
            sendall(ts,&fd);//群发退群消息 
            
            printf("%s退出群聊,已关闭线程\n",name);//在服务端显示
            printf("\n");
             
            strcpy(buf,"quit");
            send(fd,buf,strlen(buf) + 1, 0);//发送quit给需要退出的客户端,使其成功退出 
            
            closesocket(fd);//关闭套接字 
            pthread_exit(NULL);//关闭线程 
        }
        //若未退出群聊,则群发消息 
        sprintf(ts,"%s:%s\n",name, buf);
        printf("执行群发命令\n");
        sendall(ts,&fd);
}
    }
    else//客户端连接后不注册直接关闭,就关闭线程 
    {
            printf("%s连接断开,已关闭线程\n",name);//在服务端显示
            printf("\n");
            closesocket(fd);//关闭套接字 
            pthread_exit(NULL);//关闭线程 
    }
}
//主函数 
int main()
{
    
//初始化 
    WSADATA wsadata;//创建结构体变量 
    WSAStartup(MAKEWORD(2, 2), &wsadata);//初始化结构体变量 

//创建套接字 
    printf("创建socket对象...\n");
    SOCKET serverSock = socket(PF_INET, SOCK_STREAM, 0);//serversock是套接字变量名 (创建套接字)
        if(0 > serverSock)
    {
        perror("socket");//失败则返回socket函数的错误原因,后面同理 
        return -1;
    } 

//指定通信地址 (网络类型地址)
    printf("准备通信地址...\n");
    struct sockaddr_in sockAddr;//创建网络类型地址的结构体变量  
    sockAddr.sin_family = PF_INET;//地址域,要与domain(socket的PF_INET)一致 
    sockAddr.sin_addr.s_addr =inet_addr("127.0.0.2"); //IP地址,inet_addr将点分十进制的IP地址字符串转换成网络字节序的4字节整数(sockAddr.sin_addr.s_addr是4字节网络字节序) 
    sockAddr.sin_port = htons(52000);//端口号 htons将地址字节序的2字节整数转换成网络字节序 
    
//绑定地址 
    printf("绑定socket对象与地址...\n");
    if(bind(serverSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)));//将通信地址与服务端套接字绑定 
        {
        perror("bind");
    
    }
//将套接字serversock与地址 (SOCKADDR*)&sockAddr绑定 ((SOCKADDR*)&sockAddr应为将sockaddr_in取地址后强制转换为sockaddr*(sockaddr指针)类型,因为地址定义的参数是sockaddr) 

//监听端口 
    printf("设置监听...\n");
    if(listen(serverSock, 20));//20指排队的数量 
        {
        perror("listen");
    
    }
    
//接收 客户端信息 
    printf("等待客户端链接...\n");
    SOCKADDR cIntAddr; //sockaddr是基本地址 ,应该是用来接收客户端的地址 
    int nSize = sizeof(SOCKADDR);
    
    //对用来存放客户的数组置为-1 
    int index;
    for(index=0;index<maxsize;index++) 
    cIntSock[index]=-1;
    
    printf("\n");
    printf("*                         执行功能界面                                      *\n");
    
    for(; ;)
    {
     
    index=0;
    while(cIntSock[index]!=-1) index++;//找到未被占领的位置 
    cIntSock[index]= accept(serverSock, (SOCKADDR*)&cIntAddr, &nSize);//cintsock就是用来接收客户端的信息的变量 ,accpt连接成功会返回一个值,赋给cintsock用来区别客户 
    perror("accept");
    printf("\n");
    printf("新客户端连接成功!已建立线程\n");
    printf("\n");

//创立多线程 
    pthread_t tid;
    pthread_create(&tid,NULL,server,&cIntSock[index]);//将客户发信息的套接字传给接收函数 
    
}
    //关闭创建的套接字 
    closesocket(serverSock);
    WSACleanup();
    return 0;

}

 

客户端:

 

#include<stdio.h>

#include<winsock2.h>

#include<pthread.h>

#define maxsize 20//宏定义 

char F[maxsize]={"未命名"};//定义一个全局变量,在成功登陆后命名,便于判断登录与否 

 

//存放账号密码 

typedef struct seqqueue

{

 char ID[maxsize][maxsize];

 char password[maxsize][maxsize];

 int len;

}seq;

 

//客户端接收函数 

void *client_read(void*arg)

 int fd=*(int*)arg;

 char buf[50];//存放消息 

 for(; ;)

 {

  int x;

   x=recv(fd, buf, 50, 0);//故障记录 

   if(x<=0||strcmp(buf,"quit")==0)//收到服务器发来的quit,或者被断开 结束线程 

   {

    printf("已经与服务器断开连接\n");

   

    pthread_exit(NULL);

   }

   //否则就正常输出 

   printf("%s\n",buf); 

   

 }

}

 

//注册函数 

void register_1(seq *Q)

{

        printf("请输入你想要的ID:\n");

        Q->len=(Q->len+1);

        scanf("%s",Q->ID[Q->len]);

        printf("请设置你想要的密码:\n");

        scanf("%s",Q->password[Q->len]);

        printf("ID:\'%s\'注册成功!请牢记你的密码\n",Q->ID[Q->len]);

}

 

//注销函数 

void cancel_1(seq *Q)

{

 char a[maxsize];

 printf("请输入你想要注销账号的ID:\n"); 

 scanf("%s",a);

 int z,y;

    for( z=0;z<=Q->len;z++)//跟登录那一样,从0处开始,0处初始化了 ,否则会死循环! 

    {

        if(strcmp(Q->ID[z],a)==0)//根据ID查找 

        {

           y=z ;

        }

  else y=-1;

    }

 if(y!=-1) 

    {

     int i;

 for(i=y;i<=Q->len-1;i++)//删除队列中的信息 

    {

        strcpy(Q->password[i],Q->password[i+1]);

        strcpy(Q->ID[i],Q->ID[i+1]);

    }

    Q->len=Q->len-1;

    printf("账号%s注销成功 \n",a);

}

   else if(y==-1) printf("注销失败,未找到ID:%s\n",a);

}

 

//登录函数 

int login_1(seq *Q)

{

 char a[maxsize];

 char b[maxsize];

 printf("请输入ID:");

 printf("\n");

 scanf("%s",a);

 printf("请输入password:");

  printf("\n");

 scanf("%s",b);

 int x=0,y=0,z=0,h=0;

 char q[maxsize];

 for( x=0;x<=Q->len;x++)//从1开始找的话,1处未初始化,会出现直接登陆成功的错误 

    {

        if(strcmp(Q->ID[x],a)==0)//根据ID查找 

        {

            z=x;

        }

        else z=-1;

    }

     for( y=0;y<=Q->len;y++)

    {

        if(strcmp(Q->password[y],b)==0)//根据password查找

 

            h=y;

        }

        else h=-1;

    }

 if(z!=-1&&h!=-1)

 {

  strcpy(F,a);

  printf("登陆成功!\n");

 

  return 1;

   

}

else if(z==-1) 

{

 printf("登录失败,此账号未注册,是否需要注册一个新账号?(请输入是/否)\n");

 scanf("%s",q);

 char s[maxsize];

 strcpy(s,"是"); 

 if(strcmp(q,s)==0)

{

         printf("请输入你想要的ID:\n");

        Q->len=(Q->len+1);

        scanf("%s",Q->ID[Q->len]);

        printf("请设置你想要的密码:\n");

        scanf("%s",Q->password[Q->len]);

        printf("ID:\'%s\'注册成功!请牢记你的密码\n",Q->ID[Q->len]);

        printf("\n");

}

 

 return -1;

}

else if(z!=-1&&h==-1)

{

 printf("登录失败,密码错误\n");

 return -1;

}

 

 

 }

 

//退出函数 

void exit_1()

{

 printf("退出成功!\n");

 exit(0);

}

 

//主函数 

int main()

{

 //初始化 

   WSADATA wsadata;

   int nRes = WSAStartup(MAKEWORD(2, 2), &wsadata);

    if (nRes != 0) return -1;

//创建套接字 

   printf("创建socket对象...\n");

   SOCKET sock = socket(PF_INET, SOCK_STREAM, 0);

   if(0 > sock)

 {

  perror("socket");

  return -1;

 } 

//通信地址 

   printf("准备通信地址(127.0.0.2)...\n");

   struct sockaddr_in sockAddr;

   sockAddr.sin_family = PF_INET; 

   sockAddr.sin_addr.s_addr = inet_addr("127.0.0.2"); //只需要在这里指向服务器 ip 就可以了 

   sockAddr.sin_port = htons(52000);//端口 

 

   //连接服务器

    printf("链接服务端口52000...\n");

   if(connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)));

   {

    perror("connect");

   }

    printf("链接服务器成功!!!\n");

    printf("\n");

    

//功能列表 

    int n;

 seq Q;

 Q.len=0;

 strcpy(Q.ID[0],"NULL");//初始化

 strcpy(Q.password[0],"NULL");

 printf("* 功能列表 *\n");

 printf("* 1. 注册; *\n");

 printf("* 2. 注销; *\n");

 printf("* 3. 登录; *\n");

 printf("* 4. 退出。 *\n");

 printf("*************************************************************************\n");

 printf("\n");

 while (1)

 {

  int op;

  int p;

  fflush(stdin);//需要清楚缓存,否则输入错误可能会死循环 (特别是输入字母时) 

  printf("请选择你要操作的选项:\n");

  scanf("%d", &op);

  switch (op)

  {

  case 1:

   register_1(&Q);

   printf("\n");

   break;

  case 2:

            cancel_1(&Q);

             printf("\n");

   break;

  case 3:

            n=login_1(&Q);

            if(n==1){

             printf("\n");

             //登陆成功后再建立线程,防止未登录就被服务器群发消息,不符合群聊逻辑 

                 pthread_t tid;

         pthread_create(&tid,NULL,client_read,&sock);

    

            send(sock, F, strlen(F) + 1, 0);//发送ID给服务端 

            printf("* 聊天界面 *\n");

            printf("欢迎进入群聊!!\n");

          printf("(注意:群内禁止聊违法内容!)\n");

         printf("(注意:输入quit退出群聊!)\n");

         printf("\n");

            while (1) {

 

              char sendBuf[50]={"Hello"};

            

             scanf("%s",sendBuf);

      

              printf("\n");

              p=send(sock, sendBuf, strlen(sendBuf) + 1, 0);

              

             if(p<0||strcmp(sendBuf,"quit")==0)//输入quit和send发送失败时结束通信 

              {

             printf("结束通信\n");

             //关闭套接字,结束线程 

            closesocket(sock);

            pthread_exit(NULL);

        }

 

            }

          //关闭 

           WSACleanup();

           system("pause");}

             else break;

   break;

  case 4:

   exit_1();

   break;

  default:

   printf("error!\n");

   break;

  }

   }

   return 0;

}

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
由于网络聊天涉及到网络编程,需要用到socket编程库。下面是一个简单的C语言实现网络聊天群聊的代码: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <pthread.h> #define BUF_SIZE 100 #define MAX_CLNT 256 void *handle_clnt(void *arg); void send_msg(char *msg, int len); int clnt_cnt = 0; int clnt_socks[MAX_CLNT]; pthread_mutex_t mutx; int main(int argc, char *argv[]) { int serv_sock, clnt_sock; struct sockaddr_in serv_adr, clnt_adr; pthread_t t_id; if(argc != 2) { printf("Usage : %s <port>\n", argv[0]); exit(1); } pthread_mutex_init(&mutx, NULL); serv_sock = socket(PF_INET, SOCK_STREAM, 0); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family = AF_INET; serv_adr.sin_addr.s_addr = htonl(INADDR_ANY); serv_adr.sin_port = htons(atoi(argv[1])); if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1) perror("bind error"); if(listen(serv_sock, 5) == -1) perror("listen error"); while(1) { socklen_t clnt_adr_sz = sizeof(clnt_adr); clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz); pthread_mutex_lock(&mutx); clnt_socks[clnt_cnt++] = clnt_sock; pthread_mutex_unlock(&mutx); pthread_create(&t_id, NULL, handle_clnt, (void*)&clnt_sock); pthread_detach(t_id); printf("Connected client IP: %s \n", inet_ntoa(clnt_adr.sin_addr)); } close(serv_sock); return 0; } void *handle_clnt(void *arg) { int clnt_sock = *((int*)arg); int str_len = 0; char msg[BUF_SIZE]; while((str_len = read(clnt_sock, msg, sizeof(msg))) != 0) send_msg(msg, str_len); pthread_mutex_lock(&mutx); for(int i = 0; i < clnt_cnt; i++) { if(clnt_sock == clnt_socks[i]) { while(i++ < clnt_cnt - 1) clnt_socks[i] = clnt_socks[i+1]; break; } } clnt_cnt--; pthread_mutex_unlock(&mutx); close(clnt_sock); return NULL; } void send_msg(char *msg, int len) { pthread_mutex_lock(&mutx); for(int i = 0; i < clnt_cnt; i++) write(clnt_socks[i], msg, len); pthread_mutex_unlock(&mutx); } ``` 代码解析: 使用了两个线程:一个用于监听客户端的连接请求,另一个用于处理客户端的消息传输。 使用了一个clnt_socks数组来保存客户端的socket描述符,用于群发消息。 handle_clnt函数用于处理客户端的消息传输,当客户端断开连接时,从clnt_socks中删除该socket描述符。 send_msg函数用于群发消息,将消息写入所有客户端的socket描述符中。 在main函数中,使用了pthread_create和pthread_detach函数创建并分离了线程,以便能够同时处理多个客户端的连接请求。 注意事项: 该代码仅为简单的示例代码,还有很多需要完善的地方,例如: - 没有对客户端发送的消息进行处理,可能存在安全漏洞。 - 没有对客户端的连接数量进行限制,可能导致服务器资源耗尽。 - 没有考虑到多线程并发情况下的线程安全问题,可能存在数据竞争等问题。 - 没有对异常情况进行处理,可能导致程序崩溃等问题。 因此,在实际使用中需要根据具体需求进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值