websocket+c语言(聊天室那些事)
1.前言
近期,大三期末结束学校组织去培训,时间虽然就15天,但是本来合计着就特么放假了,结果来个培训。而且培训内容更操蛋,一天linux,一天C语言,一天进程,一天线程,一天数据库,一天网络编程,一天黄粱一梦,就盼着自己睡醒的那天,其实说不上郁闷,反而觉得挺好的。在大环境中氛围好更容易进入学习状态,虽然黄粱一梦,可黄粱万一存在呢,对吧。想必我算是睡一半饿醒的那个吧,经过10天努力,不负众望,还是勉强写出个差不多的聊天室,学习够用了;
2.代码简介
前言说到,只是个差不多的聊天室(由于本人之前c技术也算是学校基础课程学完那种,一般用php,所以写起来许多写法等等得从从头开始学,就比如基础的字符串拼接,内存分配等等),加上websocket之前也只是了解过一点,所以功能本就不多的聊天室用了10天时间
废话说完了下面开始介绍
websocket就不说了,想必不了解也不会看到这篇文章(对了,本代码并不涉及线程与进程,只是用了select函数)
先看看具体模型:
其他绑定端口函数啥的就不讲解了,咱们细细说说socket中的select
函数原型如下:
int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
返回值:int型
参数1: maxfdp 这个值怎么说呢,他其实代表的是一个范围,当一个新连接进来后,accept函数接收到连接后会返回一个int类型的fd(fd只是个名字而已)值,由于每个fd值是自增的,所以这个第一个参数就是还在连接中最大的这个fd值加上1就等于maxfdp(官方解释自行百度)
参数2: fd_set *readset 从类型上看,故名思意,是个fd_set 的结构体,这个结构体中应当包含所有的在线的可读的fd值(就是前面说那个),结构体具体啥样自行百度
参数3: fd_set *writeset 与参数3类似,不过参数2是读,参数3是写,故名思意,就是select会阻塞到有这两个结构体中有可读可写时然后进行消息处理。
参数4:同23不多介绍
参数5:也是一个结构体,超时时间,当阻塞时间超过这个时间就会提示错误(可以直接写0)
说完了select 就来看看具体代码吧:
服务端代码分为几个文件夹:
sever.c 服务器相关代码
function.c 握手编码发送消息等相关代码
user_operate.c 用户操作类
json_op.c 生成json消息相关代码
说到websocket,其实他和socket的区别就是多了一条在服务器上握手与消息解码
主要是从http1.1升级到ws的过程
其中设计到sha_1编码和base64编码
服务器开启,浏览器使用websocket连接时,发起的请求中会有一个
Sec-WebSocket-Key:xxxxxxxxxxx 的值段
所谓的sha_1和base64就是对获取到的key进行编码
过程 :获取key->sha_1->base64
//sha_1和base64解码函数 我这直接合成一起了,sha_1直接用的库,代码后半段是base64解码 char * get_comm_key(char *key){ char *mofa="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; char *temp_data=(char *)malloc(strlen(key)+strlen(mofa)+1); sprintf(temp_data,"%s%s",key,mofa); char *base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; size_t len = strlen(temp_data); unsigned char sha1[SHA_DIGEST_LENGTH]; SHA1(temp_data,len,sha1); int sha1_len=strlen(sha1); //定义base64编码后的长度 int de_len=0; if(sha1_len%3==0){ de_len=(sha1_len/3)*4; }else{ de_len=(sha1_len/3+1)*4; } // 定义字符数组长度 char *code_value=(char *)malloc(de_len+1); code_value[de_len]='\0'; //三个一组依次写入 for(int i=0,j=0;i<sha1_len;i+=3,j+=4){ //这里是3个齐全的 if(i+3<sha1_len){ code_value[j]=base64_table[sha1[i]>>2]; code_value[j+1]=base64_table[(sha1[i] & 0x3)<<4 | sha1[i+1]>>4]; code_value[j+2]=base64_table[(sha1[i+1] & 0xf)<<2 | sha1[i+2]>>6]; code_value[j+3]=base64_table[sha1[i+2] & 0x3f]; }else{ //当没有3个时 int c=sha1_len-i; if(c==1){ code_value[j]=base64_table[sha1[i]>>2]; code_value[j+1]=base64_table[(sha1[i] & 0x3)<<4]; }else if(c==2){ code_value[j]=base64_table[sha1[i]>>2]; code_value[j+1]=base64_table[(sha1[i] & 0x3)<<4 | sha1[i+1]>>4]; code_value[j+2]=base64_table[(sha1[i+1] & 0xf)<<2]; } } } //末尾填写= switch(sha1_len % 3) { case 1: code_value[de_len-2] = '='; code_value[de_len-1] = '='; break; case 2: code_value[de_len-1] = '='; break; } free(temp_data); return code_value; }
3.具体实现
额感觉也没啥好说的直接上代码
4.代码
//sever.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/select.h> #include <stdbool.h> // #include "function.c" #include "user_operate.c" #define MAX_CONN 100 //定义最大连接数 #define MAX_BUFF_SIZE 1024 //定义最大输入输出字节数 #define IP "0.0.0.0" //ip地址 #define PORT 6001 //端口 #define MAX_LISTEN 100 //最大监听 //初始化一个socket链接 int creat_socket(const char *ip,int port,int listen_num){ int sever_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sever_sock==-1){ printf("socket创建失败,请重启机器\n"); exit(0); return -1; } struct sockaddr_in sever_config; sever_config.sin_family = AF_INET; //使用IPv4地址 sever_config.sin_addr.s_addr = inet_addr(ip); //具体的IP地址 sever_config.sin_port = htons(port); //端口 if(bind(sever_sock, (struct sockaddr*)&sever_config, sizeof(sever_config))==-1){ printf("socket初始化信息失败,请重启机器\n"); exit(0); return -1; } //进入监听状态,等待用户发起请求 if(listen(sever_sock, listen_num)==-1){ printf("监听数设置失败,请重启机器\n"); exit(0); return -1; } printf("服务器开始监听\n"); return sever_sock; } //发送函数 bool socket_send(int sock,const char *msg){ if(strlen(msg)>MAX_BUFF_SIZE){ printf("发送失败,超过最大发送字节数\n"); return false; } send(sock,msg,strlen(msg),0); return true; } //群发函数 bool socket_sends(int sock[],int sock_num,const char *msg){ if(strlen(msg)>MAX_BUFF_SIZE){ printf("发送失败,超过最大发送字节数\n"); return false; } for(int i=0;i<sock_num;i++){ send(sock[i],msg,strlen(msg),0); } return true; } //初始化链接池 //开启服务 void sever_run(int sever_socket){ //握手池 int wait_pool[MAX_LISTEN]={0}; wait_pool[0]=sever_socket; //当前等待握手数 int wait_num=1; //等待登录池 int dl_pool[MAX_LISTEN]={0}; int dl_num=0; online_users *conn_pool=init_users(); // user_data *sever_user=creat_user(0,sever_socket,"sever"); //定义群组 groups *group_pool=init_groups(); // 服务端上线 // add_users(conn_pool,sever_user); //定义maxfdp int maxfdp=0; //初始化缓冲区 fd_set read_fd; //定义接收数组与发送数组 char read_msg[MAX_BUFF_SIZE]; int uid_key=1; int gid_key=1; while(1){ //清空读缓冲区 在对缓冲区进行重置 FD_ZERO(&read_fd); maxfdp=set_fds_arr(&read_fd,wait_pool,wait_num,0); maxfdp=set_fds_arr(&read_fd,dl_pool,dl_num,maxfdp); maxfdp=set_fds(conn_pool,&read_fd,maxfdp); int ret=select(maxfdp+1,&read_fd,NULL,NULL,NULL); if(ret>0){ if(FD_ISSET(sever_socket,&read_fd)){ struct sockaddr_in new_client; socklen_t new_client_len; int new_client_conn = accept(sever_socket,(struct sockaddr *)&new_client, &new_client_len); if(new_client_conn>0){ wait_pool[wait_num]=new_client_conn; wait_num+=1; printf("新链接进来了\n"); }else{ printf("新链接链接失败\n"); } } //读取握手消息 for(int i=0;i<wait_num;i++){ memset(read_msg, 0,sizeof(read_msg)); int byte_num=recv(wait_pool[i],read_msg,MAX_BUFF_SIZE,MSG_DONTWAIT); if(byte_num>0){ // 握手 char *temp_data=computeAcceptKey(read_msg); shakeHand(wait_pool[i],temp_data); free(temp_data); dl_pool[dl_num]=wait_pool[i]; dl_num+=1; array_splice(wait_pool,wait_num,wait_pool[i]); wait_num-=1; printf("握手成功\n"); }else if(byte_num==0){ //链接断开 array_splice(wait_pool,wait_num,wait_pool[i]); printf("链接断开\n"); wait_num-=1; } } for(int i=0;i<dl_num;i++){ int len; int ret=0; memset(read_msg, 0,sizeof(read_msg)); while((len = recv(dl_pool[i],read_msg,sizeof(read_msg),MSG_DONTWAIT))>0){ ret=on_ws_recv_data((unsigned char*)read_msg,len,(char*)&read_msg); if(ret){ cJSON *msg_json=cJSON_Parse(read_msg); if(msg_json!=NULL){ int type=-1; if(cJSON_GetObjectItem(msg_json,"type")==NULL){ char *temp_msg=J_error("请输入消息类型"); ws_send_data(dl_pool[i],temp_msg,strlen(temp_msg)); free(temp_msg); printf("登录信息错误:未输入消息类型\n"); // continue; }else{ type=cJSON_GetObjectItem(msg_json,"type")->valueint; if(type==1){ if(cJSON_GetObjectItem(msg_json,"name")==NULL){ char *temp_msg=J_error("请输入名字"); ws_send_data(dl_pool[i],temp_msg,strlen(temp_msg)); free(temp_msg); printf("登录信息错误:未输入名字\n"); }else{ //获取名字 char *name=cJSON_GetObjectItem(msg_json,"name")->valuestring; //生生新信息并加入表 user_data *new_user=creat_user(uid_key,dl_pool[i],name); add_users(conn_pool,new_user); //将cid发送到客户端 char *temp_msg=j_u_init(uid_key); ws_send_data(dl_pool[i],temp_msg,strlen(temp_msg)); uid_key+=1; //移除本来在等待登录表中的id array_splice(dl_pool,dl_num,dl_pool[i]); dl_num-=1; printf("%s 登录成功\n",name); } } } cJSON_Delete(msg_json); } } } if(len==0){ array_splice(dl_pool,dl_num,dl_pool[i]); dl_num-=1; printf("链接断开\n"); } } user_data *temp_user_node=conn_pool->head; while(temp_user_node!=NULL){ int len; int ret=0; memset(read_msg, 0,sizeof(read_msg)); while((len = recv(temp_user_node->fd_int,read_msg,sizeof(read_msg),MSG_DONTWAIT))>0){ ret=on_ws_recv_data((unsigned char*)read_msg,len,(char*)&read_msg); if(ret){ cJSON *msg_json=cJSON_Parse(read_msg); if(msg_json!=NULL){ if(cJSON_GetObjectItem(msg_json,"type")!=NULL){ int type=cJSON_GetObjectItem(msg_json,"type")->valueint; if(type==2){ //用户对用户的消息 int rid=cJSON_GetObjectItem(msg_json,"r_uid")->valueint; int sid=cJSON_GetObjectItem(msg_json,"s_uid")->valueint; char *name=cJSON_GetObjectItem(msg_json,"name")->valuestring; char *s_msg=cJSON_GetObjectItem(msg_json,"msg")->valuestring; char *data=j_u_msg(sid,rid,name,s_msg); //寻址 user_data *target_user=get_user_local(conn_pool,rid); if(target_user!=NULL){ ws_send_data(target_user->fd_int,data,strlen(data)); }else{ J_error("用户已下线"); } }else if(type==3){ //创建群聊 char *name=cJSON_GetObjectItem(msg_json,"name")->valuestring; group_data *new_group=creat_group(gid_key,name,temp_user_node->uid); gid_key+=1; add_groups(group_pool,new_group); // print_group(group_pool,gid_key-1); }else if(type==4){ //加入群聊 // user_join_group(groups *ghead,online_users *uhead,int uid,int gid) int gid=cJSON_GetObjectItem(msg_json,"gid")->valueint; user_join_group(group_pool,conn_pool,temp_user_node->uid,gid); print_group(group_pool,gid); }else if(type==5){ //退出群聊 }else if(type==6){ //群发消息 int gid=cJSON_GetObjectItem(msg_json,"gid")->valueint; char *msg=cJSON_GetObjectItem(msg_json,"msg")->valuestring; char *group_msg_j=j_g_msg(temp_user_node->uid,temp_user_node->uname,gid,msg); int group_msg_len=strlen(group_msg_j); //消息转发 //群组寻址 group_data *temp_group=get_group_local(group_pool,gid); if(temp_group!=NULL){ for(int i=0;i<temp_group->join_num;i++){ int uid=temp_group->join_uid[i]; // 用户寻址 user_data *send_temp_user=get_user_local(conn_pool,uid); if(send_temp_user!=NULL){ ws_send_data(send_temp_user->fd_int,group_msg_j,group_msg_len); } } } }else if(type==8){ char *online_user=j_users(conn_pool); ws_send_data(temp_user_node->fd_int,online_user,strlen(online_user)); }else if(type==9){ char *online_group=j_groups(group_pool); ws_send_data(temp_user_node->fd_int,online_group,strlen(online_group)); } } } } } if(len==0){ printf("登录用户下线了\n"); users_del(conn_pool,group_pool,temp_user_node->uid); } temp_user_node=temp_user_node->next_user; } } } } int main(){ int sever_sock=creat_socket(IP,PORT,MAX_LISTEN); // SetNonBlock(sever_sock); sever_run(sever_sock); close(sever_sock); return 0; }
//function.c #include <stdbool.h> #include <stdlib.h> #include <openssl/sha.h> #include <string.h> #include <stdio.h> #include <sys/socket.h> #include <openssl/pem.h> #include <openssl/bio.h> #include <openssl/evp.h> #define RESPONSE_HEADER_LEN_MAX 1024 //u char 转 char void uctoc(char* str, unsigned char* UnChar, int ucLen){ int i = 0; for(i = 0; i < ucLen; i++) { //格式化输str,每unsigned char 转换字符占两位置%x写输%X写输 sprintf(str + i * 2, "%02x", UnChar[i]); } } //sha_1 返回sha1后的码 char *sha_1(char *str,int len){ //实例化一个sha unsigned char *sSHA=(unsigned char *)malloc(20); char *data=(char *)malloc(40);; SHA1((const unsigned char*)str, len, sSHA); uctoc(data,sSHA,20); return data; } //删除数组某数 bool array_splice(int arr[],int arr_len,int data){ int flag=0; for(int i=0;i<arr_len;i++){ if(arr[i]==data){ flag=1; } if(flag==1){ if(i==arr_len-1){ arr[i]=0; return true; }else{ arr[i]=arr[i+1]; } } } return false; } //检查数组中某数据是否存在 bool array_exists(int arr[],int arr_len,int data){ for(int i=0;i<arr_len;i++){ if(arr[i]==data){ return true; } } return false; } //获取Sec-WebSocket-Key的值 char * fetchSecKey(const char * buf){ char *key; char *keyBegin; char *flag="Sec-WebSocket-Key: "; int i=0, bufLen=0; key=(char *)malloc(256); memset(key,0, 256); if(!buf){ return NULL; } keyBegin=strstr(buf,flag); if(!keyBegin){ return NULL; } //读取Sec-WebSocket-Key后面的 keyBegin+=strlen(flag); bufLen=strlen(buf); for(i=0;i<bufLen;i++){ if(keyBegin[i]==0x0A||keyBegin[i]==0x0D){ break; } key[i]=keyBegin[i]; } return key; } char * get_comm_key(char *key){ char *mofa="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; char *temp_data=(char *)malloc(strlen(key)+strlen(mofa)+1); sprintf(temp_data,"%s%s",key,mofa); char *base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; size_t len = strlen(temp_data); unsigned char sha1[SHA_DIGEST_LENGTH]; SHA1(temp_data,len,sha1); int sha1_len=strlen(sha1); //定义base64编码后的长度 int de_len=0; if(sha1_len%3==0){ de_len=(sha1_len/3)*4; }else{ de_len=(sha1_len/3+1)*4; } // 定义字符数组长度 char *code_value=(char *)malloc(de_len+1); code_value[de_len]='\0'; //三个一组依次写入 for(int i=0,j=0;i<sha1_len;i+=3,j+=4){ //这里是3个齐全的 if(i+3<sha1_len){ code_value[j]=base64_table[sha1[i]>>2]; code_value[j+1]=base64_table[(sha1[i] & 0x3)<<4 | sha1[i+1]>>4]; code_value[j+2]=base64_table[(sha1[i+1] & 0xf)<<2 | sha1[i+2]>>6]; code_value[j+3]=base64_table[sha1[i+2] & 0x3f]; }else{ //当没有3个时 int c=sha1_len-i; if(c==1){ code_value[j]=base64_table[sha1[i]>>2]; code_value[j+1]=base64_table[(sha1[i] & 0x3)<<4]; }else if(c==2){ code_value[j]=base64_table[sha1[i]>>2]; code_value[j+1]=base64_table[(sha1[i] & 0x3)<<4 | sha1[i+1]>>4]; code_value[j+2]=base64_table[(sha1[i+1] & 0xf)<<2]; } } } //末尾填写= switch(sha1_len % 3) { case 1: code_value[de_len-2] = '='; code_value[de_len-1] = '='; break; case 2: code_value[de_len-1] = '='; break; } free(temp_data); return code_value; } //加密获得电脑段key char * computeAcceptKey(const char * buf){ char * clientKey=fetchSecKey(buf); // char * serverKey=(char *)malloc(100); char * serverKey=get_comm_key(clientKey); free(clientKey); printf("sever_key=%s\n",serverKey); return serverKey; } void shakeHand(int connfd,const char *serverKey){ char *responseHeader=(char *)malloc(sizeof("a")*200); if(!connfd){ return; } if(!serverKey){ return; } memset(responseHeader,'\0',200); sprintf(responseHeader, "HTTP/1.1 101 Switching Protocols\r\n"); sprintf(responseHeader, "%sUpgrade: websocket\r\n", responseHeader); sprintf(responseHeader, "%sConnection: Upgrade\r\n", responseHeader); sprintf(responseHeader, "%sSec-WebSocket-Accept: %s\r\n\r\n", responseHeader, serverKey); write(connfd,responseHeader,strlen(responseHeader)); free(responseHeader); } void ws_send_data(int fid, unsigned char* pkg_data, unsigned int pkg_len) { static unsigned char send_buffer[8196]; unsigned int send_len; // 固定的头 send_buffer[0] = 0x81; if (pkg_len <= 125) { send_buffer[1] = pkg_len; // 最高bit为0, send_len = 2; } else if (pkg_len <= 0xffff) { send_buffer[1] = 126; send_buffer[2] = (pkg_len & 0x000000ff); send_buffer[3] = ((pkg_len & 0x0000ff00) >> 8); send_len = 4; } else { send_buffer[1] = 127; send_buffer[2] = (pkg_len & 0x000000ff); send_buffer[3] = ((pkg_len & 0x0000ff00) >> 8); send_buffer[4] = ((pkg_len & 0x00ff0000) >> 16); send_buffer[5] = ((pkg_len & 0xff000000) >> 24); send_buffer[6] = 0; send_buffer[7] = 0; send_buffer[8] = 0; send_buffer[9] = 0; send_len = 10; } memcpy(send_buffer + send_len, pkg_data, pkg_len); send_len += pkg_len; send(fid, send_buffer, send_len, 0); } //数据解码 int on_ws_recv_data(unsigned char*pkg_data, int pkg_len, char* msg) { int ret =0; // 第一个字节是头,已经判断,跳过; unsigned char* mask = NULL; unsigned char* raw_data = NULL; unsigned int len = pkg_data[1]; // 最高的一个bit始终为1,我们要把最高的这个bit,变为0; len = (len & 0x0000007f); if (len <= 125) { mask = pkg_data + 2; // 头字节,长度字节 } else if (len == 126) { // 后面两个字节表示长度; len = ((pkg_data[2]) | (pkg_data[3] << 8)); mask = pkg_data + 2 + 2; } else if (len == 127){ // 这种情况不用考虑,考虑前4个字节的大小,后面不管; unsigned int low = ((pkg_data[2]) | (pkg_data[3] << 8) | (pkg_data[4] << 16) | (pkg_data[5] << 24)); unsigned int hight = ((pkg_data[6]) | (pkg_data[7] << 8) | (pkg_data[8] << 16) | (pkg_data[9] << 24)); if (hight != 0) { // 表示后四个字节有数据int存放不了,太大了,我们不要了。 return ret; } len = low; mask = pkg_data + 2 + 8; } // mask 固定4个字节,所以后面的数据部分 raw_data = mask + 4; static unsigned char recv_buf[8096]; unsigned int i; for (i = 0; i < len; i++) { recv_buf[i] = raw_data[i] ^ mask[i % 4]; // mask只有4个字节的长度,所以,要循环使用,如果超出,取余就可以了。 } recv_buf[len] = 0; int head = pkg_data[0]; if(head==129 || head==130){ memcpy(msg, recv_buf, len); ret = 1; }else{ return -1; } return ret; }
//json_op.c #include "./JSON/cJSON.c" #include <stdio.h> //错误消息 char *J_error(const char *msg){ cJSON *json_msg=cJSON_CreateObject(); cJSON_AddStringToObject(json_msg,"data",msg); //添加字符串 cJSON_AddNumberToObject(json_msg,"type",-1); char *json_data = cJSON_Print(json_msg); cJSON_Delete(json_msg); return json_data; } //用户信息 char *j_u_msg(int s_uid,int r_uid,char *name,char *msg){ cJSON *json_msg=cJSON_CreateObject(); cJSON_AddNumberToObject(json_msg,"s_uid",s_uid); cJSON_AddNumberToObject(json_msg,"r_uid",r_uid); cJSON_AddStringToObject(json_msg,"name",name); cJSON_AddStringToObject(json_msg,"msg",msg); cJSON_AddNumberToObject(json_msg,"type",2); char *json_data = cJSON_PrintUnformatted(json_msg); cJSON_Delete(json_msg); return json_data; } //群消息 char *j_g_msg(int s_uid,char *s_name,int r_gid,char *msg){ cJSON *json_msg=cJSON_CreateObject(); cJSON_AddNumberToObject(json_msg,"s_uid",s_uid); cJSON_AddStringToObject(json_msg,"name",s_name); cJSON_AddNumberToObject(json_msg,"gid",r_gid); cJSON_AddStringToObject(json_msg,"msg",msg); cJSON_AddNumberToObject(json_msg,"type",6); char *json_data = cJSON_Print(json_msg); cJSON_Delete(json_msg); return json_data; } //生成上线信息 char *j_u_init(int cid){ cJSON *json_msg=cJSON_CreateObject(); cJSON_AddNumberToObject(json_msg,"cid",cid); cJSON_AddNumberToObject(json_msg,"type",1); char *json_data = cJSON_Print(json_msg); cJSON_Delete(json_msg); return json_data; } /* type协议 -1 error 提示错误消息 1 user_conn 用户链接 2 用户对用户发消息 必须字段 s_uid r_uid name msg type 3 创建群聊 必须字段 s_uid g_name type 4 退出群聊 必须字段 s_uid gid type 5 加入群聊 必须字段 s_uid gid type 6 群发消息 必须字段 s_uid gid type msg */
//user_operate.c #include <stdio.h> #include <stdbool.h> #include <sys/select.h> #include <stdlib.h> #include <pthread.h> #include "function.c"#include "./json_op.c" #define JOIN_MAX 100 #define GROUP_MAX 100 //用户信息 typedef struct USER_DATA{ int uid; char *uname; int fd_int; int add_group_id[JOIN_MAX]; int join_num; struct USER_DATA *next_user; } user_data; //在线用户表 typedef struct ONLINE_USERS{ user_data *head; int online_num; } online_users; //群组信息 typedef struct GROUP_DATA{ int gid; char *gname; int creat_uid; int join_uid[JOIN_MAX]; int join_num; struct GROUP_DATA *next; } group_data; //所有群组 typedef struct GROUPS{ group_data *head; int creat_group_mun; } groups; //创建一个初始化用户 user_data *creat_user(int uid,int fd_int,char *uname){ char *name=(char *)malloc(strlen(name)+1); strcpy(name,uname); user_data *user=(user_data *)malloc(sizeof(user_data)); user->uid=uid; user->fd_int=fd_int; user->uname=name; user->next_user=NULL; user->join_num=0; return user; } //初始化一个用户组 online_users *init_users(){ online_users *data=(online_users *)malloc(sizeof(online_users)); data->head=NULL; data->online_num=0; return data; } //初始化一个群组 group_data *creat_group(int gid,char *gname,int uid){ group_data *data=(group_data *)malloc(sizeof(group_data)); char *name=(char *)malloc(strlen(gname)+1); strcpy(name,gname); data->gid=gid; data->creat_uid=uid; data->gname=gname; data->join_num=0; data->next=NULL; return data; } //初始化群组组 groups *init_groups(){ groups *data=(groups *)malloc(sizeof(groups)); data->head=NULL; data->creat_group_mun=0; } //群组寻址 group_data *get_group_local(groups *headNode,int gid){ group_data *temp=headNode->head; if(headNode->head==NULL){ return NULL; } for(int i=0;i<headNode->creat_group_mun;i++){ if(temp->gid==gid){ return temp; }else{ temp=temp->next; } } return NULL; } //用户寻址 user_data *get_user_local(online_users *headNode,int uid){ user_data *temp=headNode->head; if(headNode->head==NULL){ return NULL; } for(int i=0;i<headNode->online_num;i++){ if(temp->uid==uid){ return temp; }else{ temp=temp->next_user; } } return NULL; } //用户加入在线组 bool add_users(online_users *headNode,user_data *new_node){ user_data *temp=get_user_local(headNode,new_node->uid); if(temp==NULL){ new_node->next_user=headNode->head; headNode->head=new_node; headNode->online_num+=1; return true; }else{ return false; } return true; } // 将用户信息加入在线表 bool users_add(online_users *users,user_data *user){ user_data *temp=get_user_local(users,user->uid); if(temp==NULL){ user->next_user=users->head; users->head=user->next_user; users->online_num+=1; return true; }else{ return false; } } //将群组加入群组组 bool add_groups(groups *headNode,group_data *new_group){ group_data *temp=get_group_local(headNode,new_group->gid); if(temp==NULL){ new_group->next=headNode->head; headNode->head=new_group; headNode->creat_group_mun+=1; return true; }else{ return false; } } //加入群组 bool user_join_group(groups *ghead,online_users *uhead,int uid,int gid){ //找到两个地址 group_data *gtemp=get_group_local(ghead,gid); user_data *utemp=get_user_local(uhead,uid); if(gtemp!=NULL && utemp!=NULL){ //查看用户加入群组中是否有该群组 //查看群组用户群组中是否有该用户 bool exit_g=array_exists(gtemp->join_uid,gtemp->join_num,uid); bool exit_u=array_exists(utemp->add_group_id,utemp->join_num,gid); if(!exit_g && !exit_u){ gtemp->join_uid[gtemp->join_num]=uid; utemp->add_group_id[utemp->join_num]=gid; gtemp->join_num+=1; utemp->join_num+=1; return true; } } return false; } //退出群组 bool user_rm_group(groups *ghead,online_users *uhead,int uid,int gid){ //找到两个地址 group_data *gtemp=get_group_local(ghead,gid); user_data *utemp=get_user_local(uhead,uid); if(gtemp!=NULL && utemp!=NULL){ //查看用户加入群组中是否有该群组 //查看群组用户群组中是否有该用户 bool exit_g=array_exists(gtemp->join_uid,gtemp->join_num,uid); bool exit_u=array_exists(utemp->add_group_id,utemp->join_num,gid); if(exit_g && exit_u){ array_splice(gtemp->join_uid,gtemp->join_num,uid); array_splice(utemp->add_group_id,utemp->join_num,gid); gtemp->join_num-=1; utemp->join_num-=1; return true; } } return false; } //用户下线 将用户移出在线表 bool users_del(online_users *uhead,groups *ghead,int uid){ if(uhead->head->uid==uid){ if(uhead->head->join_num!=0){ while (uhead->head->join_num!=0){ user_rm_group(ghead,uhead,uid,uhead->head->add_group_id[0]); } } uhead->head=uhead->head->next_user; uhead->online_num-=1; return true; }else{ user_data *temp=uhead->head; while(temp->next_user!=NULL){ if(temp->next_user->uid==uid){ while(temp->next_user->join_num!=0){ user_rm_group(ghead,uhead,uid,temp->next_user->add_group_id[0]); } temp->next_user=temp->next_user->next_user; uhead->online_num-=1; return true; } temp=temp->next_user; } return false; } return false; } //循环在线表 void printall(online_users *head){ user_data *temp=head->head; while (temp!=NULL){ printf("用户name为:%s\n",temp->uname); printf("用户uid为:%d\n",temp->uid); printf("用户加入群聊数为:%d\n",temp->join_num); for(int i=0;i<temp->join_num;i++){ printf("%d\t",temp->add_group_id[i]); } printf("\n"); temp=temp->next_user; } } //打印群组信息 void print_group(groups *headNode,int gid){ group_data *temp=get_group_local(headNode,gid); if(temp!=NULL){ printf("群名:%s\n",temp->gname); printf("群组成员数为:%d \n",temp->join_num); printf("群组成员为\n"); for(int i=0;i<temp->join_num;i++){ printf("%d\t",temp->join_uid[i]); } printf("\n"); }else{ printf("gid不存在\n"); } } //打印用户信息 void print_user(online_users *headNode,int uid){ user_data *temp=get_user_local(headNode,uid); if(temp!=NULL){ printf("用户name为:%s\n",temp->uname); printf("用户uid为:%d\n",temp->uid); printf("用户加入群聊数为:%d\n",temp->join_num); for(int i=0;i<temp->join_num;i++){ printf("%d\t",temp->add_group_id[i]); } printf("\n"); }else{ printf("uid不存在\n"); } } //将所有链接上的fd放进一个fd中 并设置最大maxfd int set_fds(online_users *head,fd_set *fds,int maxfdp){ int temp_int=maxfdp; user_data *temp=head->head; while(temp!=NULL){ FD_SET(temp->fd_int,fds); if(temp->fd_int>temp_int){ temp_int=temp->fd_int; } temp=temp->next_user; } return temp_int; } //将数组中的值加入fds中 int set_fds_arr(fd_set *fds,int *arr,int arr_len,int maxfdp){ int temp_int=maxfdp; for(int i=0;i<arr_len;i++){ FD_SET(arr[i],fds); if(arr[i]>temp_int){ temp_int=arr[i]; } } return temp_int; } //生成群聊信息 char *j_groups(groups *headNode){ group_data *temp=headNode->head; cJSON *json_msg=cJSON_CreateArray(); cJSON *json_msg1=cJSON_CreateObject(); while(temp!=NULL){ cJSON *temp_j = cJSON_CreateObject(); cJSON_AddStringToObject(temp_j,"name",temp->gname); cJSON_AddNumberToObject(temp_j,"gid",temp->gid); cJSON_AddItemToArray(json_msg,temp_j); temp=temp->next; } cJSON_AddItemToObject(json_msg1,"group",json_msg); cJSON_AddNumberToObject(json_msg1,"type",9); char *data2=cJSON_PrintUnformatted(json_msg1); cJSON_free(json_msg); cJSON_free(json_msg1); return data2; } //生成用户信息 char *j_users(online_users *headNode){ user_data *temp=headNode->head; cJSON *json_msg=cJSON_CreateArray(); cJSON *json_msg1=cJSON_CreateObject(); // cJSON *json_num=cJSON_CreateObject(); while(temp!=NULL){ cJSON *temp_j = cJSON_CreateObject(); cJSON_AddStringToObject(temp_j,"name",temp->uname); cJSON_AddNumberToObject(temp_j,"uid",temp->uid); cJSON_AddItemToArray(json_msg,temp_j); // cJSON_free(temp_j); temp=temp->next_user; } cJSON_AddItemToObject(json_msg1,"users",json_msg); cJSON_AddNumberToObject(json_msg1,"type",8); char *data2=cJSON_PrintUnformatted(json_msg1); cJSON_free(json_msg); cJSON_free(json_msg1); return data2; }
5.前端测试代码
//html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>聊天测试</title> <link rel="stylesheet" href="./css/main.css"> </head> <body> <header class="header"> <!-- 最顶部标题部分 --> <div class="menu"> <button id="btn3">刷新列表</button> <button id="btn1">在线人员</button> <button id="btn2">在线群聊</button> <button id="btn4">创建群聊</button> </div> </header> <div class="content_body"> <!-- 内容显示区域 --> <!-- 左边菜单 --> <div class="left_menu"> <ul class="left_menu_ul"> <!-- <li s_uid="2" msg_type="1" id="msg_card_1_1"><img src="./img/纸飞机.png" alt=""><span class="li_type">私聊:xxxx</span><span class="msg_flag"></span></li> --> </ul> </div> <!-- 右边内容区 --> <div class="content_left"> <!-- 消息区 --> <div class="msg_region"></div> <div class="online_user"></div> <div class="groups"></div> <!-- 消息输入区 --> <div class="input_text"> <textarea id="msg_input"></textarea> <button id="send_msg">发送</button> </div> </div> </div> <script src="./js/main.js"></script> </body> </html>
// css/main.css *{ padding: 0px; margin: 0px; } html{ width: 100vw; height: 100vh; } body{ width: 100%; height: 100%; } .header{ width: 100%; height: 50px; background-color: rgb(230, 230, 230); } .content_body{ width: 100%; height: calc(100% - 50px); background-color: rgb(179, 180, 181); display: flex; } .input_area{ width: 100%; height: 20%; background-color: rgb(69, 84, 97); } .menu{ width: 100%; height: 100%; display: flex; align-items: center; /* text-align: center; */ /* inline-size: 50px; */ } .menu button{ width: 100px; height: 40px; border: 0px; font-size: 16px; background-color: rgb(193, 193, 193); border-radius: 5px; /* margin-left: 20px; */ } #btn1{ margin-left: 10px; } #btn2{ margin-left: 10px; } #btn4{ margin-left: 10px; } #btn3{ margin-left: 20px; } .left_menu{ width: 15%; height: 100%; background-color: antiquewhite; } .left_menu_ul{ width: 100%; height: 100%; background-color: rgb(195, 195, 195); overflow: scroll; } .left_menu_ul::-webkit-scrollbar { display: none; } .left_menu_ul li{ width: 100%; height: 50px; border-bottom:1px black solid ; display: flex; align-items: center; position: relative; user-select: none; /* color: aliceblue; */ } .left_menu_ul li img{ width: 50px; height: 50px; } .msg_flag{ position: absolute; right: 10px; display: block; width: 20px; height: 20px; /* background-color: rgb(250, 67, 67); */ border-radius: 50%; display: none; } .content_left{ width: 85%; height: 100%; } .msg_region{ width: 100%; height: 90%; background-color: rgb(204, 204, 204); overflow: scroll; display: none; } .msg_region::-webkit-scrollbar { display: none; } .msg_card{ width: 80%; /* height: 70px; */ background-color: rgb(235, 236, 236); padding: 10px 5px; border-radius: 5px; margin-top: 5px; } .msg_card .msg_card_name{ height: 30px; width: 50px; line-height: 30px; text-align: center; background-color: rgb(198, 202, 202); margin-top: 5px; } .msg_card_content{ margin-top: 10px; } .card_right{ margin-left: 20%; } .input_text{ width: 100%; height: 9%; display: flex; } #msg_input{ display: inline-block; width: 92%; height: 100%; outline: none; font-size: 20px; /* line-height: 100%; */ resize:none; } #send_msg{ border: 0px; display: inline-block; width: 8%; height: 100%; font-size: 30px; } .online_user{ width: 100%; height: 90%; background-color: rgb(204, 204, 204); overflow: scroll; display: flex; flex-wrap: wrap; justify-content: center; align-content: flex-start; /* display: none; */ } .online_user::-webkit-scrollbar { display: none; } .online_user_card{ width: 80%; height: 50px; background-color: rgb(196, 196, 196); margin-top: 5px; display: flex; justify-content: space-between; align-items: center; border-radius: 10px; } .online_name{ margin-left: 10px; } .online_user_card button{ margin-right: 10px; width: 80px; height: 35px; border: 0px; border-radius: 7px; /* background-color: rgb(116, 212, 248); */ } .online_user_card:nth-child(1){ margin-top: 20px; } .groups{ width: 100%; height: 90%; background-color: rgb(204, 204, 204); overflow: scroll; display: flex; flex-wrap: wrap; justify-content: center; align-content: flex-start; display: none; } .groups::-webkit-scrollbar { display: none; } .group_card{ width: 80%; height: 50px; background-color: rgb(196, 196, 196); margin-top: 5px; display: flex; justify-content: space-between; align-items: center; border-radius: 10px; } .online_name{ margin-left: 10px; } .group_card button{ margin-right: 10px; width: 80px; height: 35px; border: 0px; border-radius: 7px; /* background-color: rgb(116, 212, 248); */ } .group_card:nth-child(1){ margin-top: 20px; }
// js/main.js //定义左边菜单 // var left_menu=Array //2是群聊 1是私聊 var user_groups=new ALLUSERMSGS(); var myid=1; var my_name=null; //定义在线人数 var online_user=new Array(); //定义群聊 var online_groups=new Array(); var win_statu={ type:0, uid:0, } function MENU_CARD(type,name,id){ this.type=type; this.name=name; this.id=id; } //私聊信息 function Private(s_uid,name,msg){ this.s_uid=s_uid this.msg=msg; this.name=name; } //私聊结构体 function Private_Array(uid_gentle,msg_type,s_name){ this.uid_gentle=uid_gentle; this.msgs=new Array(); this.msgs_num=0; this.msg_type=msg_type; this.s_name=s_name; } //所有私聊合集 function ALLUSERMSGS(){ this.user_num=0; this.user_array=new Array(); } function user_msg_add(user_groups,s_uid,uid_gentle,s_name,msg,msg_type){ for(var i=0;i<user_groups.user_num;i++){ if(user_groups.user_array[i].uid_gentle==uid_gentle && user_groups.user_array[i].msg_type==msg_type){ let data=new Private(s_uid,s_name,msg); user_groups.user_array[i].msgs.push(data) user_groups.user_array[i].msgs_num+=1; return true; } } return false; } function user_add(user_groups,uid_gentle,msg_type,t_name){ for(var i=0;i<user_groups.user_num;i++){ if(user_groups.user_array[i].uid_gentle==uid_gentle && user_groups.user_array[i].msg_type==msg_type){ return false; } } var new_groups=new Private_Array(uid_gentle,msg_type,t_name); user_groups.user_array.push(new_groups); user_groups.user_num+=1; return true; } //查询消息 function require_msg(user_groups,uid_gentle,msg_type){ for(var i=0;i<user_groups.user_num;i++){ if(user_groups.user_array[i].uid_gentle==uid_gentle&&user_groups.user_array[i].msg_type==msg_type){ return user_groups.user_array[i]; } } return null; } //删除与某用户的消息组 function del_user_msg(user_groups,uid_gentle,msg_type){ for(var i=0;i<user_groups.user_num;i++){ if(user_groups.user_array[i].uid_gentle==uid_gentle&&user_groups.user_array[i].msg_type==msg_type){ user_groups.user_array.splice(i, 1); user_groups.user_num-=1; return true; } } return false; } function print_menu(user_groups){ var left_menu_ul=document.getElementsByClassName("left_menu_ul")[0]; left_menu_ul.innerHTML=""; for(var i=0;i<user_groups.user_num;i++){ let s_uid=user_groups.user_array[i].uid_gentle; let msg_type=user_groups.user_array[i].msg_type; let id="msg_card_"+s_uid+"_"+msg_type; let name=user_groups.user_array[i].s_name; left_menu_ul.innerHTML+="<li data-s_uid=\""+s_uid+"\" data-msg_type=\""+msg_type+"\" id=\""+id+"\">"+ "<img src=\"./img/纸飞机.png\">"+ "<span class=\"li_type\">"+name+"</span>"+ "<span class=\"msg_flag\"></span>"+ "</li>"; } } function print_online_user(data){ let online_user=document.getElementsByClassName("online_user")[0]; online_user.innerHTML=""; for(var i=0;i<data.length;i++){ let uid=data[i].uid; let name=data[i].name; online_user.innerHTML+="<div class=\"online_user_card\" data-uid=\""+uid+"\" data-name=\""+name+"\">"+ "<div class=\"online_name\">"+name+"</div>"+ "<button id=\"build_conn\">和他聊天</button>"+ "</div>"; } } function print_groups(data){ let groups=document.getElementsByClassName("groups")[0]; groups.innerHTML=""; for(var i=0;i<data.length;i++){ let gid=data[i].gid; let name=data[i].name; groups.innerHTML+="<div class=\"group_card\" data-uid=\""+gid+"\" data-name=\""+name+"\">"+ "<div class=\"online_name\">"+name+"</div>"+ "<button id=\"build_conn\">加入群聊</button>"+ "</div>"; } } function print_content_msg(user_groups,uid_gentle,msg_type,myid){ let msg_region=document.getElementsByClassName("msg_region")[0]; msg_region.innerHTML=""; var temp_msgs=null; for(var i=0;i<user_groups.user_num;i++){ if(user_groups.user_array[i].uid_gentle==uid_gentle&&user_groups.user_array[i].msg_type==msg_type){ temp_msgs=user_groups.user_array[i]; for(var j=0;j<temp_msgs.msgs_num;j++){ if(temp_msgs.msgs[j].s_uid==myid){ let name=temp_msgs.msgs[j].name; let msg=temp_msgs.msgs[j].msg; msg_region.innerHTML+="<div class=\"msg_card card_right\">"+ "<div class=\"msg_card_name\">"+name+"</div>"+ "<div class=\"msg_card_content\">"+msg+"</div>"+ "</div>"; }else{ let name=temp_msgs.msgs[j].name; let msg=temp_msgs.msgs[j].msg; msg_region.innerHTML+="<div class=\"msg_card\">"+ "<div class=\"msg_card_name\">"+name+"</div>"+ "<div class=\"msg_card_content\">"+msg+"</div>"+ "</div>"; } } } } } //左边菜单 var left_menu_ul=document.getElementsByClassName("left_menu_ul")[0]; //打印左侧菜单栏 print_menu(user_groups); //绑定左侧菜单对应事件 left_menu_ul.addEventListener("click",function(e){ var target=e.target; if(target.tagName=="LI"){ document.getElementsByClassName("online_user")[0].style.display="none"; document.getElementsByClassName("groups")[0].style.display="none"; document.getElementsByClassName("msg_region")[0].style.display="block"; // left_menu_ul.style.display="block" let s_uid=target.dataset["s_uid"]; let msg_type=target.dataset["msg_type"]; if(myid==-1){ alert("请刷新页面"); }else{ if(msg_type==2){ win_statu.type=2; win_statu.uid=s_uid; print_content_msg(user_groups,s_uid,msg_type,myid); }else{ win_statu.type=1; win_statu.uid=Number(s_uid)-myid; print_content_msg(user_groups,s_uid,msg_type,myid); } } } }) /* type协议 -1 error 提示错误消息 1 user_conn 用户链接 2 用户对用户发消息 必须字段 s_uid r_uid name msg type 3 创建群聊 必须字段 s_uid g_name type 4 退出群聊 必须字段 s_uid gid type 5 加入群聊 必须字段 s_uid gid type 6 群发消息 必须字段 s_uid gid type msg */ // type 2 function user_tu_user(s_uid,r_uid,name,msg){ var data={ "s_uid":Number(s_uid), "r_uid":Number(r_uid), "name":name, "msg":msg, type:2 } return JSON.stringify(data); } // type 3 function u_c_group(s_uid,g_name,type){ var data={ "s_uid":Number(s_uid), "g_name":g_name, "type":3 } return JSON.stringify(data); } // type 4 function u_j_group(s_uid,gid){ var data={ "s_uid":Number(s_uid), "gid":Number(gid), "type":4 } return JSON.stringify(data); } // type 5 function u_r_group(s_uid,gid){ var data={ "s_uid":Number(s_uid), "gid":Number(gid), "type":5 } return JSON.stringify(data); } function user_to_group(s_uid,gid,msg){ var data={ "s_uid":Number(s_uid), "gid":Number(gid), "msg":msg, "type":6, } return JSON.stringify(data); } function user_dl(name){ var data={ "name":name, "type":1 } return JSON.stringify(data); } var ws = new WebSocket("ws://192.168.43.64:6001") ws.onopen = function(event){ my_name=prompt("请输入你的名字"); while(my_name==undefined){ my_name=prompt("请输入你的名字"); } this.send(user_dl(my_name)); } ws.onmessage = function(event){ var data=JSON.parse(event.data); let type=data.type; console.log(data); // console.log(data); if(data.type==-1){ alert(data.msg); }else if(type==1){ //用户登陆 myid=Number(data.cid); alert("登陆成功"); }else if(type==2){ //用户对用户 let s_uid=Number(data.s_uid); let r_uid=Number(data.r_uid); let name=data.name; let msg=data.msg; let join_flag=false for(var i=0;i<user_groups.user_num;i++){ if(user_groups.user_array[i].uid_gentle==(s_uid+r_uid)&&user_groups.user_array[i].msg_type==1){ user_groups.user_array[i].msgs.push(new Private(s_uid,name,msg)); user_groups.user_array[i].msgs_num+=1; join_flag==true; if(win_statu.uid==s_uid){ print_content_msg(user_groups,Number(s_uid)+myid,1,myid); } return 0; } } if(join_flag==false){ user_add(user_groups,s_uid+r_uid,1,name); user_msg_add(user_groups,s_uid,s_uid+r_uid,name,msg,1); } print_menu(user_groups); print_content_msg(user_groups,s_uid+r_uid,type,myid); }else if(type==6){ console.log("群聊消息为:",data); let s_uid=Number(data.s_uid); let gid=Number(data.gid); let name=data.name; let msg=data.msg; user_msg_add(user_groups,s_uid,gid,name,msg,2); print_content_msg(user_groups,gid,2,myid); }else if(type==7){ //群聊消息 online_groups=data.groups; }else if(type==8){ //用户信息 online_user=data.users; print_online_user(online_user); }else if(type==9){ //群聊信息 online_groups=data.group; console.log(data); print_groups(online_groups); } } ws.onclose = function(event){ } //在线人员 document.getElementsByClassName("online_user")[0].addEventListener("click",function(e){ var target=e.target; if(target.id=="build_conn"){ let father_node=target.parentNode; let uid=father_node.dataset["uid"]; let name=father_node.dataset["name"]; let statu=user_add(user_groups,Number(uid)+myid,1,name); if(statu){ alert("添加成功"); print_menu(user_groups); }else{ alert("添加失败"); } } }); //加入群聊 document.getElementsByClassName("groups")[0].addEventListener("click",function(e){ var target=e.target; if(target.id=="build_conn"){ let father_node=target.parentNode; let uid=father_node.dataset["uid"]; let name=father_node.dataset["name"]; let statu=user_add(user_groups,uid,2,name); if(statu){ alert("添加成功"); print_menu(user_groups); ws.send(u_j_group(myid,uid)); }else{ alert("添加失败"); } } }); //发送消息 document.getElementById("send_msg").onclick=function(){ let msg_input=document.getElementById("msg_input"); if(msg_input.value==undefined){ }else{ // console.log(msg_input.value); if(win_statu.type==1){ //同理 user_msg_add(user_groups,myid,Number(win_statu.uid)+myid,my_name,msg_input.value,1); print_content_msg(user_groups,win_statu.uid+myid,1,myid); ws.send(user_tu_user(myid,win_statu.uid,my_name,msg_input.value)); }else if(win_statu.type==2){ ws.send(user_to_group(myid,win_statu.uid,msg_input.value)); }else{ alert("发送失败,请选择发送对象"); } } } document.getElementById("btn1").onclick=function(){ document.getElementsByClassName("online_user")[0].style.display="flex"; document.getElementsByClassName("msg_region")[0].style.display="none"; document.getElementsByClassName("groups")[0].style.display="none"; let a={"type":8} ws.send(JSON.stringify(a)); } document.getElementById("btn2").onclick=function(){ document.getElementsByClassName("online_user")[0].style.display="none"; document.getElementsByClassName("msg_region")[0].style.display="none"; document.getElementsByClassName("groups")[0].style.display="flex"; let a={"type":9 } ws.send(JSON.stringify(a)); } document.getElementById("btn3").onclick=function(){ print_menu(user_groups); } document.getElementById("btn4").onclick=function(){ my_name=prompt("请输入群聊的名字"); if(my_name==undefined){ alert("创建失败"); }else{ let a={"type":3,"name":my_name} ws.send(JSON.stringify(a)); alert("成功"); } // user_msg_add(user_groups,myid,Number(win_statu.uid)+myid,my_name,msg_input.value,2); }
7.测试截图
先打开编译好的C语言程序,编译代码如下
运行程序
ps:如果出现socket初始化失败啥的请检查端口或者IP地址什么的(关闭后得等上2分钟左右才能继续开,除非换个端口)
打开前端页面 输入名字
后台提示登陆成功
按照步骤多来几个
然后创建群聊
加入群聊
在线群聊->加入群聊
将三个用户都加入群聊
后端显示
点击群聊开始聊天
用户:小白
其他用户
ps:到这就结束了,私聊同理(已经实现)有需要得自己下下来测试;还有就是,测试时请将前端连接ip换成自己的,毕竟每个人不一样,ip获得办法
连接上一个ipv4网
然后ip addr
那个192.168.43.64一看就像是ipv4网填写进去就完事了
ps:虚拟机跑的话记得sever中的地址写0.0.0.0 非要问为什么的自行百度
到着就完结了,有什么需要问的还请 企鹅:2691467667
再附加百度盘连接(前端+服务端)
链接: https://pan.baidu.com/s/1DaVhZct2MhDDdG6SRtPCcA?pwd=pajx 提取码: pajx
文中没发出来的都是可以从网上下到的库,前端代码回头再发出来