分类: 2010 April

UNIX网络编程 helloworld

今天开始看unp volume1(Unix Network Programming)了,发现W.Richard Stevens真的很有风格,不管是apue还是unp,书里的程序第一行必定是书名,比如说#include “apue.h”和#include “unp.h”,用头文件将打印函数,包裹函数封装起来无可厚非,但对我这种初学者来说,底层的东西变成了透明,写起程序来虽然会很方便,对程序的把握却又低了一个层次..

nup volume1第一章很简单,介绍了2个很容易的程序,一个服务端做时间伺服器,另一个客户端查询时间,数据的传输是从服务端到客户端的,为了练手,我照着程序写了个客户端传输数据给服务端的hello world,当然把#include “unp.h”去掉了..源码和编译好的程序在这..

记下各函数原型做个备忘.

socket

#include <sys/socket.h>
int socket(int family, int type, int protocol);

bind

#include <sys/socket.h>
int bind(int socket_fd, const struct sockaddr *myaddress, socklen_t address_length);

listen

#include <sys/socket.h>
int listen(int socket_fd, int backlog);

connect

#include <sys/socket.h>
int connect(int socket_fd, const struct sockaddr *server_address, socklen_t address_length);

accept

#include <sys/socket.h>
int accept(int socket_fd, struct sockaddr *client_address, socklen_t address_length);

recv

#include <sys/socket.h>
ssize_t recv(int socket_fd, void *buffer, size_t buffer_size, int flags);

send

#include <sys/socket.h>
ssize_t send(int socket_fd, const void *buffer, size_t buffer_size, int flags);

回头看看动态签名有没有效..

MySQL 可视化工具

What

这几天用Gtk写Mysql的GUI前台,今天终于写得差不多了,在博客里记记,源码在这http://godorz.info/wp-content/uploads/2010/04/mysqlgui.zip, 1000多行几乎都是手打,可以自由复制,但其中的致谢信息不可去掉,因为贪图方便,连基本的头文件和实现都没分开,大汗..编译命令为: gcc -o main main.c -I/usr/include/mysql -L/usr/lib/mysql -lmysqlclient `pkg-config –cflags –libs gtk+-2.0` ,截图如下:

How

程序分为几个部分,各函数功能如下:

  • cb_openmenuitem()用来显示子对话框,输入服务器,端口,用户名和密码,点击Connect按钮后调用cb_connectbutton()函数,得到用户输入,初始化myconnection,连接服务器.
  • cb_aboutmenuitem()是一些版权信息.其实是用来练手对话框的.
  • cb_executebutton()用于得到用户在主面板的输入(mysql script命令),调用make_tree_view( )将执行结果显示在showresultvbox中.
  • cb_savebutton()将用户修改过的数据表保存下来.想法可以参考这里http://godorz.info/2010/04/display-mysql-data-with-gtkclist/
  • GType getdatatype(MYSQL_FIELD *field)将数据表每一列的数据类型MYSQL_TYPE转化为对应的Gtk中的数据类型G_TYPE.函数在这里没有用上,其实是为了以后修改代码时做准备的.比如说要提供一个时间类型的加减功能,就必须事先知道该列中存储的数据类型为吗MYSQL_TYPE_TIME而不是MYSQL_TYPE_STRING.
  • SETLIST()这是一个宏命令,为了使treeview每一列的类型为editable,需要对每一列绑定不同的回调函数,在回调函数中指名列号.在程序中,我认定数据表列数不超过30列,30个基本一样的回调函数写起来很麻烦,因此用宏写了.
  • setup_tree_view()根据传入的MYSQL conn新建显示数据表需要用到的treeview,指定列名和每列的数据类型(这里全部指定为G_TYPE_STRING),然后设置每列均为可编辑.
  • make_tree_view()有了调用setup_tree_view()得到的treeview后,将数据写在treeview上,然后放在滚动窗口scrolled_win,之后将scrolled_win放在showresultvbox上,显示数据表的内容.
  • make_clist()和setup_tree_view() + make_tree_view()的功能类似,显示数据表的内容,只不过这里改成了gtk_clist实现,而不是gtk_tree_view,在程序中这个函数也没有被使用,仅仅是做个比较罢了.
  • create_and_fill_model()设置数据库名称和数据表名称的显示类型treestore–树形显示,而不是数据表内容的treeview–列表显示.
  • create_view_and_model()将数据库名称和数据表名称以树形形式dbsandtablestreeview放至滚动窗口scrolled_win_dbs_tables,再将滚动窗口显示在showdbsandtablesvbox中.
  • on_changed()接受用户选择数据库(表)的事件,将选择的数据库和数据表分别存为selecteddatabase和selectedtable

Problems

遇到的比较恶心的问题有好几个,好在大概都解决了:

  1. 回调函数,SETLIST()就是很恶心的一个,很认真的查了资料,却没有发现好的解决方法,貌似每列一个回调函数指定该列为可编辑是必须的.还有一个是树形显示中接受用户选择的GtkTreeSelection *selection的回调函数,g_signal_connect可以,但g_signal_connect_swapped就不行了,printf()的方式调式了好久才发现这个问题,悲剧的是到现在还是搞不清两者的区别..
  2. vbox的刷新,其实这么说是不正确的,因为new出来的一个vbox,是无法再在面板上显示出来的,唯一的方法是将vbox内的widget全部remove掉,然后再gtk_container_add进去,remove时需要注意的是事先需要得到需要remove的widget的引用(gtk_widget_ref)..假如该widget之后不再需要使用了,还需要将引用去掉(gtk_widget_unref).
  3. 修改后的数据表的保存,当一个单元cell为空时,将gtk_tree_model_get得到的字符串s以printf打印出来是(null),但理所当然地以为s == NULL是个大错,没有值与值为空在界面上看来都是一个空的单元,但它们还是存在区别的,这也是为什么我将对空的单元内容的判断从if( s == NULL)改成了if( s == NULL || strlen(s) < 1 )..不过,虽然知道这一点,我写程序时还是偷懒了,一律将空的单元以””的形式写回数据表..其实,负责任点的话,是需要在insert时仅仅写明需要插入的列的,至于空的单元,不理就是了.顺便提一下数据表的问题,我没有对字符编码做判断,因此数据出现中文时程序显示乱码,并非判断很难,而是我对用数据库存储中文或多或少存在一些偏见.

To be continued..

前面解决了一些问题,但是,程序没有解决的问题还有很多:

  1. 为了数据的安全,每次对数据表的操作,不管是选择,显示,修改还是保存,其实在操作之前都是需要重新连接数据库的,而在操作完之后将连接关闭.而且保存是其实还是有一些数据要判断的,因为affected_row很不靠谱.这个真的很简单,但是我没改,因为有太多的bug了.:-)
  2. 程序主面板中还有一个命令输入框scriptentry和执行按钮executebutton,其实都是测试程序时留下的,本该去掉的,如果不去掉的话,至少应该在cb_executionbutton里加入一些判断语句,禁止用户执行create table/database和drop table.database等语句,因为真的有太多bug了.一旦程序出现问题把数据库搞坏就对不起自己了.也正因为如此,凡是涉及到删除的功能程序中全部没有提供,在数据表中新建一列都被和谐了..
  3. 然后,虽然clist比起treeview真的太简单了,连单元可编辑都步支持,更别提在单元里放入widget了..但想了几天后,我觉得是可以曲线救国的,用gtk_clist_get_text()得到鼠标点击的行列,然后新建一个输入框,大小等同于clist中一个单元,让它悬浮在鼠标点击单元之上,将其完全覆盖,当用户停止输入后,将得到的结果覆盖单元原值,这么依赖,用户看起来的效果就和单元可编辑没什么两样啦.

Reference

最主要的参考资料有:

  • Gtk+ 2.0 tutorial
  • Dev help

然后是gtk+的邮件列表(gtk-devel-list@gnome.org),老外真的很热心,有什么问题一问基本上都有人回,但是,为了节省大家的时间,不妨先看完How To: Ask Questions The Smart Way..这里是邮件列表的archive,有什么不懂的在这先搜搜..http://www.mail-archive.com/gtk-app-devel-list@gnome.org/info.html

EOF

想起一个笑话,从前有个太监,下面呢..下面没了..

其实下面还有的,测试一下动态签名图:

GtkClist展示MySQL数据(2)

之前困惑于GtkClist中数据类型与MySQL数据类型之间转换的问题,在gtk-app-dvel-list邮件列表上发了一个主题提问:

Arthur1989 says:

Hello, I used clist to display data iI fetch from mysql server,but when writing data back into the table of database, I got this question: the data type of clist is always gchar *, while there are quite a lot of other data types in mysql. What can I do to get this question solved? Any tips will be appreciated.

今天发现居然有人把实现的源码都贴到邮件里了..转到博客记下.

Shawn Bakhtlar(***@hotmail.com)replys:


//Ran into the same problem.

//I use a structure like

_IsiField {

	 int type,
	  int pos,
	   int ....

}

//Then create my own list with

_IsiList {

	 GList Fields;
	  GList rows;

}


//Every time retrieve a set of values, I have a routing which sets type to a G_TYPE, which corresponds to the MYSQL_TYPE


//Here is what I do:

GList *
isi_database_fetch_fields(IsiDatabase *self)
{

   MYSQL_FIELD *field;
   IsiFields *l;
   GList *gl = NULL;
   guint column = 0;

   /* Sanity Check */
   g_return_val_if_fail(self != NULL, NULL);
   g_return_val_if_fail(self-&gt;priv != NULL, NULL);
   g_return_val_if_fail(self-&gt;priv-&gt;dispose_has_run != TRUE, NULL);
   g_return_val_if_fail(self-&gt;priv-&gt;res != NULL, NULL);

   /* Rewind the feild set */
   mysql_field_seek(self-&gt;priv-&gt;res,0L);


   while((field = mysql_fetch_field(self-&gt;priv-&gt;res)))
   {
	   /* Initialize a new IsiFields structure */
	   //l = (IsiFields*) g_new0(IsiFields, 1);
	   l = g_new0(IsiFields, 1);

	   /* Set the values */
	   l-&gt;alias = g_strdup(field-&gt;name);
	   l-&gt;name = g_strdup(field-&gt;org_name);
	   l-&gt;length = field-&gt;length;

	   /* always make fields visable */

	   l-&gt;hidden = FALSE;
	   l-&gt;sortable = FALSE;
	   l-&gt;pos = column++;


	   switch (field-&gt;type){

		   /* Integer types */
		   case MYSQL_TYPE_TINY:
		   case MYSQL_TYPE_SHORT:
		   case MYSQL_TYPE_INT24:

			   /* Check for signage */
			   if (field-&gt;flags &amp;amp; UNSIGNED_FLAG)
				   l-&gt;type = G_TYPE_UINT;
			   else
				   l-&gt;type = G_TYPE_INT;

			   break;


			   /* Long types */
		   case MYSQL_TYPE_LONG:
		   case MYSQL_TYPE_LONGLONG:

			   /* Check for signage */
			   if (field-&gt;flags &amp;amp; UNSIGNED_FLAG)
				   l-&gt;type = G_TYPE_ULONG;
			   else
				   l-&gt;type = G_TYPE_LONG;
			   break;


			   /* Decimal types */
		   case MYSQL_TYPE_DECIMAL:
		   case MYSQL_TYPE_NEWDECIMAL:
		   case MYSQL_TYPE_FLOAT:
		   case MYSQL_TYPE_DOUBLE:
			   l-&gt;type = G_TYPE_DOUBLE;
			   break;

			   /* Bit types */
		   case MYSQL_TYPE_BIT:
			   l-&gt;type = G_TYPE_BOOLEAN;
			   break;

			   /* ENUM types */
		   case MYSQL_TYPE_ENUM:
			   l-&gt;type = G_TYPE_ENUM;
			   break;

			   /* All other types */
		   default:
		   case MYSQL_TYPE_STRING:
		   case MYSQL_TYPE_VAR_STRING:
		   case MYSQL_TYPE_BLOB:
		   case MYSQL_TYPE_SET:
		   case MYSQL_TYPE_TIMESTAMP:
		   case MYSQL_TYPE_DATE:
		   case MYSQL_TYPE_TIME:
		   case MYSQL_TYPE_DATETIME:
		   case MYSQL_TYPE_YEAR:
		   case MYSQL_TYPE_GEOMETRY:
		   case MYSQL_TYPE_NULL:

			   if(l-&gt;length &lt;= 1){
				   l-&gt;type = G_TYPE_CHAR;
			   }else{
				   l-&gt;type = G_TYPE_STRING;
			   }
			   break;
	   }


	   /*DEBUG*/
	   //g_print(&quot;%s %d %d \n&quot;, l-&gt;alias,l-&gt;type,l-&gt;length);

	   /* Save pointer to list */
	   gl = g_list_append(gl,(gpointer)l);

   }

   return gl;
}



//now convert the row data to a GList and you have two GLists in your one lists, one with the field header info, the other with the data.
//and create the liststore like this:

GtkTreeModel *
isi_display_liststore_create(IsiDisplay *self, GList *fields)
{
	guint num_fields, i;
	IsiFields *l;
	GtkTreeModel *model;
	GType *types;
	guint search_col_adj = 0;

	/* Sanity Check */
	g_return_val_if_fail(self != NULL, NULL);
	g_return_val_if_fail(self-&gt;priv != NULL, NULL);
	g_return_val_if_fail(self-&gt;priv-&gt;dispose_has_run != TRUE, NULL);

	/* Get the number of fields */
	if(fields != NULL){

		num_fields = g_list_length(fields);

		/* Initialize values based on number of columns */
		types = (GType*) g_new0( GType, num_fields);

		for(i=0;i&lt;num_fields;i++){

			l = (IsiFields*)g_list_nth_data(fields,i);
			types[i] = l-&gt;type;

		}

		/* create the model store for data input */
		model =  (GtkTreeModel*) gtk_list_store_newv(num_fields,types);

		g_free(types);

		for(i=0;i&lt;num_fields;i++){

			l = (IsiFields*)g_list_nth_data(fields,i);

			/* Setup sorting functions for the modle */
			switch(l-&gt;type){
				case G_TYPE_INT:
					l-&gt;sortable=TRUE;
					gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), l-&gt;pos, sort_by_int,(gpointer) l-&gt;pos, NULL);
					break;
				case G_TYPE_UINT:
					l-&gt;sortable=TRUE;
					gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), l-&gt;pos, sort_by_uint,(gpointer) l-&gt;pos, NULL);
					break;
				case G_TYPE_LONG:
					l-&gt;sortable=TRUE;
					gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), l-&gt;pos, sort_by_long,(gpointer) l-&gt;pos, NULL);
					break;
				case G_TYPE_ULONG:
					l-&gt;sortable=TRUE;
					gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), l-&gt;pos, sort_by_ulong,(gpointer) l-&gt;pos, NULL);
					break;
				case G_TYPE_DOUBLE:
					l-&gt;sortable=TRUE;
					gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), l-&gt;pos, sort_by_double,(gpointer) l-&gt;pos, NULL);
					break;
				case G_TYPE_STRING:
					l-&gt;sortable=TRUE;
					gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), l-&gt;pos, sort_by_string,(gpointer) l-&gt;pos, NULL);
					break;
			}
		}

	return model;
}

//Hope this helps,
//Shawn

认真的看了一下,发现原来和自己想的差不多,都是事先记录好MySQL数据表中每列的数据类型,往回写时做个判断进行转换..只不过我是想用类似于map的字符串哈希映射实现的,这里变成了switch case罢了..

PS: 本文在vim下通过vimpress插件编辑发布,不知道浏览器看起来怎样,呵呵..