文章目录

  • 前言
  • 头文件
  • 服务器端代码
  • 客户端代码
    • 字库源码头文件
    • 头文件
    • 字库源码
    • 客户端主函数

前言

这里编写了一个即时通讯工具,不同的客户端可以通过连接服务器进行聊天互动,发表情和传文件;
数据量比较多,因此用了几个链表方便存储管理数据,看代码从主函数开始看起,按照主函数的脉络会发现主要实现的逻辑关系为:登陆——刷新好友列表——连接其他客户端——聊天/发送表情/发送文件——关闭聊天界面;

字库源码头文件代码行数太多,一篇博客写不下。需要的人留言,我看到后会抽空发给你。

头文件

#ifndef MYIOHEAD_H_
#define MYIOHEAD_H_
#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <stdbool.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/socket.h>
#include <dirent.h>
#include <linux/input.h>
// #include "kernel_list.h"
#include <termio.h>
#include <bits/signum.h>
#include <signal.h>
#include <semaphore.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>#endif

服务器端代码

#include "myiohead.h"int mysocketfd;
int newsock;
//创建结构体 --对方客户端
struct  sockaddr_in clientaddr;//定义一个结构体表示双向循环列表,为了省事把此双向链表类型定义为dlist
typedef struct doublelist
{char ip[16]; //存放ipunsigned short someport; //存放端口号int sock; //存放套接字int wz;struct doublelist *next;struct doublelist *fnext;
}dlist;//初始化链表头
dlist * init_list()
{dlist *head = calloc(1,sizeof(dlist));bzero(head->ip,16);bzero(&head->someport,sizeof(head->someport));bzero(&head->sock,sizeof(head->sock));head->next = head;head->fnext = head;return head;
}dlist *mydlist;
dlist *otherdlist;//核对账号密码
int detection()
{//打开保存注册名字的文件int fd = open("a.txt",O_RDWR);if (-1 == fd){perror("打开源文件失败");exit(0);}//注册/核对账号密码char flag[50];while(1){bzero(flag,50);int ret = read(newsock,flag,50);//接受客户端发送的信息if (-1 == ret){perror("读取失败");exit(0);}else if (0==ret){continue;}if (strcmp(flag,"zhuce") == 0)//说明客户端想注册账号密码{lseek(fd,0,SEEK_SET);read(newsock,flag,50);//接受客户端发送的新用户名信息write(fd,flag,sizeof(flag));lseek(fd,12,SEEK_SET);read(newsock,flag,50);//接受客户端发送的新密码信息write(fd,flag,sizeof(flag));lseek(fd,12,SEEK_SET);printf("注册成功,请重新登陆\n");}else if(strcmp(flag,"hedui") == 0)//说明客户端想核对账号密码{char usename[50] = {0};char password[50] = {0};char buf[100] = {0};char buff[100] = {0};bzero(usename,50);bzero(password,50);read(newsock,usename,50);//接受客户端发送的用户名信息read(newsock,password,50);//接受客户端发送的密码信息for (int i = 0; i < 100; ++i){lseek(fd,24*i,SEEK_SET);read(fd,buf+i,12);//把注册名存到buf中lseek(fd,24*i+12,SEEK_SET);read(fd,buff+i,12);//把密码存到buff中}int i = 0;while ((strcmp(usename,buf+i) != 0) || (strcmp(password,buff+i) != 0)){i++;if (i = 99){break;}}if ((strcmp(usename,buf+i) == 0) && (strcmp(password,buff+i) == 0)){bzero(flag,50);write(newsock,"right",50);printf("账号密码正确\n");break;}else{bzero(flag,50);write(newsock,"notright",50);printf("账号或密码错误\n");}}}
}void * sendlist(void * n)
{int i = 0;char msg[100] = {0};otherdlist=init_list();  dlist *p = mydlist;dlist *q = otherdlist;dlist *r = (dlist *)n;while(p->next != mydlist){p = p->next;i++;bzero(msg,100);        strcpy(msg,"ip:");strcat(msg,p->ip);if(p->sock==r->sock){strcat(msg,"(myself) ");} elsestrcat(msg,"(friend) ");//插入到链表的尾部q =otherdlist;while(q->next != otherdlist){q = q->next;}//把文件描述符存放到链表中dlist *newnode = malloc(sizeof(dlist));strcpy(newnode->ip,p->ip);newnode->someport = p->someport;newnode->wz = i;newnode->next = otherdlist;q->next = newnode;otherdlist->fnext = newnode;       newnode->fnext = q;write(r->sock,msg,strlen(msg));    sleep(1);printf("%s:%s\n",p->ip,msg );}write(r->sock,"over",strlen("over"));pthread_exit(NULL);
}void * task(void * n)
{char command[50] = {0};bzero(command,50);dlist *r = (dlist *)n;while(1){bzero(command,50);read(r->sock,command,50);//接受客户端发送的指令信息if (strcmp(command,"dlqq")==0)//核对账号密码{detection();}if (strcmp(command,"getlist")==0)//刷新在线列表{pthread_t id;pthread_create(&id,NULL,sendlist,(dlist *)n);}if (strcmp(command,"othertalking")==0)//要聊天{char a[50];bzero(a,50);read(r->sock,a,50);//接受客户端发送的指令信息printf("我收到的定位是%d\n", *a);dlist *p = mydlist;dlist *q = otherdlist;for (int i = 0; i < *a; ++i){               q = q->next;}while(strcmp(q->ip,p->ip) !=0){p = p->next;}if (strcmp(r->ip,p->ip) ==0){printf("ip:%s\n",p->ip );write(r->sock,"youself",strlen("youself"));}else{write(r->sock,"您已经进入和其他人聊天的流程",strlen("您已经进入和其他人聊天的流程"));char msg[50];while(1){                   bzero(msg,50);printf("我进入了这里\n");read(r->sock,msg,50);//接受客户端发送的指令信息if (strcmp(msg,"pleaseoverit") ==0){break;}strcat(msg,"  ");write(p->sock,msg,strlen(msg));write(p->sock,"  ",strlen("  "));printf("%s发送给%s的信息是:%s\n",r->ip,p->ip,msg );}}}}    }int main(int argc, char const *argv[])
{//signal(2,finish);mydlist=init_list();//创建tcp套接字mysocketfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == mysocketfd){perror("创建套接字失败!");exit(0);}//创建结构体 --放自己服务器的ipv4地址和端口号struct sockaddr_in mysockaddr;//清空结构体bzero(&mysockaddr,sizeof(mysockaddr));//给结构体里面赋值mysockaddr.sin_family = AF_INET;//保存地址协议mysockaddr.sin_addr.s_addr = htonl(INADDR_ANY);//最终存放要绑定的服务器端ip地址//不要存1024以内的端口后,因为很多都被Linux系统占用了mysockaddr.sin_port = htons(6000);    //存放要绑定的端口号//清空结构体--对方bzero(&clientaddr,sizeof(clientaddr));int addrsize = sizeof(clientaddr);//设定端口号可以重复使用int on = 1;//on一定要设置成大于0的常数setsockopt(mysocketfd, SOL_SOCKET,SO_REUSEADDR, &on,sizeof(on));//绑定服务器ip和端口号int ret = bind(mysocketfd, (struct sockaddr *)&mysockaddr, sizeof(mysockaddr));if (-1 == ret){perror("绑定ip和端口号失败");exit(0);}//监听ret = listen(mysocketfd,8);//最多允许8个客户端同时连接此服务器if (-1 == ret){perror("监听失败!");exit(0);}while(1){//接收客户端的连接请求newsock = accept(mysocketfd,(struct sockaddr *)&clientaddr,&addrsize);if (-1 == newsock){perror("接收客户端的连接请求失败");exit(0);}printf("有新的客户端连接成功了,新的文件描述符:%d\n",newsock);printf("有新的客户端连接成功了,他的IP地址是:%s\n",inet_ntoa(clientaddr.sin_addr));printf("有新的客户端连接成功了,他的端口号是:%hu\n",ntohs(clientaddr.sin_port));//把文件描述符存放到链表中dlist *newnode=malloc(sizeof(dlist));newnode->sock=newsock;strcpy(newnode->ip,inet_ntoa(clientaddr.sin_addr));newnode->someport=ntohs(clientaddr.sin_port);//插入到链表的尾部dlist * p =mydlist;while(p->next != mydlist){p = p->next;}// dlist *newnode = calloc(1,sizeof(dlist));// strcpy(newnode->data, newdata);newnode->next = mydlist;p->next = newnode;mydlist->fnext = newnode;newnode->fnext = p;//创建线程-->给客户端回复需要的信息pthread_t id;pthread_create(&id,NULL,task,newnode); }return 0;
}

客户端代码

字库源码头文件

此处代码数太多,想要的人可以留言,我看到后会抽空发给你;

头文件


#ifndef __PROJECT_H__
#define __PROJECT_H__//声明性的语句(不需分配数据存储空间),可存放于头文件#define  FB_SIZE  800*480*4typedef struct User_Info{   //用户信息结构char Tel[20];char Name[30]; char Passwd[6];unsigned char Gender;unsigned char Age;unsigned short Id;int Money;//unsigned char pic[0];//unsigned char *pic;      }UInfo,*pUInfo;typedef struct User_Data{   //用户请求服务数据int Value;}UData,*pUData;typedef int  (*WinP_fn)(pUInfo,pUData);  //定义窗口服务函数指针typedef struct Windows_Info{unsigned int PCount;  //窗口的服务次数unsigned int TCount;  //窗口的服务时长WinP_fn Process_fn;   //窗口的服务函数unsigned short UsrId[100];       //该窗口的服务用户Idunsigned char Total;  //总服务人数unsigned short CurIndex; //当前服务的用户序号}WInfo,*pWInfo;//BMP文件信息头  14 BYTE
typedef struct BMPfileHeader {
unsigned short bfType;
unsigned int bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned int bfOffBits;
}__attribute__ ((packed)) BMPFH, *pBMPFH;//BMP位图信息头  40 BYTE
typedef struct BMPInfoHeader{
unsigned int biSize;
unsigned long  biWidth;
unsigned long  biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
unsigned long  biXPelsPerMeter;
unsigned long  biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
}__attribute__ ((packed)) BMPIH, *pBMPIH;typedef struct Pos {int x;int y;}POS,*pPOS; typedef struct Key {unsigned char sn;char cn;int x;int y;char pic_name[30];
}KEY,*pKEY; extern int Win0_Process_fn(pUInfo Usr,pUData Data);
extern int Win1_Process_fn(pUInfo Usr,pUData Data);
extern int Win2_Process_fn(pUInfo Usr,pUData Data);
extern int Win3_Process_fn(pUInfo Usr,pUData Data);
extern int Win4_Process_fn(pUInfo Usr,pUData Data);
extern int Win5_Process_fn(pUInfo Usr,pUData Data);
extern int Win6_Process_fn(pUInfo Usr,pUData Data);
extern int Win7_Process_fn(pUInfo Usr,pUData Data);#endif  //__PROJECT_H__

字库源码


#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "project.h"  //  #include <xxx.h> 默认搜索标准库的头文件路径  /usr/include//  #include "xxx.h" 默认先搜索当前路径,再搜索标准库的头文件路径  /usr/include#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "font_libs.h"int LCD_fd;
unsigned long *fb_mem  = NULL;
unsigned char *bmp_buf = NULL;int Init_Lcd(void){int ret;//申请LCD操作许可、LCD_fd = open("/dev/fb0",O_RDWR);if(LCD_fd == -1){perror("Open LCD");return -1;}//显存映射fb_mem = (unsigned long *)mmap(NULL,    //程序空间的映射地址 , 不确定可以赋值NULL,由系统分配FB_SIZE, //映射的内存的大小    LCD 800*480*4PROT_READ |  PROT_WRITE,         //映射区的保护操作           PROT_EXEC  Pages may be executed.//                           PROT_READ  Pages may be read.//                           PROT_WRITE Pages may be written.//                           PROT_NONE  Pages may not be accessed.MAP_SHARED,       //共享标志            MAP_SHARED     MAP_PRIVATELCD_fd,           //目标文件描述符 ----> 内存映射的目标文件0);   //映射内存的偏移大小   默认从头开始,0if(fb_mem == MAP_FAILED)  {perror("Mmap LCD");return -1;    }//Show_Bmp(0,0,"logo.bmp");return 0;}void  UnInit_Lcd(void){free(bmp_buf);//撤销映射munmap(fb_mem,   //映射后的地址,通过mmap返回的值FB_SIZE); //映射的大小 close(LCD_fd); }int Show_Bmp(int X, int Y,const char *bmpfile){FILE *filp;int x,y,i,ret;int width,height,bits;BMPFH  BmpFH;      //BMP文件信息头BMPIH  BmpIH;    //BMP位图信息头//加载一张BMP位图// 打开BMP,获得操作许可filp = fopen(bmpfile,"r");if(filp == NULL){perror("Fppen BMP");return -1;}    //获取BMP的文件信息头  14BYTEfread(&BmpFH,sizeof(BMPFH),1,filp); //printf("TYPE: %c",BmpFH.bfType & 0xFF);     //显示bfType的低字节//printf("%c \n",(BmpFH.bfType >> 8) & 0xFF); //显示bfType的高字节//printf("FILE SIZE: %d \n",BmpFH.bfSize);//获取BMP的位图信息头fread(&BmpIH,sizeof(BMPIH),1,filp);//printf("BIT: %d \n",BmpIH.biBitCount);//printf("RGB SIZE: %d \n",BmpIH.biSizeImage);   //printf("x width: %d \n",BmpIH.biWidth);//printf("x heigth: %d \n",BmpIH.biHeight);width   = BmpIH.biWidth;height  = BmpIH.biHeight;bits    = BmpIH.biBitCount>>3;  // 相当于 BmpIH.biBitCount/8 ,获得一个像素对应的字节数bmp_buf = (unsigned char *)malloc(BmpIH.biSizeImage);memset(bmp_buf,0x0,BmpIH.biSizeImage);    //获取BMP位图的RGB信息fread(bmp_buf,BmpIH.biSizeImage,1,filp);//如果该图片的每一行的大小不是4字节的倍数,则图片将有额外字节填充,须对齐int index;int linebyte = (BmpIH.biSizeImage/height); //获取数据对齐的标准下的每行像素数据字节数//将24bit的RGB填充到32bit的ARGB中for(index=0,y=(479-Y-height),i=0;y<(479-Y);y++,index+=linebyte){for(i=index,x=X;x<X+width;x++,i+=3){               fb_mem[(479-y)*800+x]  =  (bmp_buf[i] |  bmp_buf[i+1]<<8  |  bmp_buf[i+2] <<16);}}// *(fb_mem+(479-y)*800+x)   fb_mem 作为显存的首地址,通过指针偏移操作,访问对应的像素位置//写LCD//尺寸 7 寸   分辨率: 800*480 色深(位): ARGB  16bit(565; 5:5:5:1)   24bit(8:8:8)  32bit(8:8:8:8)// 黑色: 0x00000000   白色: 0xFFFFFFFF  红色:0xFF0000fclose(filp);return 0;}int Show_Msg(int x,int y,char *msg){int i=0;char str[50];char cn;while( (cn = *(msg++))!= '\0'){if((cn > 47 && cn < 58) || (cn >96 && cn<123)){memset(str,0,sizeof(str));sprintf(str,"keyboard/%c.bmp",cn);Show_Bmp(x+i++*50, y,str);}}  sleep(2);return 0;}int  Create_LineEdit(int X,int Y,int width,int height)
{int x,y;for(y=Y;y<Y+height;y++){for(x=X;x<X+width;x++){              fb_mem[y*800+x]  =  0xFFFFFFFF;}} return 0;
}//此处可以设置背景颜色
int  Clean_Area(int X,int Y,int width,int height)
{int x,y;for(y=Y;y<Y+height;y++){for(x=X;x<X+width;x++){              // fb_mem[y*800+x]  =  0xFFFFFFFF; //设置背景颜色为白色,你可以修改fb_mem[y*800+x]  =  0xFF66FF; //设置背景颜色为淡紫色,你可以修改}}    return 0;
}int  Draw_Text16(unsigned int x,unsigned int y,unsigned long color,const unsigned char ch[])
{unsigned short int i,j;unsigned char mask,buffer;for(i=0;i<16;i++){mask =0x80;              //掩码buffer =ch[i*2];       //提取一行的第一个字节for(j=0;j<8;j++){if(buffer &mask){fb_mem[(y+i)*800+(x+j)]= color; //为画笔上色 }mask =mask >>1;}mask =0x80;buffer =ch[i*2+1];for(j=0;j<8;j++){if(buffer &mask){fb_mem[(y+i)*800+(x+j+8)]= color;}mask =mask>>1;    }       }return 0;
}int  Draw_TextX(unsigned int x,unsigned int y,unsigned long color,const unsigned char ch[],int size)
{unsigned short int i,j,k,m;unsigned char mask,buffer;for(i=0;i<16;i++){mask =0x80;              //掩码buffer =ch[i*2];       //提取一行的第一个字节for(j=0;j<8;j++){if(buffer &mask){for(k=0;k<size;k++){for(m=0;m<size;m++){fb_mem[(y+i*size+m)*800+(x+j*size+k)]= color;}}}mask =mask >>1;}mask =0x80;buffer =ch[i*2+1];for(j=0;j<8;j++){if(buffer &mask){for(k=0;k<size;k++){ for(m=0;m<size;m++){  fb_mem[(y+i*size+m)*800+(x+(j+8)*size+k)]= color;}}}mask =mask>>1;    }       }return 0;
}int  Draw_ASCII(unsigned int x,unsigned int y,unsigned long color,const unsigned char ch[])
{unsigned short int i,j;unsigned char mask,buffer;for(i=0;i<16;i++){mask=0x80;buffer=ch[i];for(j=0;j<8;j++){                   if(buffer&mask){fb_mem[(y+i)*800+(x+j)]= color;}mask=mask>>1;                   }}return 0;
}int  Draw_ASCIIX(unsigned int x,unsigned int y,unsigned long color,const unsigned char ch[],int size)
{unsigned short int i,j,k,m;unsigned char mask,buffer;for(i=0;i<16;i++){mask=0x80;buffer=ch[i];for(j=0;j<8;j++){                   if(buffer&mask){for(k=0;k<size;k++){for(m=0;m<size;m++){fb_mem[(y+i*size+m)*800+(x+j*size+k)]= color;}}}mask=mask>>1;                   }}return 0;
}int  Display_character(unsigned int x,unsigned int y,unsigned int len,unsigned char *string)
{int k, xx;unsigned char qh,wh;const unsigned char *mould;unsigned int length =len;for(k=0,xx=x;k<length-1;k++){if(string[k]&0x80)   //中文字符{qh =string[k]-0xa0;            //区号wh =string[k+1]-0xa0;     //位号mould =&__MYCHS[((qh-1)*94+wh-1)*32];Draw_Text16(4+xx,y,0xFF0000,mould);xx+=16;k++;}else{mould =&__ASCII[string[k]*16];Draw_ASCII(4+xx,y,0xFF0000,mould);xx+=8;}}return 0;
}//此处可以修改颜色
int  Display_characterX(unsigned int x,unsigned int y,unsigned int len,unsigned char *string,int size)
{int k, xx;unsigned char qh,wh;const unsigned char *mould;unsigned int length =len;for(k=0,xx=x;k<length-1;k++){if(string[k]&0x80)   //中文字符{qh =string[k]-0xa0;            //区号wh =string[k+1]-0xa0;     //位号mould =&__MYCHS[((qh-1)*94+wh-1)*32];Draw_TextX(4*size+xx,y,0x00000000,mould,size); //加4为了让每个中文之间有一定的间隙xx+=16*size;//当前的中文字模为32*32,每次显示下一个中文时横向偏移32个bitk++; //加载下一个中文,两次++操作,偏移2个字节}else{mould =&__ASCII[string[k]*16];Draw_ASCIIX(4*size+xx,y,0x00000000,mould,size);xx+=8*size;//当前的ASCII字模显示为8*16,每次显示下一个中文时横向偏移8个bit}}return 0;
}int  Display_characterXX(unsigned int x,unsigned int y,unsigned int len,unsigned char *string,int size,unsigned long color)
{int k, xx;unsigned char qh,wh;const unsigned char *mould;unsigned int length =len;for(k=0,xx=x;k<length-1;k++){if(string[k]&0x80)   //中文字符{qh =string[k]-0xa0;            //区号wh =string[k+1]-0xa0;     //位号mould =&__MYCHS[((qh-1)*94+wh-1)*32];Draw_TextX(4*size+xx,y,color,mould,size); //加4为了让每个中文之间有一定的间隙xx+=16*size;//当前的中文字模为32*32,每次显示下一个中文时横向偏移32个bitk++; //加载下一个中文,两次++操作,偏移2个字节}else{mould =&__ASCII[string[k]*16];Draw_ASCIIX(4*size+xx,y,color,mould,size);xx+=8*size;//当前的ASCII字模显示为8*16,每次显示下一个中文时横向偏移8个bit}}return 0;
}int Roll_Dispaly(unsigned char *str)
{int x;for(x=600;x>20;x--){Display_characterXX(x,45,strlen(str)+1,str,4,0xFF0000);usleep(50000);Display_characterXX(x+1,45,strlen(str)+1,str,4,0xFFFFFFFF); }Display_characterXX(x+1,45,strlen(str)+1,str,4,0xFFFFFFFF);return 0;
}

客户端主函数

#include <stdio.h>
#include <string.h>
#include "myiohead.h"
#include "project.h"extern int Init_Lcd(void);
extern int Display_characterX(unsigned int x,unsigned int y,unsigned int len,unsigned char *string,int size); //注意汉字编码必须修改成GB2312
extern void  UnInit_Lcd(void);
extern int  Clean_Area(int X,int Y,int width,int height);/*开发板上字符编码设置成默认default,此篇设置6个字宽100,高20
*/int mysocketfd;
int talknum = 0;
pthread_t id,id1,id2,id3;//定义一个结构体表示双向循环列表,为了省事把此双向链表类型定义为dlist
typedef struct bmplist
{char data[100];struct bmplist *next;struct bmplist *fnext;
}bqdlist;bqdlist * bqinit_list()
{bqdlist *head = calloc(1,sizeof(bqdlist));head->next = head;head->fnext = head;return head;
}//尾插
int bqinsert_(char * newdata,bqdlist * head)
{bqdlist * p =head;while(p->next != head){p = p->next;}bqdlist *newnode = calloc(1,sizeof(bqdlist));//newnode->data = newdata;strcpy(newnode->data, newdata);newnode->next = head;p->next = newnode;head->fnext = newnode;newnode->fnext = p;
}//读取目录,获得图片名字
int read_dir(char *path,bqdlist * head)
{DIR * dir = opendir(path);if (NULL == dir){perror("打开目录失败!");exit(0);}struct dirent * rd = NULL;while((rd = readdir(dir)) != NULL) { //printf("d_name : %s\n", rd->d_name); if (rd->d_type == DT_REG){//printf("这是普通文件!\n");if (strstr(rd->d_name,".bmp") != 0){bqinsert_(rd->d_name,head);}}}return 0;
}//定义一个结构体表示双向循环列表,为了省事把此双向链表类型定义为dlist
typedef struct doublelist
{int who;char talk_about[50];struct doublelist *next;struct doublelist *fnext;
}dlist;dlist * init_list()
{dlist *head = calloc(1,sizeof(dlist));bzero(head->talk_about,50);head->next = head;head->fnext = head;return head;
}//尾插
int insert_(int newwho,char *about,dlist * head)
{dlist * p =head;while(p->next != head){p = p->next;}dlist *newnode = calloc(1,sizeof(dlist));newnode->who = newwho;strcpy(newnode->talk_about,about);newnode->next = head;p->next = newnode;head->fnext = newnode;newnode->fnext = p;
}
dlist * mydlist;
//普通展示图片
int showbmp(char *bmppath)
{int bmpfd;int lcdfd;int i;int x,y;int w,h;//打开你要显示的w*h大小的bmpbmpfd=open(bmppath,O_RDWR);if(bmpfd==-1){perror("打开图片失败!\n");return -1;}//读取图片的宽和高lseek(bmpfd,18,SEEK_SET);read(bmpfd,&w,4);//读取宽read(bmpfd,&h,4);//读取高//定义一个数组,依据图片的大小char bmpbuf[w*h*3]; //char占1个字节//定义另外一个数组,存放转换得到的ARGB数据int lcdbuf[w*h]; //int占4字节//定义中间变量。临时存放数据int tempbuf[w*h];//打开lcd驱动lcdfd=open("/dev/fb0",O_RDWR);if(lcdfd==-1){perror("打开lcd失败!\n");return -1;}//映射得到lcd的首地址int *lcdmem=mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);if(lcdmem==NULL){perror("映射lcd失败!\n");return -1;}//跳过bmp图片头信息54字节,从55字节开始读取lseek(bmpfd,54,SEEK_SET);//读取bmp图片的RGB数据//每三个字节为一组,构成一个像素点的RGB数据read(bmpfd,bmpbuf,w*h*3); //bmpbuf[0] bmpbuf[1] bmpbuf[2]//    B            G       R   //bmpbuf[3] bmpbuf[4] bmpbuf[5]  //把三个字节--》转换成四个字节/*细节分析如下:lcdbuf[0]=0x00<<24|bmpbuf[0]<<16|bmpbuf[1]<<8|bmpbuf[2]lcdbuf[1]=0x00<<24|bmpbuf[3]<<16|bmpbuf[4]<<8|bmpbuf[5] */for(i=0; i<w*h; i++)lcdbuf[i]=0x00<<24|bmpbuf[3*i+2]<<16|bmpbuf[3*i+1]<<8|bmpbuf[3*i];//把颠倒的图片翻转过来/*细节分析如下:lcdbuf[0] --->lcdbuf[479*800]*/for(x=0; x<w; x++)for(y=0; y<h; y++)//lcdbuf[(h-1-y)*w+x]=lcdbuf[y*w+x];tempbuf[(h-1-y)*w+x]=lcdbuf[y*w+x];//把转换后的数据写入lcd中for (int j = 0; j < h; j++){for(int i = 0,n = 0;i < w;i++){*(lcdmem+(j*800)+i)=tempbuf[j*w+i];  //n++;           }}//关闭close(bmpfd);close(lcdfd);return 0;
}//获取滑动方向的触摸屏坐标
int yxget_x_y(int *touch_x,int *touch_x1,int *touch_y,int *touch_y1)
{int flag = 1;int to = open ("/dev/input/event0",O_RDWR);if (-1 == to){perror("打开触摸屏的驱动失败!");exit(0);}struct input_event myevent;int count = 1;while(1){   read(to,&myevent,sizeof(struct input_event));if (myevent.type == EV_ABS){   if(myevent.code==ABS_X) //x坐标//由于新开发板的坐标范围跟800*480不一致,按比例修正//printf("你点击的坐标位置X坐标是:%d\n",myevent.value);{   * touch_x1 = (myevent.value*800)/1024;if (count){* touch_x = (myevent.value*800)/1024;count = 0;}                        }if(myevent.code==ABS_Y) //y坐标{* touch_y1 = (myevent.value*480)/600;if (flag){* touch_y = (myevent.value*480)/600;flag = 0;}}}if(myevent.type==EV_KEY && myevent.code==BTN_TOUCH && myevent.value==0){//count = 0;//printf("x0=%d,y0=%d\n", *touch_x,*touch_y);//printf("x1=%d,y1=%d\n", *touch_x1,*touch_y1);break;}}close(to);
}//缩放图片--展示选择的表情
int zoom_showbmp(char *bmppath)
{int bmpfd;int lcdfd;int i;int x,y;int w,h;//打开你要显示的w*h大小的bmpbmpfd=open(bmppath,O_RDWR);if(bmpfd==-1){perror("打开图片失败!\n");return -1;}//读取图片的宽和高lseek(bmpfd,18,SEEK_SET);read(bmpfd,&w,4);//读取宽read(bmpfd,&h,4);//读取高//定义一个数组,依据图片的大小char bmpbuf[w*h*3]; //char占1个字节//定义另外一个数组,存放转换得到的ARGB数据int lcdbuf[w*h]; //int占4字节//定义中间变量。临时存放数据int tempbuf[w*h];//打开lcd驱动lcdfd=open("/dev/fb0",O_RDWR);if(lcdfd==-1){perror("打开lcd失败!\n");return -1;}//映射得到lcd的首地址int *lcdmem=mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);if(lcdmem==NULL){perror("映射lcd失败!\n");return -1;}//跳过bmp图片头信息54字节,从55字节开始读取lseek(bmpfd,54,SEEK_SET);//读取bmp图片的RGB数据//每三个字节为一组,构成一个像素点的RGB数据read(bmpfd,bmpbuf,w*h*3); //bmpbuf[0] bmpbuf[1] bmpbuf[2]//    B            G       R   //bmpbuf[3] bmpbuf[4] bmpbuf[5]  //把三个字节--》转换成四个字节/*细节分析如下:lcdbuf[0]=0x00<<24|bmpbuf[0]<<16|bmpbuf[1]<<8|bmpbuf[2]lcdbuf[1]=0x00<<24|bmpbuf[3]<<16|bmpbuf[4]<<8|bmpbuf[5] */for(i=0; i<w*h; i++)lcdbuf[i]=0x00<<24|bmpbuf[3*i+2]<<16|bmpbuf[3*i+1]<<8|bmpbuf[3*i];//把颠倒的图片翻转过来/*细节分析如下:lcdbuf[0] --->lcdbuf[479*800]*/for(x=0; x<w; x++)for(y=0; y<h; y++)//lcdbuf[(h-1-y)*w+x]=lcdbuf[y*w+x];tempbuf[(h-1-y)*w+x]=lcdbuf[y*w+x];// //把转换后的数据写入lcd中for (int j = 0,m = 0; j < (h/6); j++,m = m+6){j = j+400;for(int i = 0,n = 0;i < (w/5);i++,n = n+5){i = i+640;*(lcdmem+(j*800)+i)=tempbuf[m*w+n];  i = i-640;           }j = j-400;}  //关闭close(bmpfd);close(lcdfd);return 0;
}//缩放图片--用于相册缩略图
int zoom1_showbmp(char *bmppath,int num)
{int bmpfd;int lcdfd;int w,h;//打开你要显示的w*h大小的bmpbmpfd=open(bmppath,O_RDWR);if(bmpfd==-1){perror("打开图片失败!\n");return -1;}//读取图片的宽和高lseek(bmpfd,18,SEEK_SET);read(bmpfd,&w,4);//读取宽read(bmpfd,&h,4);//读取高//定义一个数组,依据图片的大小char bmpbuf[w*h*3]; //char占1个字节//定义另外一个数组,存放转换得到的ARGB数据int lcdbuf[w*h]; //int占4字节//定义中间变量。临时存放数据int tempbuf[w*h];//打开lcd驱动lcdfd=open("/dev/fb0",O_RDWR);if(lcdfd==-1){perror("打开lcd失败!\n");return -1;}//映射得到lcd的首地址int *lcdmem=mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);if(lcdmem==NULL){perror("映射lcd失败!\n");return -1;}//跳过bmp图片头信息54字节,从55字节开始读取lseek(bmpfd,54,SEEK_SET);//读取bmp图片的RGB数据//每三个字节为一组,构成一个像素点的RGB数据read(bmpfd,bmpbuf,w*h*3); //bmpbuf[0] bmpbuf[1] bmpbuf[2]//    B            G       R //bmpbuf[3] bmpbuf[4] bmpbuf[5]  //把三个字节--》转换成四个字节/*细节分析如下:lcdbuf[0]=0x00<<24|bmpbuf[0]<<16|bmpbuf[1]<<8|bmpbuf[2]lcdbuf[1]=0x00<<24|bmpbuf[3]<<16|bmpbuf[4]<<8|bmpbuf[5] */for(int i=0; i<w*h; i++)lcdbuf[i]=0x00<<24|bmpbuf[3*i+2]<<16|bmpbuf[3*i+1]<<8|bmpbuf[3*i];//把颠倒的图片翻转过来/*细节分析如下:lcdbuf[0] --->lcdbuf[479*800]*/for(int x=0; x<w; x++)for(int y=0; y<h; y++)//lcdbuf[(h-1-y)*w+x]=lcdbuf[y*w+x];tempbuf[(h-1-y)*w+x]=lcdbuf[y*w+x];/*//把转换后的数据写入lcd中,高缩小2倍,宽缩小2倍for (int j = 0,m = 0; j < (h/2); j++,m = m+2){for(int i = 0,n = 0;i < (w/2);i++,n = n+2){*(lcdmem+(j*800)+i)=tempbuf[m*w+n];             }}*/// //把转换后的数据写入lcd中,高缩小(h/160)倍,宽缩小w/160倍int a = (num%5)*160;int b = (num/5)*160;for (int j = 0,m = 0; j < (h/(h/160)); j = j-b+1,m = m+(h/160)){j = j+b;for(int i = 0,n = 0;i < (w/(w/160));i = i-a+1,n = n+(w/160)){i = i + a;*(lcdmem+(j*800)+i)=tempbuf[m*w+n];             }} //关闭close(bmpfd);close(lcdfd);return 0;
}//我的相册
int myphoto()
{int *touch_x = calloc(1,1000);int *touch_y = calloc(1,1000);int *touch_x1 = calloc(1,1000);int *touch_y1 = calloc(1,1000);bqdlist *adlist=bqinit_list();read_dir("/myphoto/",adlist);char a[500] = "/myphoto/";int page = 0;int count = 0;int num = 0;//计算总共有多少张图片bqdlist * p = adlist;while(p->next != adlist){p = p->next;count++;}//开头显示第一页缩略图int k = 0;page = 0;p = adlist;while(p->next != adlist && k<15){ k++;p = p->next;num++;bzero(a,500);strcpy(a,"/myphoto/");strcat(a,p->data);zoom1_showbmp(a,num-1);}num = 0;p = adlist;while(1){//到while循环的顶部了yxget_x_y(touch_x,touch_x1,touch_y,touch_y1);//向左划,下一页缩略图if((*touch_x>*touch_x1)&&((*touch_x-*touch_x1)>=80)){    if (count<=15){printf("所有图片都在这里了噢。\n");continue;}if (page <= count/15){page++;}if (page > count/15){if (count%15 == 0){printf("这已经是最后一页了。\n");if (page > count/15){page--;}continue;}}                    p = adlist;for (int i = 0; i < 15*page; i++){p = p->next;}k = 0;        while(p->next != adlist && k<15){    k++;p = p->next;num++;bzero(a,500);strcpy(a,"/myphoto/");strcat(a,p->data);zoom1_showbmp(a,num-1);}}num = 0;//向右划,上一页if( ((*touch_x1-*touch_x) >=80) && (*touch_x1>=*touch_x) ){   if (page==0){printf("当前已经是第一页了。\n");continue;}page--;       p = adlist;for (int i = 0; i < 15*page; i++){p = p->next;}k = 0;        while(p->next != adlist && k<15){    k++;p = p->next;num++;bzero(a,500);strcpy(a,"/myphoto/");strcat(a,p->data);zoom1_showbmp(a,num-1);}}num = 0;//跳转点击到的图片if( ((*touch_x1-*touch_x) <80) && (*touch_x1>=*touch_x) ){int h = (*touch_x1/160) + (*touch_y1/160)*5;p = adlist->next;for (int i = 0; i < 15*page; i++){p = p->next;}for (int i = 0; i < h; i++){if (p->next != adlist){p = p->next;}else{printf("当前位置没有对应图片,自动跳转到最后一张图片。\n");break;}}bzero(a,500);strcpy(a,"/myphoto/");strcat(a,p->data);char buf[50];char buff[50];bzero(buf,50);bzero(buff,50);strcpy(buf,"(发送了一个表情)");strcat(buf,"  ");insert_(1,buf,mydlist);talknum++;strcpy(buff,"您最新发送:");strcat(buff,buf);printf("%s\n",buff );write(mysocketfd,buf,50);//发送要聊天内容   usleep(60000);Display_characterX(0,330,strlen(buff),buff,1);            zoom_showbmp(a);break;}//跳转点击到的图片if( ((*touch_x-*touch_x1) <80) && (*touch_x>=*touch_x1) ){int h = (*touch_x1/160) + (*touch_y1/160)*5;p = adlist->next;for (int i = 0; i < 15*page; i++){p = p->next;}for (int i = 0; i < h; i++){if (p->next != adlist){p = p->next;}else{printf("当前位置没有对应图片,自动跳转到最后一张图片。\n");break;}}bzero(a,500);strcpy(a,"/myphoto/");strcat(a,p->data);char buf[50];char buff[50];bzero(buf,50);bzero(buff,50);strcpy(buf,"(发送了一个表情)");strcat(buf,"  ");insert_(1,buf,mydlist);talknum++;strcpy(buff,"您最新发送:");strcat(buff,buf);printf("%s\n",buff );write(mysocketfd,buf,50);//发送要聊天内容  usleep(60000);Display_characterX(0,330,strlen(buff),buff,1);zoom_showbmp(a);break;}//到while循环的底部了}}//登陆和注册
int denglu()
{char usename[100] = {0};char newname[100] = {0};char password[100] = {0};char newword[100] = {0};char ifright[50] = {0};int *touch_x1 = calloc(1,1000);int *touch_y1 = calloc(1,1000);int *touch_x = calloc(1,1000);int *touch_y = calloc(1,1000);showbmp("qq.bmp");while(1){//注册账号和密码yxget_x_y(touch_x,touch_x1,touch_y,touch_y1);if ((*touch_x>123) &&(*touch_x<205) && (*touch_y>392) && (*touch_y<432) ){   printf("进入了注册流程\n");char a[10] = "zhuce";int ret = write(mysocketfd,a,10);//告诉服务器我想要注册账号密码if (-1 == ret){perror("写入失败");exit(0);}printf("请输入你想注册的账号!(<=12位)\n");scanf("%s",newname);write(mysocketfd,newname,strlen(newname));//发送注册的用户名信息printf("请输入你想注册的密码!(<=12位)\n");scanf("%s",newword);write(mysocketfd,newword,strlen(newword));//发送注册的密码信息printf("注册成功,请重新登陆\n");continue;}else{printf("进入了登陆流程\n");char a[10] = "hedui";write(mysocketfd,a,10);//告诉服务器我想要核对账号密码printf("请输入用户名!\n");//初始用户名我设置为chenwangxinscanf("%s",usename);write(mysocketfd,usename,strlen(usename));//发送输入的用户名信息printf("请输入密码!\n");//初始密码我设置为123456scanf("%s",password);write(mysocketfd,password,strlen(password));//发送输入的密码信息//接受服务器端的信息--验证账号密码是否正确bzero(ifright,50);read(mysocketfd,ifright,50);//接受服务器读取到的核对信息if (strcmp(ifright,"right") == 0){printf("账号密码正确\n");break;}printf("用户名或密码错误,请重新输入!\n");}}}//刷新在线列表
int getlist()
{char buff[100] = {0};char buf[100] = {0};int i = 0;showbmp("qqlist.bmp");while(1){i++;bzero(buff,100);int ret=read(mysocketfd,buff,100);if(ret==0)//说明服务器断开连接{printf("服务器挂了!\n");exit(-1); //结束整个进程}if (strcmp(buff,"over") == 0){break;}if (strcmp(buff,"over") != 0){strcpy(buf,buff);}//给汉字设置背景颜色//int  Clean_Area(int X,int Y,int width,int height)Clean_Area(0,50*i,600,40); //默认是淡紫色背景//显示字符串//int  Display_characterX(unsigned int x,unsigned int y,unsigned int len,unsigned char *string,int size)Display_characterX(0,50*i,strlen(buf),buf,1); //注意汉字编码必须修改成GB2312// UnInit_Lcd();}
}void * displaytask(void * n)//每当有信息输入或者输出时,就刷新界面
{int a = 0;dlist *p = mydlist;while(1){if (a<talknum){int num = 0;a = talknum;showbmp("talking.bmp");while(p->next != mydlist){p = p->next;}while (p->fnext != mydlist){p = p->fnext;num++;if (p->who == 1)//自己发的信息{int size = strlen(p->talk_about);//给汉字设置背景颜色//int  Clean_Area(int X,int Y,int width,int height)Clean_Area(610-(((100/6)+1)*size),300-(num*30),((100/6)+1)*size,20); //默认是淡紫色背景//显示字符串//int  Display_characterX(unsigned int x,unsigned int y,unsigned int len,unsigned char *string,int size)//Display_characterX(800-(((100%6)+1)*strlen(p->talk_about)),330-(count*30),strlen(p->talk_about),p->talk_about,1); //注意汉字编码必须修改成GB2312Display_characterX(610-(((100/6)+1)*size),300-(num*30),strlen(p->talk_about),p->talk_about,1);}if (p->who == 2)//自己收到的信息{if (strcmp(p->talk_about,"  ") ==0){num--;continue;}//给汉字设置背景颜色//int  Clean_Area(int X,int Y,int width,int height)//Clean_Area(0,330-(count*30),((100%6)+1)*strlen(p->talk_about),20); //默认是淡紫色背景Clean_Area(0,300-(num*30),((100/6)+1)*strlen(p->talk_about),20);//显示字符串//int  Display_characterX(unsigned int x,unsigned int y,unsigned int len,unsigned char *string,int size)//Display_characterX(0,330-(count*30),strlen(p->talk_about),p->talk_about,1); //注意汉字编码必须修改成GB2312Display_characterX(0,300-(num*30),strlen(p->talk_about),p->talk_about,1);}}            }}
}void * recevetask(void * n)
{char buf[50];while(1){bzero(buf,50);int ret = read(mysocketfd,buf,50);if (ret >0){talknum++;insert_(2,buf,mydlist);} }
}void * task(void * n)//不断发送键盘输入的信息
{   showbmp("talking.bmp");   char buf[50];char buff[50];while(1){            bzero(buf,50);bzero(buff,50);printf("请输入你要发送的信息:\n");scanf("%s",buf);strcat(buf,"  ");insert_(1,buf,mydlist);talknum++;strcpy(buff,"您最新发送:");strcat(buff,buf);printf("%s\n",buff );write(mysocketfd,buf,50);//发送要聊天内容 usleep(60000);Display_characterX(0,330,strlen(buff),buff,1);                            }
}void * do_what(void * n)//选择是否要发送表情包或者文件
{int *touch_x1 = calloc(1,1000);int *touch_y1 = calloc(1,1000);int *touch_x = calloc(1,1000);int *touch_y = calloc(1,1000);while(1){yxget_x_y(touch_x,touch_x1,touch_y,touch_y1);if ((*touch_y>367) &&(*touch_x<386)&& (*touch_x<37))//发送表情包{myphoto();}//退出聊天窗口if ((*touch_x>742) && (*touch_y<36)){pthread_cancel(id);pthread_cancel(id1);pthread_cancel(id2);write(mysocketfd,"pleaseoverit",50);//发送要关闭聊天窗口指令getlist();//展示在线好友列表界面break;}}
}
//后续可以在这里添加QQ空间和群聊
int option()
{int *touch_x1 = calloc(1,1000);int *touch_y1 = calloc(1,1000);int *touch_x = calloc(1,1000);int *touch_y = calloc(1,1000);yxget_x_y(touch_x,touch_x1,touch_y,touch_y1);char buf[50] = {0};int mytouch = 0;mytouch = (*touch_y/50);printf("%d\n", mytouch);if (mytouch >= 1){write(mysocketfd,"othertalking",50);//发送要聊天的信息/sleep(1);write(mysocketfd,&mytouch,50);//发送要和谁聊天的信息/printf("触摸标志为:%d\n",mytouch );bzero(buf,50);read(mysocketfd,buf,50);if (strcmp(buf,"youself") ==0){printf("你点击了你自己\n");option();}else{printf("和其他人聊天\n");pthread_create(&id,NULL,task,NULL); pthread_create(&id1,NULL,displaytask,NULL); pthread_create(&id2,NULL,recevetask,NULL);pthread_create(&id3,NULL,do_what,NULL); pthread_join(id, NULL);          }}
}int main()
{mydlist = init_list();//创建tcp套接字mysocketfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == mysocketfd){perror("创建套接字失败!");exit(0);}//创建结构体 --放自己客户端的ipv4地址和端口号struct sockaddr_in mysockaddr;//清空结构体bzero(&mysockaddr,sizeof(mysockaddr));//给结构体里面赋值mysockaddr.sin_family = AF_INET;//保存地址协议mysockaddr.sin_addr.s_addr =  htonl(INADDR_ANY);//最终存放要绑定的客户端ip地址//不要存1024以内的端口后,因为很多都被Linux系统占用了mysockaddr.sin_port = htons(5000);    //存放要绑定的端口号//创建结构体 --放对方服务器的ipv4地址和端口号struct  sockaddr_in serveraddr;//清空结构体bzero(&serveraddr,sizeof(serveraddr));//给结构体里面赋值serveraddr.sin_family = AF_INET;//保存地址协议serveraddr.sin_addr.s_addr =  inet_addr("192.168.1.103");//最终存放要绑定的服务器ip地址//不要存1024以内的端口后,因为很多都被Linux系统占用了serveraddr.sin_port = htons(6000);    //存放要绑定的端口号//设定端口号可以重复使用int on = 1;//on一定要设置成大于0的常数setsockopt(mysocketfd, SOL_SOCKET,SO_REUSEADDR, &on,sizeof(on));//绑定ip和端口号int ret = bind(mysocketfd, (struct sockaddr *)&mysockaddr, sizeof(mysockaddr));if (-1 == ret){perror("绑定ip和端口号失败");exit(0);}//连接服务器ret = connect(mysocketfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));if (-1 == ret){perror("连接服务器失败");exit(0);}//初始化lcdInit_Lcd();//发送进入登陆界面的信息write(mysocketfd,"dlqq",strlen("dlqq"));denglu();printf("您已登陆QQ\n");//刷新列表printf("请问是否刷新列表(输入任何数表示是)\n");int y;scanf("%s",&y);write(mysocketfd,"getlist",strlen("getlist"));getlist();option();//选择和谁聊天while(1);UnInit_Lcd();
}

“伪QQ”---一个即时聊天通讯的工具相关推荐

  1. php和ajax实现聊天功能,怎么在PHP项目中使用jquery与ajax实现一个即时聊天功能

    怎么在PHP项目中使用jquery与ajax实现一个即时聊天功能 发布时间:2020-12-19 16:19:01 来源:亿速云 阅读:81 作者:Leah 本篇文章给大家分享的是有关怎么在PHP项目 ...

  2. 使用vue+golang+mysql写一个即时聊天、多人视频的项目

    项目为学习golang的一个新手项目,功能包括短信登陆.注册.添加好友.修改备注.创建群组.加入群组.好友即时聊天.视频聊天.群组聊天.多人视频等功能 前端:vue.element-ui.vue-so ...

  3. 使用 node + express + socket 来一个即时聊天~

    首先需要在电脑安装Node.js 如果基础不好的话看下官网了解一下 然后我们就可以搭建一个简单的HTTP服务器了 下面搭建一个简单的服务器了解一下不是正文 首先在文件目录里创建server.js内容为 ...

  4. android 从TCP实现一个即时聊天app的简单理论过程

    注:本人适用于有socket基础和接触过XMPP的人一起探讨.不喜勿喷,纯属,闲了总结一下工作经验和小分享而已. 一.socket基础知识 TCP与UDP区别总结: 1.TCP面向连接(如打电话要先拨 ...

  5. 【详细搭建教程】在线客服系统源码3.0防黑版,即时聊天通讯源码 带机器人,防注入 无后门

    亲测搭建环境: Linux , PHP5.6+MYSQL5.6 一.下载网站源码 源码下载:源码 二.上传网站源码 网站源码上传至空间/服务器根目录 三.导入数据库 导入aixiang.sql数据库文 ...

  6. ios开发xmpp仿微信即时聊天工具

    最近在做一个项目,需要一个即时聊天工具,先打算有第三方环信(http://www.easemob.com),但是最终老板不允许,要自己开发用自己的服务器,哎!如果有需要的可以去看看这个环信,真的不错. ...

  7. java实现仿QQ即时聊天

    这是我的java大作业,这里就直接贴上我的实验报告了. 2.0版已更新地址:Java仿QQ2.0版 项目已开源:github地址:imitate-qq 欢迎fork与star 仿微信App:canar ...

  8. webScoket即时聊天,用户不在线时消息暂存,上线立马收到

    这个代码是用webScoket写的即时聊天通讯的,功能可群发单发,可对不在线用户发送消息时用户一上线立马就能收到消息,也可以查看未读数量,这个代码写了有一段时间了,忽然想起来就想着发上来记一下和交流一 ...

  9. golang从简单的即时聊天来看架构演变

    前言 俗话说的好,架构从来都不是一蹴而就的,没有什么架构一开始设计就是最终版本,其中需要经过很多步骤的变化,今天我们就从一个最简单的例子来看看,究竟架构这个东西是怎么变的. 我将从一个最简单的聊天室的 ...

最新文章

  1. linux文件属性解析,Linux操作系统的文件属性与目录配置解析
  2. 中国首款L4级Robovan发布!文远知行商用落地两条腿走路
  3. python爬虫百科-Python从概念上先了解爬虫
  4. 使用PHP CURL 模拟HTTP实现在线请求工具-toolfk程序员工具网
  5. c++中的类型转换--reinterpret_cast
  6. 刘浩(专业打劫三十年)20155307的预备作业02:
  7. RabbitMQ 入门:1. Message Broker(消息代理)
  8. Redis 工具类_慕课版本
  9. ADBB的完整形式是什么?
  10. 南孚电池:如何从0-1建立经营分析报表平台,助力集团转型?
  11. Python+django网页设计入门(17):模板语法及应用
  12. Delphi D10.X VCL和FireMonkey之间的常见差异介绍
  13. 移动通信网络架构的演进
  14. 自爆神舟电脑为什么便宜,明眼人可以看懂。
  15. BeanUtils.copyProperties 无法转换数据类型不同的数据
  16. 关于微信小程序过滤器filter的正确使用
  17. 电商数仓:用户行为数据仓库(一)数据仓库建设和技术选型
  18. rust外服靶场怎么进_rust 学习之旅一, rust编程环境相关
  19. append和extend的区别
  20. 苹果新功能惹众怒,4000多家组织和个人签署公开信 敦促苹果放弃“儿童安全”功能

热门文章

  1. 微软Office 2013定价及版本详情曝光
  2. [arch Linux] 使用grub实现Linux和Windows双系统的引导
  3. 软件测试工程师春招薪资20K+,BAT校招薪资表出炉!学弟学妹们看齐...
  4. 统一网络控制器Func
  5. 元宇宙将如何影响我们的投资、就业和生活方式?
  6. 微原实验二 数码转换
  7. SMTP, POP3, IMAP,Exchange ActiveSync区别
  8. 双硬盘装双系统遇到的坑
  9. 经典面试题助你成功就业
  10. Java对象结构与锁实现原理及MarkWord详解