这段时间做了一个比较简单的即时通信软件,就把这个过程记录一下吧,一方面可以加深一下自己对这个项目的印象,另一方面也希望可以帮助到各位正在学习这一块内容的博友!!!



文章目录

  • 代码实现
    • Client
      • client.h
      • client.c
      • list.h
      • list.c
      • main.c
      • makefile
    • server
      • server.c
      • makefile
  • 部分截图
    • 登录窗口
    • 好友列表
    • 聊天窗口
  • 使用方法

代码实现

Client

client.h
#include <gtk/gtk.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define OURPORT 8088 //定义端口号extern gint sd; //套接字句柄
extern struct sockaddr_in s_in; //套接字数据结构
extern gchar username[64]; //用户名
extern gchar buf[1024];//写缓冲区
extern gchar get_buf[1048];  //读缓冲区
extern gboolean isconnected;   //定义逻辑值表示是否连接
extern GtkWidget *text;
extern GtkTextBuffer *buffer;     //显示对话内容的文本显示缓冲区
extern GtkWidget *message_entry;  //显示输入消息的单行录入控件
extern GtkWidget *name_entry;  //输入用户名的单行录入控件
extern GtkWidget *login_button; //登录按钮void on_destroy(GtkWidget *widget, GdkEvent *event, gpointer data);
void on_button_clicked(GtkButton *button, gpointer data);
gboolean do_connect(char *username);
void on_send (gpointer *name);
void create_window(char *name);
client.c
#include "client.h"//连接多人聊天服务器
gboolean do_connect(char *username)
{GtkTextIter iter;
gint slen;sd = socket(AF_INET,SOCK_STREAM,0);if(sd < 0){gtk_text_buffer_get_end_iter(buffer,&iter);gtk_text_buffer_insert(buffer,&iter,"打开套接字时出错!\n",-1);return FALSE;}s_in.sin_family = AF_INET;s_in.sin_port = OURPORT;slen = sizeof(s_in);if(connect(sd,&s_in,slen) < 0) {gtk_text_buffer_get_end_iter(buffer,&iter);gtk_text_buffer_insert(buffer,&iter,"连接服务器时出错!\n",-1);return FALSE;}else{isconnected = TRUE;return TRUE;}
}//向服务器发送数据
void on_send (gpointer *name)
{
GtkTextIter iter;
const char* message;
if(isconnected==FALSE) return;message = gtk_entry_get_text(GTK_ENTRY(message_entry));sprintf(buf,">%s\n\n",message);write(sd,buf,1024);//发送数据给服务器
gtk_text_buffer_get_end_iter(buffer,&iter);
gtk_text_buffer_insert(buffer,&iter,buf,-1);
read(sd,username,64);
gtk_text_buffer_get_end_iter(buffer,&iter);
gtk_text_buffer_insert(buffer,&iter,username,-1);sprintf(buf,"%s\n\n","");
gtk_entry_set_text(GTK_ENTRY(message_entry),"");//清除单行录中的文字}//关闭主窗口时执行
void on_delete_event (GtkWidget *widget, GdkEvent* event, gpointer data)
{ close(sd);//关闭gtk_main_quit();
}//创建聊天窗口
void create_window(char *name)
{gpointer n=(gpointer)name;
GtkWidget *window;GtkWidget *vbox, *hbox, *button, *label, *view;if(!g_thread_supported())g_thread_init(NULL); //初始线程window = gtk_window_new(GTK_WINDOW_TOPLEVEL);gtk_window_set_title(GTK_WINDOW(window),name);gtk_window_set_default_size(GTK_WINDOW(window),800,500);g_signal_connect(G_OBJECT(window),"delete_event",G_CALLBACK(on_delete_event),NULL);gtk_container_set_border_width(GTK_CONTAINER(window),10);vbox = gtk_vbox_new(FALSE,0);gtk_container_add(GTK_CONTAINER(window),vbox);hbox = gtk_hbox_new(FALSE,0);gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,5);view = gtk_scrolled_window_new(NULL,NULL);gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(view),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);text = gtk_text_view_new();gtk_box_pack_start(GTK_BOX(vbox),view,TRUE,TRUE,5);gtk_container_add(GTK_CONTAINER(view),text);buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));hbox = gtk_hbox_new(FALSE,0);gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,5);label = gtk_label_new("输入消息:");gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,5);message_entry = gtk_entry_new();gtk_box_pack_start(GTK_BOX(hbox),message_entry,FALSE,FALSE,5);button = gtk_button_new_with_label("发送");gtk_box_pack_start(GTK_BOX(hbox),button,FALSE,FALSE,5);g_signal_connect(G_OBJECT(button),"clicked",G_CALLBACK(on_send),n);gtk_widget_show_all(window);gdk_threads_enter();gtk_main();gdk_threads_leave();
}
list.h
#include <gtk/gtk.h>
enum {NAME_COLUMN,
IP_COLUMN,
PORT_COLUMN,
STATUS_COLUMN,
NBR_COLUMNS
};
void view_onRowActivated (GtkTreeView *treeview,GtkTreePath *path,GtkTreeViewColumn *col,gpointer userdata);
static GtkWidget *create_view_and_model(void);
static GtkTreeModel *create_and_fill_model(void);
int list(char *name );
list.c
#include "list.h"
#include"client.h"//当双击好友昵称时触发的事件
void view_onRowActivated (GtkTreeView *treeview,GtkTreePath *path,GtkTreeViewColumn *col,gpointer userdata)
{GtkTreeModel *model;GtkTreeIter iter;model = gtk_tree_view_get_model(treeview);if (gtk_tree_model_get_iter(model, &iter, path)){gchar *name;gint *ip;gint *port;gtk_tree_model_get(model, &iter, NAME_COLUMN, &name, -1);gtk_tree_model_get(model, &iter, IP_COLUMN, &ip, -1);gtk_tree_model_get(model, &iter, PORT_COLUMN, &port, -1);do_connect(name);create_window(name);g_free(name);
}
}//通过迭代生成好友列表
static GtkTreeModel *create_and_fill_model(void)
{GtkTreeStore *treestore;GtkTreeIter toplevel, child;treestore = gtk_tree_store_new(NBR_COLUMNS, G_TYPE_STRING,G_TYPE_INT,G_TYPE_INT,G_TYPE_STRING);gtk_tree_store_append(treestore, &toplevel, NULL);gtk_tree_store_set(treestore, &toplevel, NAME_COLUMN, "我的好友", -1);gtk_tree_store_append(treestore, &child, &toplevel);gtk_tree_store_set(treestore, &child, NAME_COLUMN, "瓜仔", IP_COLUMN,1111,PORT_COLUMN,0,STATUS_COLUMN,"在线",-1);gtk_tree_store_append(treestore, &child, &toplevel);gtk_tree_store_set(treestore, &child, NAME_COLUMN, "胖仔",IP_COLUMN, 2222,PORT_COLUMN,1,STATUS_COLUMN,"在线",-1);gtk_tree_store_append(treestore, &child, &toplevel);gtk_tree_store_set(treestore, &child, NAME_COLUMN, "三火", IP_COLUMN,3333,PORT_COLUMN,2,STATUS_COLUMN,"在线",-1);gtk_tree_store_append(treestore, &child, &toplevel);gtk_tree_store_set(treestore, &child, NAME_COLUMN, "耿妹子", IP_COLUMN,4444,PORT_COLUMN,3,STATUS_COLUMN,"在线",-1);gtk_tree_store_append(treestore, &toplevel, NULL);gtk_tree_store_set(treestore, &toplevel, NAME_COLUMN, "陌生人", -1);gtk_tree_store_append(treestore, &child, &toplevel);gtk_tree_store_set(treestore, &child, NAME_COLUMN, "张三", STATUS_COLUMN,"离线",-1);gtk_tree_store_append(treestore, &child, &toplevel);gtk_tree_store_set(treestore, &child, NAME_COLUMN, "李四", STATUS_COLUMN,"离线",-1);gtk_tree_store_append(treestore, &child, &toplevel);gtk_tree_store_set(treestore, &child, NAME_COLUMN, "王五", STATUS_COLUMN,"离线",-1);gtk_tree_store_append(treestore, &toplevel, NULL);gtk_tree_store_set(treestore, &toplevel, NAME_COLUMN, "黑名单", -1);gtk_tree_store_append(treestore, &child, &toplevel);gtk_tree_store_set(treestore, &child, NAME_COLUMN, "邢文璇", STATUS_COLUMN,"离线",-1);return GTK_TREE_MODEL(treestore);
}//通过渲染器进行好友列表的显示
static GtkWidget *create_view_and_model(void)
{GtkTreeViewColumn *col;GtkCellRenderer *renderer;GtkWidget *view;GtkTreeModel *model;view = gtk_tree_view_new();col = gtk_tree_view_column_new();gtk_tree_view_column_set_title(col, "好友列表");gtk_tree_view_column_set_resizable (col, TRUE);gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);gtk_tree_view_column_set_fixed_width (col, 180);gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);renderer = gtk_cell_renderer_text_new();gtk_tree_view_column_pack_start(col, renderer, TRUE);gtk_tree_view_column_add_attribute(col, renderer, "text", NAME_COLUMN);model = create_and_fill_model();gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);g_object_unref(model);col = gtk_tree_view_column_new();gtk_tree_view_column_set_resizable (col, TRUE);gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);gtk_tree_view_column_set_fixed_width (col, 100);gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);renderer = gtk_cell_renderer_text_new();gtk_tree_view_column_pack_start(col, renderer, TRUE);gtk_tree_view_column_add_attribute(col, renderer, "text",STATUS_COLUMN);model = create_and_fill_model();gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);g_object_unref(model);return view;
}
int list(char *name)
{GtkWidget *view;GtkWidget *window;GtkTreeSelection *selection;GtkWidget *vbox;GtkWidget *statusbar;
// gtk_init(&argc, &argv);window = gtk_window_new(GTK_WINDOW_TOPLEVEL);gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);gtk_window_set_title(GTK_WINDOW(window), name);gtk_window_set_default_size(GTK_WINDOW(window),357,688);vbox = gtk_vbox_new(FALSE, 2);gtk_container_add(GTK_CONTAINER(window), vbox);view = create_view_and_model();selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 1);statusbar = gtk_statusbar_new();gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1);g_signal_connect(view, "row-activated", (GCallback) view_onRowActivated, NULL);gtk_widget_show_all(window);gtk_main();return 0;
}
main.c
#include<stdio.h>
#include<gtk/gtk.h>
#include<stdlib.h>
#include<string.h>
#include"list.h"
#include"client.h"
/**************************************************************************************/
static gboolean release_resourse(GtkWidget *widget,GdkEvent *event,gpointer data);
void chang_background(GtkWidget *widget, int w, int h, const gchar *path);
void callback_button(GtkWidget *button,GtkWidget *entry[3]);gint sd; //套接字句柄
struct sockaddr_in s_in; //套接字数据结构
gchar username[64]; //用户名
gchar buf[1024]; //写缓冲区
gchar get_buf[1048]; //读缓冲区
gboolean isconnected = FALSE; //定义逻辑值表示是否连接
GtkWidget *text;
GtkTextBuffer *buffer; //显示对话内容的文本显示缓冲区
GtkWidget *message_entry; //显示输入消息的单行录入控件
GtkWidget *name_entry; //输入用户名的单行录入控件
GtkWidget *login_button; //登录按钮/*************************************************************************************//*************************************************************************************/GtkWidget *window1; /*************************************************************************************//***************************************更换背景***************************************/
void chang_background(GtkWidget *widget, int w, int h, const gchar *path){gtk_widget_set_app_paintable(widget, TRUE);     //允许窗口可以绘图                                      gtk_widget_realize(widget); gtk_widget_queue_draw(widget);GdkPixbuf *src_pixbuf = gdk_pixbuf_new_from_file(path, NULL);GdkPixbuf *dst_pixbuf = gdk_pixbuf_scale_simple(src_pixbuf, w, h, GDK_INTERP_BILINEAR);GdkPixmap *pixmap = NULL;gdk_pixbuf_render_pixmap_and_mask(dst_pixbuf, &pixmap, NULL, 128);gdk_window_set_back_pixmap(widget->window, pixmap, FALSE);g_object_unref(src_pixbuf);g_object_unref(dst_pixbuf);g_object_unref(pixmap);}   /***************************************回应登录***********************************/
void callback_button(GtkWidget *button,GtkWidget *entry[3])
{const gchar *name = NULL;const gchar *password = NULL;gboolean btn_state;name = gtk_entry_get_text((GtkEntry *)entry[0]);password = gtk_entry_get_text((GtkEntry *)entry[1]);if(strcmp(name,"123456") == 0 && strcmp(password,"123456") == 0)//初始的账号和密码{gtk_widget_hide(window1);list(name);}else{GtkWidget *dialog;GtkWidget *label;dialog = gtk_dialog_new();//创建一个对话框gtk_window_set_title(GTK_WINDOW(dialog), "错误提示");gtk_widget_set_usize(dialog, 300, 100);//设置对话框大小label = gtk_label_new("用户名或密码错误!请重新输入");gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);g_signal_connect(G_OBJECT(dialog), "delete_event", G_CALLBACK(release_resourse), NULL);gtk_widget_show_all(dialog);gtk_main();}
}/**************************************关闭窗口回应**********************************/
static gboolean release_resourse(GtkWidget *widget,GdkEvent *event,gpointer data)
{gtk_main_quit();return FALSE;
}/**************************************登录界面*********************************************/void main(int argc,char *argv[])
{GtkWidget *label1;GtkWidget *label2;GtkWidget *button1;GtkWidget *fixed;GtkWidget *entry1;GtkWidget *entry2;GtkWidget *entry[3];gtk_init(&argc,&argv);window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);gtk_window_set_title((GtkWindow*)window1,"QQ");//标题gtk_widget_set_usize(window1,540,375);gtk_container_set_border_width(GTK_CONTAINER(window1),10);gtk_window_set_position(GTK_WINDOW(window1),GTK_WIN_POS_CENTER);chang_background(window1, 540, 375, "2.jpg");//登录界面用到的图片//创建一个固定布局,创建标签和按钮并把标签按钮和行编辑都放在这个固定布局里面button1 = gtk_button_new_with_label("登录");label1 = gtk_label_new("用户");label2 = gtk_label_new("密码");entry1 = gtk_entry_new_with_max_length(20);entry2 = gtk_entry_new_with_max_length(20);fixed = gtk_fixed_new();gtk_container_add(GTK_CONTAINER(window1),fixed);gtk_fixed_put(GTK_FIXED(fixed),label1,30,150);gtk_fixed_put(GTK_FIXED(fixed),label2,30,205);gtk_fixed_put(GTK_FIXED(fixed),entry1,110,150);gtk_fixed_put(GTK_FIXED(fixed),entry2,110,205);gtk_fixed_put(GTK_FIXED(fixed),button1,110,280);gtk_entry_set_visibility(GTK_ENTRY(entry2),FALSE);//设置密码不可见,entry2是一个编辑密码行,所以肯定不可见gtk_widget_set_size_request(button1,310,50);gtk_widget_set_size_request(label1,100,50);gtk_widget_set_size_request(label2,100,50);gtk_widget_set_size_request(entry1,310,50);gtk_widget_set_size_request(entry2,310,50);entry[0] = entry1;entry[1] = entry2;entry[2] = window1;/***************写回调函数**********************************************************/g_signal_connect(G_OBJECT(button1),"pressed",G_CALLBACK(callback_button),&entry);g_signal_connect(G_OBJECT(window1),"delete_event",G_CALLBACK(release_resourse),NULL);gtk_widget_show_all(window1);gtk_main();}
makefile
CC=gcc
all:$(CC) -Wall -g -o main main.c client.c list.c  `pkg-config --cflags --libs gtk+-2.0 gthread-2.0`


server

server.c
#include <glib.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#define OURPORT 8088
#define MAX_USERS 8
//定义用户数据结构
struct _client { gint sd; gboolean in_use; gchar name[64]; gchar buf[1024];
};
typedef struct _client client;
//定义用户数据区
client user[MAX_USERS];
//定义服务线程
void do_service (gpointer id)
{ gint j; char tobuf[1024]; while(read(user[GPOINTER_TO_INT(id)].sd, user[GPOINTER_TO_INT(id)].buf,1024)!=-1) { sprintf(tobuf,"%s\n\n", user[GPOINTER_TO_INT(id)].buf); for(j=0; j<MAX_USERS; j++) { if(user[j].in_use) { write(user[j].sd,tobuf,1024); g_print("%s",tobuf); } } } // user[GPOINTER_TO_INT(id)].in_use = FALSE; close(user[GPOINTER_TO_INT(id)].sd); //exit(0);
}
int main(int argc, char* argv[])
{ gint sd, newsd; struct sockaddr_in *sin; gint slen; gint count = 0; gint flags; gchar buf[1024]; gchar tobuf[1024]; gint length,i,j; if(!g_thread_supported()) g_thread_init(NULL); else g_print("thread not supported\n"); sd = socket(AF_INET,SOCK_STREAM,0); if(sd == -1) { g_print("create socket error!\n"); return -1; } sin = g_new(struct sockaddr_in,1); sin->sin_family = AF_INET; sin->sin_port = OURPORT; slen = sizeof(struct sockaddr_in); if(bind(sd,sin,slen)<0) { g_print("bind error!\n"); return -1; } if(listen(sd,8)<0) { g_print("listen error!\n"); return -1; } for(i=0; i<MAX_USERS; i++) user[i].in_use = FALSE; flags = fcntl(sd,F_GETFL); fcntl(sd,F_SETFL,flags&~O_NDELAY); for(;;) { newsd = accept(sd,sin,&slen); if(newsd == -1) { g_print("accept error!\n"); break; } else { if(count >= MAX_USERS) { sprintf(buf,"用户数量过多服务器不能连接。\n"); write(newsd,buf,1024); close(newsd); } else { flags = fcntl(user[i].sd,F_GETFL); fcntl(user[i].sd,F_SETFL,O_NONBLOCK); user[count].sd = newsd; user[count].in_use = TRUE; read(newsd,user[count].name,64); //创建为用户服务的线程g_thread_create((GThreadFunc)do_service, (gpointer)count,TRUE,NULL); count++; } } }//for(;;) close(sd); g_free(sin);
}
makefile
CC = gcc
all: $(CC) -o server server.c `pkg-config --cflags --libs glib-2.0 gthread-2.0`

部分截图

登录窗口

好友列表

聊天窗口

使用方法

  1. 首先把服务器端的代码运行起来,然后等待客户端发送的请求
  2. 其次运行客户端的代码,然后进行登录,由于没有使用数据库,所以没有注册功能,有能力的朋友可以自己添加
  3. 登录的初始账号和密码都是123456,可以根据自己的喜好在代码中进行修改
  4. 输入消息即可在聊天窗口和服务器端收到


以上就是博主对这次项目的一些简单总结,代码都是博主运行过的,如果大家在使用的过程中遇到什么问题可以给博主留言,看到必回,如果有帮助到各位的话记得点赞加关注哦!!!

基于Linux的即时通信软件相关推荐

  1. 和java通信_[源码和文档分享]基于JAVA的即时通信软件

    一.设计任务书 1.1 设计任务 本文设计的是一个简单的即时通信软件,利用 Java Socket 进行点到点通信,其工作机制模仿即时通信软件的基本功能,已实现的功能有:客户端登录 客户端退出 群组成 ...

  2. [源码和文档分享]基于JAVA的即时通信软件

    一.设计任务书 1.1 设计任务 本文设计的是一个简单的即时通信软件,利用 Java Socket 进行点到点通信,其工作机制模仿即时通信软件的基本功能,已实现的功能有: 客户端登录 客户端退出 群组 ...

  3. 计算机网络课程设计即时通讯,计算机网络课程设计报告-基于LAN的即时通信软件.doc...

    一.概述 1.1 设计目的: 利用MFC编程实现客户端之间通过服务器进行通信. 1.2 设计内容: 网络通信软件的数据通信是通过网络套接字进行的.根据该原理,其编程步骤应分为创建套接字.在套接字上进行 ...

  4. 基于Linux的及时通信软件

    /************************************************************************* > File Name: server.c ...

  5. 基于加密的即时通信系统设计

    1.1. 系统功能概述 根据自己的实际水平,以及即时聊天的流程需求,基于安全的即时聊天系统应该由服务器端模块.客户端模块.RSA加密解密模块.DES加密解密模块.数字签名和验证模块5部分组成. 根据实 ...

  6. linux系统最好的c类语言开发软件,Linux下基于C 语言开发即时通信软件.doc

    Linux下基于C 语言开发即时通信软件 Linux下基于C++语言开发即时通信软件 关键词:聊天软件 文字聊天 Linux平台 C/S架构ICE中间件 Linux-based instant mes ...

  7. C语言项目(四)——基于Linux系统下的带有GUI界面的即时通信软件

    二十一.TCP是如何保证可靠数据传输的? TCP提供一种面向连接的.可靠的字节流服务. 面向连接:意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个TCP连接.在 ...

  8. 【网络编程入门】使用socket在Linux下实现即时通信软件

    使用socket在Linux下实现即时通信软件 在前一篇文章中讲到了如何使用winsock:[网络编程入门]在C++中使用Windows TCP Sockets,也算是勉强入门了吧,接下来自己写一下在 ...

  9. 基于Linux下的即时通讯聊天室项目(全代码 有注释 可直接运行)

    基于Linux下的即时通讯聊天室项目 一.序言 二.具体功能 三.系统客户要求 四.具体代码 1.服务器代码 2.客户端代码 一.序言 最近在写一个基于Linux下的聊天工具 它适合于局域网内所有人进 ...

最新文章

  1. Codeforces 768E:Game of Stones
  2. redis主从配置 从而实现数据备份和读写分离
  3. oracle 触发器 upsert,如何使SQLAlchemy insert与Postgres多处理proof upsert触发器一起工作?...
  4. 理解CSS3 Flexbox
  5. docker安装sentinel的超简单步骤
  6. 演说之禅:演说中的图效优势
  7. 支持向量机在风控竞赛数据集上的应用
  8. 计算机形导论形考作业答案,计算机导论形考
  9. WEB 安全之 SQL注入 二 暴库
  10. centos7挂载nas存储_CentOS 7配置NAS(网络共享存储)
  11. 教师计算机课游戏教学设计,有趣的游戏教学设计及课堂实录
  12. J2EE开发全程实录
  13. 高并发解决方案——Redis(一)
  14. Asset Pricing:Introduction
  15. Drools简单入门
  16. 王牌英雄(Awesomenauts)研究报告
  17. Android Studio-Didn't find class XXX on path: DexPathList [zip file /data/xxx/base.apk]解决方法
  18. 什么是反射?有什么作用?
  19. 一个人最顶级的才华,是会填坑
  20. google海底光缆图_Google领头建造横跨太平洋海底光缆PLCN:连接香港,120Tbps

热门文章

  1. python爬取天天基金_分享天天基金数据爬取的Python脚本
  2. Java 函数式编程合集
  3. 类的继承python事例_【Python五篇慢慢弹(5)】类的继承案例解析,python相关知识延伸...
  4. 如何使用PHP和MySQL搭建一个简单的算命网站
  5. 最全PHP防止sql注入方法
  6. 邮箱注册(发送邮件验证码;QQ邮箱)
  7. 以古典大师之名 飞利浦X3再现Fidelio王者风范
  8. 【转载】卡卡论坛UFO不幸外人的《【原创】手工检测病毒(2月19日更新表格)【新手发帖必读】》
  9. Date类和LocalDate类的使用
  10. umi框架:react优雅引入阿里巴巴矢量图标库