1、
C语言也能开发网站!!!一通百通!!!一切语言都是纸老虎!
2、
3、
动态Web的原理:浏览器向服务器发送请求,服务器响应请求然后将响应的结果以HTML的方式发送给浏览器,浏览器将HTML显示出来。
4、
本课程讲解的C语言开发Web的技术叫“CGI”,网上搜索相关资料的关键字“C语言 CGI”。
5、
课程需要HTML的基础,如果HTML不熟悉的话请参考《自己动手写网站》的课程 ,视频下载页面: http://www.rupeng.com/forum/forum-28-1.html
6、
环境准备:
a)
下载安装apache,此软件是Http服务器,和TomcatIIS等类似。推荐下载地址:http://www.skycn.com/soft/1218.html。可能遇到的问题,已经安装了TomcatIIS等软件,将他们卸载(也可以修改Apache的端口,不过新手还是直接卸载其他软件比较好)。
b)
VCC-FreeDevC++TCPellesC等任意一款可以开发控制台C程序的开发工具(几乎所有C开发工具都支持)。课上将使用VC
7、
开发第一个CGI程序
a)
新建控制台程序
b)
代码:
#include "stdafx.h"

int main(int argc, char* argv[])
{

printf("Contenttype:text/html\n\n"); //根据HTTP协议,这里一定要有个空行。

printf("Hello RuPeng!");


return 0;
}
c)
编译生成exe程序(比如aaa.exe),将exe拷贝到apachecgi-bin目录,一般是C:\Program Files\Apache Software Foundation\Apache2.2\cgi-bin
d)
打开浏览器,敲入:http://127.0.0.1/cgi-bin/aaa.exe
e)
分析:127.0.0.1是什么?main程序就是普通的控制台程序,printf只是前边多了一句“printf("Contenttype:text/html\n\n");”,暂时不关心它的意义。
f)
每次修改后都要重新拷贝execgi-bin,有没有简单的方法?VC中设置输出路径:设置(settings)→连接(Link)→输出文件名(Output FileName)。注意每次修改要重新编译才能起作用。
8、
一定注意程序是运行在服务器上的,比如将CGI程序放到如鹏的服务器上,那么aaa.exe是运行在如鹏服务器上的,只不过是把printf的结果打印到用户的浏览器上而已。CGI程序中可以使用所有的C语言特性、函数、调用ODBC访问数据库等等。如果不调用Windows 的函数,则可以一行不动的移植到Linux/Unix等操作系统下。
9、
输出完整的HTML页面:

#include "stdafx.h"
#include "stdlib.h"//add getenv

int main(int argc, char* argv[])
{

printf("Contenttype:text/html\n\n"); //根据HTTP协议,这里一定要有个空行。

printf("<html>");


printf("<body>"); 

printf("hello <a href='http://www.rupeng.com'>RuPeng.com</a>");

printf("</body>");


printf("</html>"); 


return 0;
}


10、
输出环境变量:
a)
getenv,在stdlib.h,
char * getenv(const char *),参数是环境变量的名字,返回值是环境变量的值,都是字符串(确切的说是字符指针)类型。
b)
printf("%s ",getenv("SERVER_SOFTWARE"));
c)
可选值:
HTTP_USER_AGENT
递交表单的浏览器的名称、版本 和其他平台性的附加信息。

SERVER_NAME
CGI脚本运行时的主机名和IP地址.

SERVER_SOFTWARE
你的服务器的类型如:
CERN/3.0

NCSA/1.3.

SCRIPT_NAME
指向这个CGI脚本的路径,
是在URL中显示的(,
/cgi-bin/thescript).

REMOTE_HOST
递交脚本的主机名,这个值不能被设置.

REMOTE_ADDR
递交脚本的主机IP地址.

QUERY_STRING
脚本参数或者表单输入项(如果是用GET递交).
QUERY_STRING
包含URL中问号后面的参数.

CONTENT_TYPE
如果表单是用POST递交,
这个值将是
application/x-www-form-urlencoded.
在上载文件的表单中,
content-type
是个
multipart/form-data.

CONTENT_LENGTH
对于用POST递交的表单,
标准输入口的字节数.

REQUEST_METHOD
POST
 
GET,
取决于你的表单是怎样递交的.

HTTP_REFERER
递交表单的文本的
URL,不是所有的浏览器都发出这个信息,不要依赖它

HTTP_ACCEPT
浏览器能直接接收的Content-types

GATEWAY_INTERFACE
运行的CGI版本
SERVER_PROTOCOL
服务器运行的HTTP协议.

SERVER_PORT
服务器运行的TCP.

11、
怎么无法换行?
“\n”是文本级别的换行,浏览器把”<br/>”才当成换行。但是“a\nb”和“ab”没有不同吗?看一看,分析一下。
12、
请求参数
a)
可以通过地址给CGI程序传递参数“aa.exe?name=tom”,参数和CGI名称之间用“?”间隔,参数的“名”、“值”之间用“=”间隔
b)
取参数printf("%s\n",getenv("QUERY_STRING"));
c)
打招呼,单个参数:

char* pQueryStr=getenv("QUERY_STRING");

char pName[256];

sscanf(pQueryStr,"name=%s",pName);

printf("Hello %s!\n",pName);
d)
计算器,多个参数。多个参数键值对之间用“&”分割:

char* pQueryStr=getenv("QUERY_STRING");

int i1,i2;

sscanf(pQueryStr,"i1=%i&i2=%i",&i1,&i2);

printf("%i+%i=%i\n",i1,i2,i1+i2);
13、
课下练习:
a)
两个参数,“girl=lily&boy=tom”,打印“lily love tom
b)
登录功能,“username=admin&password=123456”,打印“密码错误”、“登陆成功”。
c)
思考:CGI程序太危险了,在CGI程序中写system(“format d:”)就可以格式化浏览者的硬盘了???
14、
HTMLCGI提交参数
HTML有头Head、有身体Body
表单:formPlease fill the form。注册、登录都是一种申请,都需要Fill the form

<input type="text" name="boy"/>文本框
<input type="submit"/>提交按钮
action=http://127.0.0.1/love 表示表单要提交给谁处理。否则大街上随便有人给你表单,你填了 都不知道给谁

乱码问题,后面的课程
14、课后作业:
 连接数据库,结合18ODBC的知识,开发注册、登录功能的Web程序。
判断浏览者的IP地址、浏览器类型、操作系统类型等

C语言也能干大事》第二十节 多线程开发
1、多线程也是所有语言中都有的一个重要特性,一通百通的一个东西。
2Sleep函数简介。为了简化问题,用Sleep函数模拟耗时操作,实际项目中用长时间数据库操作、读取大文件、网络操作等替换Sleep就可以。Sleep函数例子:msgbox 1,sleep 50000,msgbox 2。放一个额外button上去,更有效果。Sleep过程中界面是没有响应的,同理长时间的数据库操作、文件操作、网络操作等也会造成没有响应,原理简单分析:消息循环。
3CreateThread函数:CreateThread( NULL, 0, ThreadFunc, 0, 0, 0 );一般注意第3个参数即可,回调,回调函数格式。Void指针(可以指向任何类型的数据)。

DWORD WINAPI ThreadFunc( LPVOID lpParam )
{
return 0;
}

改造msgbox的例子,暂时第一个参数传NULL
CreateThread之后加一句msg,更明白主线程不会等着子线程。
小例子:取消登录功能的使用。
总结,什么叫线程。普通的执行顺序是顺序执行一句执行完成执行另外一句,但是一旦某一句执行时间很长,后面的就要等;线程(Thread)就是主干执行流程中分出去的一条线,独立于主线程运行,在主线程中只是通过CreateThread把线程调度起来,然后就继续向后执行了,不等着线程执行完毕,让分出去的线程自己折腾去。
4、给线程传递参数。回忆:给子对话框传递参数。给子线程传递参数使用第四个参数,子线程函数中lpParam参数的值就是传递过来的参数。注意传递的是指针
1
CreateThread( NULL, 0, ThreadFunc, TEXT("如鹏网"), 0, 0 );
MessageBox(NULL,(TCHAR *)lpParam,TEXT(""),MB_OK);
(2):
CreateThread( NULL, 0, ThreadFunc, hwnd, 0, 0 );
SetDlgItemText(hwnd,IDC_OK,"hello");

讲故事:数据导入。课下作业:读取data.txt,将数据导入数据库,这个整合了线程、ODBCIO操作的知识。听明白我讲的东西你只掌握了5%,只有把这个作业做出来才是真的懂了。实时显示数据处理进度,并且用户可以随时取消数据处理。
data.txt下载:  data.rar (294 Bytes, 下载次数: 919)

上节课作业点评。待优化点。最好不能每次插入数据都重新连接数据库,因为非常慢。每次插入一条数据要10秒。数据库连接是非常耗时的,所以最好是连接一次,然后多次插入,这样速度会快很多。
1、 函数库:把函数组织起来,供程序调用,函数的复用。静态库与动态库。
动态库、静态库是其他语言中也都有对应概念的东西,也是一通百通的。C#中的Assembly、Java中的jar等。
静态库:编译时代码编译进exe中,会使得程序体积非常庞大。不利于模块的共享,比如ODBC的库如果是以静态库的方式的话计算机里访问数据库的程序都会连接进去ODBC的代码,也许你的Windows就不止2、3GB那么大了,可能20GB都不止。优点:不会有dll hell的问题。好像“企业间的吞并”
动态库:dll。代码在dll中,其他程序调用dll中的代码,多个程序可以共享。缺点:dll hell(dll地狱),版本问题。好像“企业间的合作”。哪怕不需要被其他程序共享,仍然可以通过dll的方式减小单个exe的尺寸,并且有利于开发人员的分工。
2、 编写dll。新建Win32 dynamic link library
只有在函数前标注:extern "C" _declspec(dllexport) 的函数才能被其他的程序通过dll的方式调用

extern "C" _declspec(dllexport) int add(int i1,int i2)
{

return i1+i2;
}

(1) 静态链接:新建win32 DialogBased application。把dll和lib拷贝到工程目录下:
声明:
#pragma comment(lib, "dll1.lib")
extern "C" _declspec(dllexport) int add(int i1,int i2);
然后就可以像调用普通函数一样调用了。
如果dll中函数很多的话,一般都是由dll开发者开发好.h文件,把函数的声明都写好,然后供其他人调用,只要include就好了。
(2) 动态链接:

//有__cdecl __stdcall 等不同的调用约定,也就是参数的压栈顺序等,暂时不用关心,只要保证调用的时候和dll中的调用约定一样就可以。
//否则会报错:The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention. 
typedef int(__cdecl* FunctionAdd)(int,int);


函数指针

HMODULE hModule;


FunctionAdd add;


hModule = LoadLibrary("dll1.dll");//调试时hModule为0x10000000 
If(NULL==hModule )
{
//error.
}


add =(FunctionAdd)GetProcAddress(hModule,"add");



If(NULL==add)
{
//error
}




int r = add(1,1);
Wsprintf msgbox


FreeLibrary(hModule);




2、其他语言也有对应的概念,C#、Java中的反射。
静态链接和动态链接的区别:静态链接在程序启动的时候就会去检查dll是否存在,如果不存在在启动的时候就会报错,程序无法启动;动态链接是在运行到的时候才会去检查是否存在,而且可以由程序员决定在dll不存在的时候判断逻辑。
要区分静态库、动态库。静态调用dll和动态调用dll。
3、 插件机制。课下作业:
a. 将你编程中经常用到的重复性代码封装成dll,并且写验证程序调用它们。
b. 参考机制FindFirstFile等函数。导出GetPluginName、Execute两个函数。
void GetPluginName(TCHAR* name);
void Execute(...);
参考扩展阅读:调用约定:cdecl、stdcall。
[/free

1、Dll的调试。填入主EXE的全路径。

GetCurrentDirectory、getcwd。不能依靠他们用他们取得exe的文件夹。因为他们是“当前目录”,而不是“exe所在的目录”(Linux、DOS:目录,Windows:文件夹)。
GetModuleFileName():当前EXE文件的全路径。D:\俺的文档\培训\用C语言写有用的东西\第二十二节\zuoye\Debug\zuoye.exe

strcpy
指向同一个数组的指针可以做减法,结果就是两个指针的距离。

通配符(搜一下)
*.dll所有dll文件
*.txt所有txt文件
a.*就是文件名是a,后缀任意的文件
a*.txt就是所有文件名以a开头的文本文件。
C语言:堆数据、栈数据。很多公司面试、笔试必考内容。.
调试的时候注意设置活动工程。
类:属性(字段)、方法(Method、也就是函数)
函数名字一样,但是参数不一样,函数的重载(overload)。把对象的指针放在函数的第一位,约定而已。
2、作业:开发如鹏tools,最基本的记事本的功能,增加一个“插件”菜单项,菜单项的内容是动态根据插件目录中的插件构造出来的,可能的插件有:文字转大写、文字转小写、文字去空格、从一个网址中粘贴文字、插入计算器计算结果、自动定时保存记事本中的文件等。
自己写出来,扩展成为如鹏的一个开源项目。如鹏NotePad
3、C语言面向对象开发
a) 面向对象不是代替面向过程的,面向对象是面向过程的一个补充和发展,面向对象的本质仍然是面向过程。
b) 面向对象只是一种组织数据和算法的方法思维方式而已,与语言无关,Java、C#等面向对象的语言只不过是从语法层面上简化了面向对象代码的编写而已,C之类的面向过程的语言也能实现面向对象。
c) 面向对象的三个特征:封装、继承、多态。许多书上来就把封装、继承、多态稀里哗啦的讲完了,这是不符合认知规律的。只有编写过大量面向过程的代码以后,再来学封装,才能真正的掌握封装;只有熟练应用封装一段时间以后才能真正的掌握继承,而只有编写过大量的使用封装、继承的代码以后才能真正的掌握多态。所以不要急于求成,咱们的课只讲C语言实现面向对象中的封装。对于初学者来说把封装用好了、用熟了就可以。高级的东西确实看起来NB,也很好炫耀,但是功力不到位的话玩儿高级的东西很容易走火入魔。
d) 通过C语言学面向对象更不容易像学Java、C#那样把类当成多高级的技术来学的人,学Java、C#的人很容易写出“伪类”来。


类:一堆有共同特征对象的抽象,对象就是真实的东西。人只是抽象,看不到摸不着的,杨中科才是一个人的具体的实例,才是对象。
5、第一个类
a) C语言进行面向对象开发并不会引入新的语法。结构体、指针、函数。
b) 用结构体实现类的定义

struct Person
{

TCHAR* name;

int age;
};


对比C#中Person类的定义:

Class Person
{
Public String Name{set;get;}
Public int Aget{set;get;}
}

创建一个Person类的实例:Person p1;
c) 为成员变量赋值:p1.name="tom";p1.age=20;
d) 创建一个Person类的方法:void SayHello(Person p)
e) 创建一个增长年龄的方法:void IncreaseAge(Person p),为什么会出现这种现象。结构体是值传递的,克隆人,因此在需要改变结构体状态的环境中(比如面向对象编程)必须使用结构体指针。这一点在其他语言中也有类似的问题,也是一个易错点,因此需要注意。理解一下学C语言时Swap:交换两个变量。
f) PPerson、Person*;typedef Person *PPerson;typdef struct Person{} *PPerson。
g) 约定,把类的实例指针做为方法的第一个参数。
h) 类的多个实例。
设计一个矩形类,包含相交判断、相交运算等

Rectangle:长、宽,左上角坐标。X,y,height,width.两个方法:计算矩形的面积,一个是计算两个矩形是否有相交的部分。(是某年的微软笔试题。)