首页 › 月度存档 › 三月 2014

CentOS下搭建NGINX+SVN

因为没有什么可以定制的, 所以svn直接使用系统自带的包管理软件安装,
以centos系列为例, 命令如下:

yum install subversion

然后是nginx的安装:

mkdir -p /opt/soft 
groupadd web 
useradd http -g web -s /sbin/nologin 
cd /opt/soft 
wget http://sourceforge.net/projects/pcre/files/pcre/8.12/pcre-8.12.tar.gz 
wget http://www.openssl.org/source/openssl-1.0.0d.tar.gz 
tar zxvf pcre-8.12.tar.gz 
tar zxvf openssl-1.0.0d.tar.gz 
tar zxvf nginx-0.8.54.tar.gz 
cd nginx-0.8.54 
./configure --prefix=/opt/nginx --user=http --group=web --with-http_stub_status_module --with-http_ssl_module --with-md5=/usr/lib/ --with-pcre=/opt/soft/pcre-8.12 --with-openssl=/opt/soft/openssl-1.0.0d 
make 
make install 

root 指定到/data/wwwroot

vi /opt/nginx/conf/nginx.conf 

创建svn仓库:

svnadmin create /data/svn

修改配置文件:

vi /data/svn/conf/authz
[groups] 
admin=test #admin组里的成员 
[/] 
test = rw #成员对/目录的权限: r读,w写 
vi /data/svn/conf/passwd 
[users] 
test = 123456 #用户test的密码 
vi /data/svn/conf/svnserve.conf 
anon-access = none 
auth-access = write 
password-db = passwd 
authz-db = authz 
realm = My First Repository

启动svnserv

svnserve -d -r /data/svn

也可以使用–listen-port参数指定端口,启动多个仓库
导入项目,假如项目目录为svn://host/test ,创建svn仓库副本(nginx的读取目录)

cd /data/wwwroot 
svn co svn://localhost/test --username test --password 123456

设置svn更新后自动更新nginx的读取目录:

vi /data/svn/hooks/post-commit 
#!/bin/sh 
svn up /opt/www/test/ --username "lyf" --password "123456" 
chown -R http:web /opt/www #这一步或许可以滤过

给脚本增加可执行权限

chmod +x /data/svn/hooks/post-commit

大功告成, 现在可以测试一下了,svn更新后nginx读取到的内容(访问http)也随之变化, 是不是比apache还有简单

nginx rewrite规则和参考

正则表达式匹配,其中:

* ~ 为区分大小写匹配
* ~* 为不区分大小写匹配
* !~和!~*分别为区分大小写不匹配及不区分大小写不匹配

文件及目录匹配,其中:

* -f和!-f用来判断是否存在文件
* -d和!-d用来判断是否存在目录
* -e和!-e用来判断是否存在文件或目录
* -x和!-x用来判断文件是否可执行

flag标记有:

* last 相当于Apache里的[L]标记,表示完成rewrite
* break 终止匹配, 不再匹配后面的规则
* redirect 返回302临时重定向 地址栏会显示跳转后的地址
* permanent 返回301永久重定向 地址栏会显示跳转后的地址

一些可用的全局变量有,可以用做条件判断(待补全)

$args
$content_length
$content_type
$document_root
$document_uri
$host
$http_user_agent
$http_cookie
$limit_rate
$request_body_file
$request_method
$remote_addr
$remote_port
$remote_user
$request_filename
$request_uri
$query_string
$scheme
$server_protocol
$server_addr
$server_name
$server_port
$uri

结合QeePHP的例子

    if (!-d $request_filename) {
    rewrite ^/([a-z-A-Z]+)/([a-z-A-Z]+)/?(.*)$ /index.php?namespace=user&controller=$1&action=$2&$3 last;
    rewrite ^/([a-z-A-Z]+)/?$ /index.php?namespace=user&controller=$1 last;
    break;

多目录转成参数

abc.domian.com/sort/2 => abc.domian.com/index.php?act=sort&name=abc&id=2

    if ($host ~* (.*)\.domain\.com) {
    set $sub_name $1;
    rewrite ^/sort\/(\d+)\/?$ /index.php?act=sort&cid=$sub_name&id=$1 last;
    }

目录对换

/123456/xxxx -> /xxxx?id=123456

    rewrite ^/(\d+)/(.+)/ /$2?id=$1 last;

例如下面设定nginx在用户使用ie的使用重定向到/nginx-ie目录下:

    if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /nginx-ie/$1 break;
    }

目录自动加“/”

    if (-d $request_filename){
    rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;
    }

禁止htaccess

    location ~/\.ht {
    deny all;
    }

禁止多个目录

    location ~ ^/(cron|templates)/ {
    deny all;
    break;
    }

禁止以/data开头的文件
可以禁止/data/下多级目录下.log.txt等请求;

    location ~ ^/data {
    deny all;
    }

禁止单个目录
不能禁止.log.txt能请求

    location /searchword/cron/ {
    deny all;
    }

禁止单个文件

    location ~ /data/sql/data.sql {
    deny all;
    }

给favicon.ico和robots.txt设置过期时间;
这里为favicon.ico为99天,robots.txt为7天并不记录404错误日志

    location ~(favicon.ico) {
    log_not_found off;
    expires 99d;
    break;
    }
    location ~(robots.txt) {
    log_not_found off;
    expires 7d;
    break;
    }

设定某个文件的过期时间;这里为600秒,并不记录访问日志

    location ^~ /html/scripts/loadhead_1.js {
    access_log   off;
    root /opt/lampp/htdocs/web;
    expires 600;
    break;
    }

文件反盗链并设置过期时间

这里的return 412 为自定义的http状态码,默认为403,方便找出正确的盗链的请求
“rewrite ^/ http://leech.c1gstudio.com/leech.gif;”显示一张防盗链图片
“access_log off;”不记录访问日志,减轻压力
“expires 3d”所有文件3天的浏览器缓存

    location ~* ^.+\.(jpg|jpeg|gif|png|swf|rar|zip|css|js)$ {
    valid_referers none blocked *.c1gstudio.com *.c1gstudio.net localhost 208.97.167.194;
    if ($invalid_referer) {
    rewrite ^/ http://leech.c1gstudio.com/leech.gif;
    return 412;
    break;
    }
    access_log   off;
    root /opt/lampp/htdocs/web;
    expires 3d;
    break;
    }

只充许固定ip访问网站,并加上密码

    root  /opt/htdocs/www;
    allow   208.97.167.194;
    allow   222.33.1.2;
    allow   231.152.49.4;
    deny    all;
    auth_basic "C1G_ADMIN";
    auth_basic_user_file htpasswd;

将多级目录下的文件转成一个文件,增强seo效果
/job-123-456-789.html 指向/job/123/456/789.html

    rewrite ^/job-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /job/$1/$2/jobshow_$3.html last;

将根目录下某个文件夹指向2级目录
如/shanghaijob/ 指向 /area/shanghai/
如果你将last改成permanent,那么浏览器地址栏显是/location/shanghai/

 rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;

上面例子有个问题是访问/shanghai 时将不会匹配

    rewrite ^/([0-9a-z]+)job$ /area/$1/ last;
    rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;

这样/shanghai 也可以访问了,但页面中的相对链接无法使用,
如./list_1.html真实地址是/area/shanghia/list_1.html会变成/list_1.html,导至无法访问。

那我加上自动跳转也是不行咯
(-d $request_filename)它有个条件是必需为真实目录,而我的rewrite不是的,所以没有效果

    if (-d $request_filename){
    rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;
    }

知道原因后就好办了,让我手动跳转吧

    rewrite ^/([0-9a-z]+)job$ /$1job/ permanent;
    rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;

文件和目录不存在的时候重定向:

    if (!-e $request_filename) {
    proxy_pass http://127.0.0.1;
    }

域名跳转

server
{
    listen       80;
    server_name  jump.c1gstudio.com;
    index index.html index.htm index.php;
    root  /opt/lampp/htdocs/www;
    rewrite ^/ http://www.c1gstudio.com/;
    access_log  off;
}

多域名转向

    server_name  www.c1gstudio.com www.c1gstudio.net;
    index index.html index.htm index.php;
    root  /opt/lampp/htdocs;
    if ($host ~ "c1gstudio\.net") {
    rewrite ^(.*) http://www.c1gstudio.com$1 permanent;
    }

三级域名跳转

    if ($http_host ~* "^(.*)\.i\.c1gstudio\.com$") {
    rewrite ^(.*) http://top.yingjiesheng.com$1;
    break;
    }

域名镜向

server
{
    listen       80;
    server_name  mirror.c1gstudio.com;
    index index.html index.htm index.php;
    root  /opt/lampp/htdocs/www;
    rewrite ^/(.*) http://www.c1gstudio.com/$1 last;
    access_log  off;
}

某个子目录作镜向

    location ^~ /zhaopinhui {
    rewrite ^.+ http://zph.c1gstudio.com/ last;
    break;
    }

discuz ucenter home (uchome) rewrite

    rewrite ^/(space|network)-(.+)\.html$ /$1.php?rewrite=$2 last;
    rewrite ^/(space|network)\.html$ /$1.php last;
    rewrite ^/([0-9]+)$ /space.php?uid=$1 last;

discuz 7 rewrite

    rewrite ^(.*)/archiver/((fid|tid)-[\w\-]+\.html)$ $1/archiver/index.php?$2 last;
    rewrite ^(.*)/forum-([0-9]+)-([0-9]+)\.html$ $1/forumdisplay.php?fid=$2&page=$3 last;
    rewrite ^(.*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/viewthread.php?tid=$2&extra=page\%3D$4&page=$3 last;
    rewrite ^(.*)/profile-(username|uid)-(.+)\.html$ $1/viewpro.php?$2=$3 last;
    rewrite ^(.*)/space-(username|uid)-(.+)\.html$ $1/space.php?$2=$3 last;
    rewrite ^(.*)/tag-(.+)\.html$ $1/tag.php?name=$2 last;

给discuz某版块单独配置域名

server_name  bbs.c1gstudio.com news.c1gstudio.com;
location = /
{
    if ($http_host ~ news\.c1gstudio.com$)
    {
    rewrite ^.+ http://news.c1gstudio.com/forum-831-1.html last;
    break;
    }
}

discuz ucenter 头像 rewrite 优化

    location ^~ /ucenter {
    location ~ .*\.php?$
    {
    #fastcgi_pass  unix:/tmp/php-cgi.sock;
    fastcgi_pass  127.0.0.1:9000;
    fastcgi_index index.php;
    include fcgi.conf;
    }
    location /ucenter/data/avatar {
    log_not_found off;
    access_log   off;
    location ~ /(.*)_big\.jpg$ {
    error_page 404 /ucenter/images/noavatar_big.gif;
    }
    location ~ /(.*)_middle\.jpg$ {
    error_page 404 /ucenter/images/noavatar_middle.gif;
    }
    location ~ /(.*)_small\.jpg$ {
    error_page 404 /ucenter/images/noavatar_small.gif;
    }
    expires 300;
    break;
    }
    }

jspace rewrite

    location ~ .*\.php?$
    {
    #fastcgi_pass  unix:/tmp/php-cgi.sock;
    fastcgi_pass  127.0.0.1:9000;
    fastcgi_index index.php;
    include fcgi.conf;
    }
    location ~* ^/index.php/
    {
    rewrite ^/index.php/(.*) /index.php?$1 break;
    fastcgi_pass  127.0.0.1:9000;
    fastcgi_index index.php;
    include fcgi.conf;
    }

wordpress rewrite

    location / {
    index index.html index.php;
    if (-f $request_filename/index.html){
    rewrite (.*) $1/index.html break;
    }
    if (-f $request_filename/index.php){
    rewrite (.*) $1/index.php;
    }
    if  (!-e $request_filename)
    {
    rewrite (.*) /index.php;
    }
    }

VC++七种延时方式

方式一:VC中的WM_TIMER消息映射能进行简单的时间控制。首先调用函数SetTimer()设置定时间隔,如SetTimer(0,200,NULL)即为设置200ms的时间间隔。然后在应用程序中增加定时响应函数OnTimer(),并在该函数中添加响应的处理语句,用来完成到达定时时间的操作。这种定时方法非常简单,可以实现一定的定时功能,但其定时功能如同Sleep()函数的延时功能一样,精度非常低,最小计时精度仅为30ms,CPU占用低,且定时器消息在多任务操作系统中的优先级很低,不能得到及时响应,往往不能满足实时控制环境下的应用。只可以用来实现诸如位图的动态显示等对定时精度要求不高的情况。如示例工程中的Timer1。   

方式二:VC中使用sleep()函数实现延时,它的单位是ms,如延时2秒,用sleep(2000)。精度非常低,最小计时精度仅为30ms,用sleep函数的不利处在于延时期间不能处理其他的消息,如果时间太长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中。如示例工程中的Timer2。   

方式三:利用COleDateTime类和COleDateTimeSpan类结合WINDOWS的消息处理过程来实现秒级延时。如示例工程中的Timer3和Timer3_1。以下是实现2秒的延时代码:

COleDateTimestart_time = COleDateTime::GetCurrentTime();
COleDateTimeSpanend_time = COleDateTime::GetCurrentTime() - start_time;
while(end_time.GetTotalSeconds())

 

方式四:在精度要求较高的情况下,VC中可以利用GetTickCount()函数,该函数的返回值是DWORD型,表示以ms为单位的计算机启动后经历的时间间隔。精度比WM_TIMER消息映射高,在较短的定时中其计时误差为15ms,在较长的定时中其计时误差较低,如果定时时间太长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中。如示例工程中的Timer4和Timer4_1。下列代码可以实现50ms的精确定时:   

DWORDdwStart = GetTickCount();   
DWORDdwEnd = dwStart;   
do   
{   
    dwEnd = GetTickCount() - dwStart;
}while(dwEnd)

为使GetTickCount()函数在延时或定时期间能处理其他的消息,可以把代码改为:

DWORDdwStart=GetTickCount();
DWORDdwEnd=dwStart;
do
{
    MSGmsg;
    GetMessage(msg,NULL,0,0);
    TranslateMessage(msg);
    DispatchMessage(msg);
    dwEnd=GetTickCount() - dwStart;
}while(dwEnd)

  

虽然这样可以降低CPU的占有率,并在延时或定时期间也能处理其他的消息,但降低了延时或定时精度。   

方式五:与GetTickCount()函数类似的多媒体定时器函数DWORDtimeGetTime(void),该函数定时精度为ms级,返回从Windows启动开始经过的毫秒数。微软公司在其多媒体Windows中提供了精确定时器的底层API持,利用多媒体定时器可以很精确地读出系统的当前时间,并且能在非常精确的时间间隔内完成一个事件、函数或过程的调用。不同之处在于调用DWORDtimeGetTime(void)函数之前必须将Winmm.lib和Mmsystem.h添加到工程中,否则在编译时提示DWORDtimeGetTime(void)函数未定义。由于使用该函数是通过查询的方式进行定时控制的,所以,应该建立定时循环来进行定时事件的控制。如示例工程中的Timer5和Timer5_1。   

方式六:使用多媒体定时器timeSetEvent()函数,该函数定时精度为ms级。利用该函数可以实现周期性的函数调用。如示例工程中的Timer6和Timer6_1。函数的原型如下:

MMRESULTtimeSetEvent(UINTuDelay, UINTuResolution, PTIMECALLBACKlpTimeProc, WORDdwUser, UINTfuEvent)

该函数设置一个定时回调事件,此事件可以是一个一次性事件或周期性事件。事件一旦被激活,便调用指定的回调函数,成功后返回事件的标识符代码,否则返回NULL。函数的参数说明如下:   
uDelay:以毫秒指定事件的周期。   
Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。
LpTimeProc:指向一个回调函数。   
DwUser:存放用户提供的回调数据。   
FuEvent:指定定时器事件类型:   
TIME_ONESHOT:uDelay毫秒后只产生一次事件   
TIME_PERIODIC:每隔uDelay毫秒周期性地产生事件。   

具体应用时,可以通过调用timeSetEvent()函数,将需要周期性执行的任务定义在LpTimeProc回调函数中(如:定时采样、控制等),从而完成所需处理的事件。需要注意的是,任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后,应及时调用timeKillEvent()将之释放。   

方式七:对于精确度要求更高的定时操作,则应该使用QueryPerformanceFrequency()和QueryPerformanceCounter()函数。这两个函数是VC提供的仅供Windows95及其后续版本使用的精确时间函数,并要求计算机从硬件上支持精确定时器。如示例工程中的Timer7、Timer7_1、Timer7_2、Timer7_3。   
QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型如下:   
BOOLQueryPerformanceFrequency(LARGE_INTEGER*lpFrequency);    BOOLQueryPerformanceCounter(LARGE_INTEGER*lpCount);   
数据类型ARGE_INTEGER既可以是一个8字节长的整型数,也可以是两个4字节长的整型数的联合结构,其具体用法根据编译器是否支持64位而定。该类型的定义如下:

typedefunion_LARGE_INTEGER
{
    struct
    {   
        DWORDLowPart;         //4字节整型数
        LONGHighPart;         //4字节整型数
    };
    LONGLONGQuadPart;         //8字节整型数
}LARGE_INTEGER;

在进行定时之前,先调用QueryPerformanceFrequency()函数获得机器内部定时器的时钟频率,然后在需要严格定时的事件发生之前和发生之后分别调用QueryPerformanceCounter()函数,利用两次获得的计数之差及时钟频率,计算出事件经历的精确时间。
下列代码实现1ms的精确定时:   

LARGE_INTEGERlitmp;
LONGLONGQPart1,QPart2;
doubledfMinus,dfFreq,dfTim;
QueryPerformanceFrequency(&litmp);
dfFreq=(double)litmp.QuadPart;      //获得计数器的时钟频率
QueryPerformanceCounter(&litmp);
QPart1=litmp.QuadPart;         //获得初始值
do   
{ 
    QueryPerformanceCounter(&litmp);
    QPart2=litmp.QuadPart;      //获得中止值
    dfMinus=(double)(QPart2-QPart1);
    dfTim=dfMinus/dfFreq;      //获得对应的时间值,单位为秒   
}while(dfTim)   

其定时误差不超过1微秒,精度与CPU等机器配置有关。
下面的程序用来测试函数Sleep(100)的精确持续时间:

LARGE_INTEGERlitmp;
LONGLONGQPart1,QPart2;
doubledfMinus,dfFreq,dfTim;
QueryPerformanceFrequency(&litmp);
dfFreq=(double)litmp.QuadPart;         //获得计数器的时钟频率
QueryPerformanceCounter(&litmp);
QPart1=litmp.QuadPart;      //获得初始值   
Sleep(100);
QueryPerformanceCounter(&litmp);
QPart2=litmp.QuadPart;      //获得中止值   
dfMinus=(double)(QPart2-QPart1);
dfTim=dfMinus/dfFreq;      //获得对应的时间值,单位为秒 

由于Sleep()函数自身的误差,上述程序每次执行的结果都会有微小误差。
下列代码实现1微秒的精确定时:

LARGE_INTEGERlitmp;
LONGLONGQPart1,QPart2;
doubledfMinus,dfFreq,dfTim;
QueryPerformanceFrequency(&litmp);
dfFreq=(double)litmp.QuadPart;         //获得计数器的时钟频率
QueryPerformanceCounter(&litmp);
QPart1=litmp.QuadPart;         //获得初始值
do
{
    QueryPerformanceCounter(&litmp);
    QPart2=litmp.QuadPart;      //获得中止值
    dfMinus=(double)(QPart2-QPart1);
    dfTim=dfMinus/dfFreq;      //获得对应的时间值,单位为秒   
}while(dfTim)

其定时误差一般不超过0.5微秒,精度与CPU等机器配置有关。

Linux服务器下配置exif功能

DZ程序中支持图片附件显示 EXIF
信息,但是我们的服务器一般都不支持此功能,现在就简单介绍一下如果在LINUX服务器上配置exif功能,最后提供一个exif压缩包的下载地址(如果你服务器上没有这个文件的话可以下载使用)

先找一下LINUX服务器上php的安装目录,例如为:/root/lnmp0.4-full/php-5.2.10/

则执行:

cd /root/lnmp0.4-full/php-5.2.10/ext/

我们要安装exif模块,执行

cd exif/

再执行

/usr/local/php/bin/phpize 会返回如下信息:

Configuring for:

PHP Api Version: 20041225

Zend Module Api No: 20060613

Zend Extension Api No: 220060519

再执行以下命令:

[root@vpser imap]# ./configure 
-with-php-config=/usr/local/php/bin/php-config

[root@vpser imap]# make && make install

执行完返回:

Build complete.

Don't forget to run 'make test'.

Installing shared extensions: 
/usr/local/php/lib/php/extensions/no-debug-non-zts-20060613/

表示已经成功,再修改

/usr/local/php/etc/php.ini

查找:extension = 再最后一个extension= 后面添加上extension = "exif.so"

保存,执行/root/lnmp restart 重启。

C++ 发送邮件(C++源码)

 

 

 

效果

001

这里是类的代码 CSendMail .h

*************************************************************************/  
#pragma once  
struct sMailInfo //邮件信息  
{  
    char*   m_pcUserName;//用户登录邮箱的名称  
    char*   m_pcUserPassWord;//用户登录邮箱的密码  
    char*   m_pcSenderName;//用户发送时显示的名称  
    char*   m_pcSender;//发送者的邮箱地址  
    char*   m_pcReceiver;//接收者的邮箱地址  
    char*   m_pcTitle;//邮箱标题  
    char*   m_pcBody;//邮件文本正文  
    char*   m_pcIPAddr;//服务器的IP  
    char*   m_pcIPName;//服务器的名称(IP与名称二选一,优先取名称)  
    sMailInfo(){memset(this,0,sizeof(sMailInfo));}  
};  
class CSendMail  
{  
public:  
    CSendMail(void);  
    ~CSendMail(void);  
  
public:  
    bool SendMail(sMailInfo &smailInfo);//发送邮件,需要在发送的时候初始化邮件信息  
    void AddFilePath(char * pcFilePath);//添加附件的决定路径到附件列表中  
    void DeleteFilePath(char* pcFilePath);//删除附件路径,如果有的话  
    void DeleteAllPath(void);//删除全部附件的路径  
  
protected:  
    void GetFileName(char* fileName,char* filePath);//从附件的路径中获取文件名称  
    void Char2Base64(char* pBuff64,char* pSrcBuff,int iLen);//把char类型转换成Base64类型  
    bool  CReateSocket(SOCKET &sock);//建立socket连接  
    bool Logon(SOCKET &sock);//登录邮箱,主要进行发邮件前的准备工作  
    int GetFileData(char* FilePath);//由文件路径获取附件内容  
  
    bool SendHead(SOCKET &sock);//发送邮件头  
    bool SendTextBody(SOCKET &sock);//发送邮件文本正文  
    bool SendFileBody(SOCKET &sock);//发送邮件附件  
    bool SendEnd(SOCKET &sock);//发送邮件结尾  
protected:  
    CList<char*,char*> m_pcFilePathList;//记录附件路径  
  
    char  m_cSendBuff[4096];//发送缓冲区  
    char  m_cReceiveBuff[1024];  
    char* m_pcFileBuff;//指向附件内容  
    sMailInfo m_sMailInfo;  
};  

CSendMail.cpp

/************************************************************************ 
* 发送邮件模块 
*可以发送文本和附件(支持多个附件一起发送) 
*Date:2011-12-01 
************************************************************************/  
#include "StdAfx.h"  
#include "CSendMail.h"  
#include "winsock2.h"  
#pragma comment(lib,"WSOCK32")  
#pragma comment(lib, "ws2_32")

CSendMail::CSendMail(void)  
{  
    m_pcFileBuff=NULL;  
    memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
    memset(m_cReceiveBuff,0,sizeof(m_cReceiveBuff));  
}  
  
  
CSendMail::~CSendMail(void)  
{  
    DeleteAllPath();  
}  
  
  
void CSendMail::Char2Base64(char* pBuff64,char* pSrcBuff,int iLen)  
{  
    //1   1   1   1   1   1   1   1  
    // 分配给pBuff64  ↑ 分配给pBuff64+1  
    //         point所在的位置  
    static char Base64Encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";//base64所映射的字符表  
    int point;//每一个源字符拆分的位置,可取2,4,6;初始为2  
    point=2;  
    int i;  
    int iIndex;//base64字符的索引  
    char n=0;//上一个源字符的残留值  
    for(i=0;i<iLen;i++)  
    {  
        if(point==2)  
        {  
            iIndex=((*pSrcBuff)>>point)&0x3f;//取得pSrcBuff的高point位  
        }  
        else if (point==4)  
        {  
            iIndex=((*pSrcBuff)>>point)&0xf;//取得pSrcBuff的高point位  
        }  
        else if(point==6)  
        {  
            iIndex=((*pSrcBuff)>>point)&0x3;//取得pSrcBuff的高point位  
        }  
        iIndex+=n;//与pSrcBuff-1的低point结合组成Base64的索引  
        *pBuff64++=Base64Encode[iIndex];//由索引表得到pBuff64  
        n=((*pSrcBuff)<<(6-point));//计算源字符中的残留值  
        n=n&0x3f;//确保n的最高两位为0  
        point+=2;//源字符的拆分位置上升2  
        if(point==8)//如果拆分位置为8说明pSrcBuff有6位残留,可以组成一个完整的Base64字符,所以直接再组合一次  
        {  
            iIndex=(*pSrcBuff)&0x3f;//提取低6位,这就是索引了  
            *pBuff64++=Base64Encode[iIndex];//  
            n=0;//残留值为0  
            point=2;//拆分位置设为2  
        }  
        pSrcBuff++;  
  
    }  
    if(n!=0)  
    {  
        *pBuff64++=Base64Encode[n];  
    }  
    if(iLen%3==2)//如果源字符串长度不是3的倍数要用'='补全  
    {  
        *pBuff64='=';  
    }  
    else if(iLen%3==1)  
    {  
        *pBuff64++='=';  
        *pBuff64='=';  
    }  
}  
  
void CSendMail::AddFilePath(char * pcFilePath)//添加附件路径  
{  
    if(pcFilePath==NULL)  
    {  
        return;  
    }  
    int i;  
    char* temp;  
    for(i=0;i<m_pcFilePathList.GetCount();i++)  
    {  
        temp=m_pcFilePathList.GetAt(m_pcFilePathList.FindIndex(i));  
        if(strcmp(pcFilePath,temp)==0)//如果已经存在就不用再添加了  
        {  
            return;  
        }  
    }  
    m_pcFilePathList.AddTail(pcFilePath);  
}  
  
void CSendMail::DeleteFilePath(char* pcFilePath)//删除附件路径  
{  
    int i;  
    char* temp;  
    for(i=0;i<m_pcFilePathList.GetCount();i++)  
    {  
        temp=m_pcFilePathList.GetAt(m_pcFilePathList.FindIndex(i));  
        if(strcmp(temp,pcFilePath)==0)//找到并删除它,如果没找到就算了  
        {  
            m_pcFilePathList.RemoveAt(m_pcFilePathList.FindIndex(i));  
            delete[] temp;  
            return;  
        }  
    }  
}  
  
  
void CSendMail::DeleteAllPath(void)  
{  
    m_pcFilePathList.RemoveAll();  
}  
  
int CSendMail::GetFileData(char* FilePath)  
{  
    m_pcFileBuff=NULL;  
    if(FilePath==NULL)  
    {  
        return 0;  
    }  
    CFile f;  
    int len;  
      
      
    USES_CONVERSION;  
    if(!f.Open(A2W(FilePath),CFile::modeRead|CFile::modeNoTruncate|CFile::typeBinary))  
    {  
        return 0;  
    }  
    len=(int)f.GetLength();  
    m_pcFileBuff=new char[len+1];  
    memset(m_pcFileBuff,0,len+1);  
    f.Read(m_pcFileBuff,len);  
  
    f.Close();  
    return len;  
}  
  
void CSendMail::GetFileName(char* fileName,char* filePath)  
{  
    if(filePath==NULL || fileName==NULL)  
    {  
        return;  
    }  
    int i;  
    for(i=0;i<(int)strlen(filePath);i++)  
    {  
        if(filePath[strlen(filePath)-1-i]=='\\')  
        {  
            memcpy(fileName,&filePath[strlen(filePath)-i],i);  
            return;  
        }  
    }  
}  
  
bool CSendMail::CReateSocket(SOCKET &sock)  
{  
    WORD wVersionRequested;  
    WSADATA wsaData;  
    int err;  
    wVersionRequested = MAKEWORD( 2, 2 );  
    err = WSAStartup( wVersionRequested, &wsaData );  
    if ( err != 0 )   
    {  
        return false;  
    }  
    if ( LOBYTE( wsaData.wVersion ) != 2 ||  
        HIBYTE( wsaData.wVersion ) != 2 )  
    {  
        WSACleanup( );  
        return false;   
    }  
    sock = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);  
    if (sock == INVALID_SOCKET)  
    {  
        return false;  
    }  
  
    sockaddr_in servaddr;  
    memset(&servaddr,0,sizeof(sockaddr_in));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_port = htons(25);//发邮件一般都是25端口  
    if(m_sMailInfo.m_pcIPName=="")  
    {  
        servaddr.sin_addr.s_addr = inet_addr(m_sMailInfo.m_pcIPAddr);//直接使用IP地址  
    }  
    else  
    {  
        struct hostent *hp=gethostbyname(m_sMailInfo.m_pcIPName);//使用名称  
		if (hp == NULL)
		{
			DWORD dwErrCode =	GetLastError();
			return false;
		}
        servaddr.sin_addr.s_addr=*(int*)(*hp->h_addr_list);  
    }  
  
  
    int ret = connect(sock,(sockaddr*)&servaddr,sizeof(servaddr));//建立连接  
    if (ret == SOCKET_ERROR)  
    {  
		DWORD dwErr = GetLastError();
        return false;  
    }  
  
    return true;  
}  
  
  
bool CSendMail::Logon(SOCKET &sock)  
{  
    recv(sock,m_cReceiveBuff,1024,0);  
  
    memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
    sprintf_s(m_cSendBuff,"HELO []\r\n");  
    send(sock,m_cSendBuff,strlen(m_cSendBuff),0);//开始会话  
    recv(sock,m_cReceiveBuff,1024,0);  
    if(m_cReceiveBuff[0]!='2' || m_cReceiveBuff[1]!='5' || m_cReceiveBuff[2]!='0')  
    {  
        return false;  
    }  
  
    memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
    sprintf_s(m_cSendBuff,"AUTH LOGIN\r\n");  
    send(sock,m_cSendBuff,strlen(m_cSendBuff),0);//请求登录  
    recv(sock,m_cReceiveBuff,1024,0);  
    if(m_cReceiveBuff[0]!='3' || m_cReceiveBuff[1]!='3' || m_cReceiveBuff[2]!='4')  
    {  
        return false;  
    }  
  
    memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
    Char2Base64(m_cSendBuff,m_sMailInfo.m_pcUserName,strlen(m_sMailInfo.m_pcUserName));  
    m_cSendBuff[strlen(m_cSendBuff)]='\r';  
    m_cSendBuff[strlen(m_cSendBuff)]='\n';  
    send(sock,m_cSendBuff,strlen(m_cSendBuff),0);//发送用户名  
    recv(sock,m_cReceiveBuff,1024,0);  
    if(m_cReceiveBuff[0]!='3' || m_cReceiveBuff[1]!='3' || m_cReceiveBuff[2]!='4')  
    {  
        return false;  
    }  
  
    memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
    Char2Base64(m_cSendBuff,m_sMailInfo.m_pcUserPassWord,strlen(m_sMailInfo.m_pcUserPassWord));  
    m_cSendBuff[strlen(m_cSendBuff)]='\r';  
    m_cSendBuff[strlen(m_cSendBuff)]='\n';  
    send(sock,m_cSendBuff,strlen(m_cSendBuff),0);//发送用户密码  
    recv(sock,m_cReceiveBuff,1024,0);  
    if(m_cReceiveBuff[0]!='2' || m_cReceiveBuff[1]!='3' || m_cReceiveBuff[2]!='5')  
    {  
        return false;  
    }  
    return true;//登录成功  
}  
  
  
bool CSendMail::SendHead(SOCKET &sock)  
{  
    int rt;  
    memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
    sprintf_s(m_cSendBuff,"MAIL FROM:<%s>\r\n",m_sMailInfo.m_pcSender);  
    rt=send(sock,m_cSendBuff,strlen(m_cSendBuff),0);  
      
    if(rt!=strlen(m_cSendBuff))  
    {  
        return false;  
    }  
    recv(sock,m_cReceiveBuff,1024,0);  
  
    memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
    sprintf_s(m_cSendBuff,"RCPT TO:<%s>\r\n",m_sMailInfo.m_pcReceiver);  
    rt=send(sock,m_cSendBuff,strlen(m_cSendBuff),0);  
    if(rt!=strlen(m_cSendBuff))  
    {  
        return false;  
    }  
    recv(sock,m_cReceiveBuff,1024,0);  
  
    memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
    memcpy(m_cSendBuff,"DATA\r\n",strlen("DATA\r\n"));  
    rt=send(sock,m_cSendBuff,strlen(m_cSendBuff),0);  
    if(rt!=strlen(m_cSendBuff))  
    {  
        return false;  
    }  
    recv(sock,m_cReceiveBuff,1024,0);  
  
    memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
    sprintf_s(m_cSendBuff,"From:\"%s\"<%s>\r\n",m_sMailInfo.m_pcSenderName,m_sMailInfo.m_pcSender);  
    sprintf_s(&m_cSendBuff[strlen(m_cSendBuff)],150,"To:\"INVT.COM.CN\"<%s>\r\n",m_sMailInfo.m_pcReceiver);  
    sprintf_s(&m_cSendBuff[strlen(m_cSendBuff)],150,"Subject:%s\r\nMime-Version: 1.0\r\nContent-Type: multipart/mixed;   boundary=\"INVT\"\r\n\r\n",m_sMailInfo.m_pcTitle);  
    rt=send(sock,m_cSendBuff,strlen(m_cSendBuff),0);  
    if(rt!=strlen(m_cSendBuff))  
    {  
        return false;  
    }  
  
    return true;  
}  
  
bool CSendMail::SendTextBody(SOCKET &sock)  
{  
    int rt;  
    memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
    sprintf_s(m_cSendBuff,"--INVT\r\nContent-Type: text/plain;\r\n  charset=\"gb2312\"\r\n\r\n%s\r\n\r\n",m_sMailInfo.m_pcBody);  
    rt=send(sock,m_cSendBuff,strlen(m_cSendBuff),0);  
    if(rt!=strlen(m_cSendBuff))  
    {  
        return false;  
    }  
    else  
    {  
        return true;  
    }  
}  
  
bool CSendMail::SendFileBody(SOCKET &sock)  
{  
    int i;  
    char* filePath;  
    int rt;  
    int len;  
    int pt=0;  
    char fileName[128];  
    for(i=0;i<m_pcFilePathList.GetCount();i++)  
    {  
        pt=0;  
        memset(fileName,0,128);  
        filePath=m_pcFilePathList.GetAt(m_pcFilePathList.FindIndex(i));  
        len=GetFileData(filePath);  
        GetFileName(fileName,filePath);  
  
        sprintf_s(m_cSendBuff,"--INVT\r\nContent-Type: application/octet-stream;\r\n  name=\"%s\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n  filename=\"%s\"\r\n\r\n",fileName,fileName);  
        send(sock,m_cSendBuff,strlen(m_cSendBuff),0);  
        while (pt<len)  
        {  
            memset(m_cSendBuff,0,sizeof(m_cSendBuff));  
            Char2Base64(m_cSendBuff,&m_pcFileBuff[pt],min(len-pt,3000));  
            m_cSendBuff[strlen(m_cSendBuff)]='\r';  
            m_cSendBuff[strlen(m_cSendBuff)]='\n';  
            rt=send(sock,m_cSendBuff,strlen(m_cSendBuff),0);  
            pt+=min(len-pt,3000);  
            if(rt!=strlen(m_cSendBuff))  
            {  
                return false;  
            }  
        }  
        if(len!=0)  
        {  
            delete [] m_pcFileBuff;  
        }  
    }  
  
    return true;  
}  
  
bool CSendMail::SendEnd(SOCKET &sock)  
{  
    sprintf_s(m_cSendBuff,"--INVT--\r\n.\r\n");  
    send(sock,m_cSendBuff,strlen(m_cSendBuff),0);  
  
    sprintf_s(m_cSendBuff,"QUIT\r\n");  
    send(sock,m_cSendBuff,strlen(m_cSendBuff),0);  
    closesocket(sock);  
    WSACleanup();  
    return true;  
}  
  
  
bool CSendMail::SendMail(sMailInfo &smailInfo)  
{  
    memcpy(&m_sMailInfo,&smailInfo,sizeof(smailInfo));  
    if(m_sMailInfo.m_pcBody==NULL  
        || m_sMailInfo.m_pcIPAddr==NULL  
        || m_sMailInfo.m_pcIPName==NULL  
        || m_sMailInfo.m_pcReceiver==NULL  
        || m_sMailInfo.m_pcSender==NULL  
        || m_sMailInfo.m_pcSenderName==NULL  
        || m_sMailInfo.m_pcTitle==NULL  
        || m_sMailInfo.m_pcUserName==NULL  
        || m_sMailInfo.m_pcUserPassWord==NULL)  
    {  
        return false;  
    }  
    SOCKET sock;  
    if(!CReateSocket(sock))//建立连接  
    {  
        return false;  
    }  
  
    if(!Logon(sock))//登录邮箱  
    {  
        return false;  
    }  
  
    if(!SendHead(sock))//发送邮件头  
    {  
        return false;  
    }  
  
    if(!SendTextBody(sock))//发送邮件文本部分  
    {  
        return false;  
    }  
  
    if(!SendFileBody(sock))//发送附件  
    {  
        return false;  
    }  
  
    if(!SendEnd(sock))//结束邮件,并关闭sock  
    {  
        return false;  
    }  
  
    return true;  
}  

测试代码

#include "CSendMail/CSendMail.h"
void testMail2()
{
	sMailInfo sm;
	sm.m_pcUserName = "这里是QQ号码";//"";
	sm.m_pcUserPassWord ="这里是QQ密码";// "";
	sm.m_pcSenderName = "爱说话的汤姆猫";
	sm.m_pcSender = "snifferzhu@qq.com";//发送者的邮箱地址  
	sm.m_pcReceiver = "w_izard@qq.com";//接收者的邮箱地址  
	sm.m_pcTitle = "测试垃圾邮件";//邮箱标题  
	sm.m_pcBody = "你想快速发家致富吗?你想一夜成名吗?访问 http://www.sb.com";//邮件文本正文  
	sm.m_pcIPAddr = "" ;//服务器的IP可以留空 
	sm.m_pcIPName ="smtp.qq.com" ;//服务器的名称(IP与名称二选一,优先取名称)  

	CSendMail csm;
	while(TRUE)
	{
		csm.SendMail(sm);
		Sleep(1000 * 20);
	}
	
}

代码说明:
SMailer 是网上流传最广泛的一个C++发送邮件的类。但是我在测试的时候并没有发送成功。没有找出原因在哪里。
CSendMail 是在CSDN上找到的一个发送邮件的类。经测试可用。
我写了一个测试的工程
TestEMail

对virtualbox虚拟机进行网络配置,实现内网外网互通!桥接!

实验环境如下:
virtualbox上运行的虚拟机:windows 2008 en
实体机IP:192.168.18.99 内网段:192.168.18.X
实体机:Red Hat Enterprise Linux Server release 5 (Tikanga)

virtualbox主要有下面几种方式(不同版本称法不一样,但实质是一样的):
1、Intelnal Network:利用主机上的所有的虚拟机构建一个虚拟网络
2、NAT:能访问互联网,不能访问主机(或局域网)
3、Host Interface:能访问主机(或局域网),不能访问互联网

本教程运用NAT+interface的方法。
首先打开virtualbox>设置>网络,启用网络连接,并按如下配置。

001

这里点确定,并运行虚拟机,本机系统会自动创建一个名为vboxnet0的虚拟网卡

002

IP为:192.168.56.1
进入虚拟机系统(本人安装的是winvows 2008 en),将网卡的IP设置为vboxnet0的虚拟网卡同网段IP,ouyaoxiazai.com,网关为vboxnet0的IP,192.168.56.1

003

这里你会发现可以ping 192.168.56.1 但无法连接到局域网。
这时回到实体机,
系统>管理>网络,打开网络配置>设备,双击当前设备,设置路由如下:

004

然后保存退出,打开终端,重启下网卡:
/etc/rc.d/init.d/network restart
这时你就可以正常访问内网了,但无法访问互联网,就是不能上网。
接下来进行NAT,很简单,先关闭虚拟机系统。
和上面一样,设置,网络。
选择网络连接2,启用NAT,如图:

005

然后启动系统,你会发现多了一块网卡,自动获取IP如下:

006

现在应该可以正常上网了,并能访问内网了。。。。

007

Linux下socket异步通讯聊天程序

Linux下socket异步通讯聊天程序(转)

网络课的project 1能用到的资料,程序结构比较清晰,转来学习一下

什么是异步通讯?
就是通讯任意一方可以任意发送消息,有消息来到时会收到系统提示去接收消息。

这里要用到select函数。使用步骤如下:
1、设置一个集合变量,用来存放所有要判断的句柄(file descriptors:即我们建立的每个socket、用open打开的每个文件等)
2、把需要判断的句柄加入到集合里
3、设置判断时间
4、开始等待,即select
5、如果在设定的时间内有任何句柄状态变化了就马上返回,并把句柄设置到集合里

服务器端源代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#define MAXBUF 1024
/************关于本文档********************************************
*filename: async-server.c
*purpose: 演示网络异步通讯,这是服务器端程序
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 21:22
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
	int sockfd, new_fd;
	socklen_t len;
	struct sockaddr_in my_addr, their_addr;
	unsigned int myport, lisnum;
	char buf[MAXBUF + 1];
	fd_set rfds;
	struct timeval tv;
	int retval, maxfd = -1;
	
	if (argv[1])
		myport = atoi(argv[1]);
	else
		myport = 7838;
		
	if (argv[2])
		lisnum = atoi(argv[2]);
	else
		lisnum = 2;
		
	if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) 
	{
		perror("socket");
		exit(1);
	}
	
	bzero(&my_addr, sizeof(my_addr));
	my_addr.sin_family = PF_INET;
	my_addr.sin_port = htons(myport);
	
	if (argv[3])
		my_addr.sin_addr.s_addr = inet_addr(argv[3]);
	else
		my_addr.sin_addr.s_addr = INADDR_ANY;
		
	if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))== -1) 
	{
		perror("bind");
		exit(1);
	}
	
	if (listen(sockfd, lisnum) == -1) 
	{
		perror("listen");
		exit(1);
	}
	
	while (1) 
	{
		printf("\n----等待新的连接到来开始新一轮聊天……\n");
		len = sizeof(struct sockaddr);
		
		if ((new_fd =accept(sockfd, (struct sockaddr *) &their_addr,&len)) == -1) 
		{
			perror("accept");
			exit(errno);
		} 
		else
			printf("server: got connection from %s, port %d, socket %d\n", inet_ntoa(their_addr.sin_addr),ntohs(their_addr.sin_port), new_fd);
			
		/* 开始处理每个新连接上的数据收发 */
		printf("\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n");
		while (1) 
		{
			/* 把集合清空 */
			FD_ZERO(&rfds);
			
			/* 把标准输入句柄0加入到集合中 */
			FD_SET(0, &rfds);
			maxfd = 0;
			
			/* 把当前连接句柄new_fd加入到集合中 */
			FD_SET(new_fd, &rfds);
			if (new_fd > maxfd)
				maxfd = new_fd;
				
			/* 设置最大等待时间 */
			tv.tv_sec = 1;
			tv.tv_usec = 0;
			
			/* 开始等待 */
			retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
			if (retval == -1) 
			{
				printf("将退出,select出错! %s", strerror(errno));
				break;
			} 
			else if (retval == 0) 
			{
				/* printf("没有任何消息到来,用户也没有按键,继续等待……\n"); */
				continue;
			} 
			else 
			{
				if (FD_ISSET(0, &rfds)) 
				{
					/* 用户按键了,则读取用户输入的内容发送出去 */
					bzero(buf, MAXBUF + 1);
					fgets(buf, MAXBUF, stdin);
					if (!strncasecmp(buf, "quit", 4)) 
					{
						printf("自己请求终止聊天!\n");
						break;
					}
					
					len = send(new_fd, buf, strlen(buf) - 1, 0);
					if (len > 0)
						printf("消息:%s\t发送成功,共发送了%d个字节!\n", buf, len);
					else 
					{
						printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",buf, errno, strerror(errno));
						break;
					}
				}
				if (FD_ISSET(new_fd, &rfds)) 
				{
					/* 当前连接的socket上有消息到来则接收对方发过来的消息并显示 */
					bzero(buf, MAXBUF + 1);
					
					/* 接收客户端的消息 */
					len = recv(new_fd, buf, MAXBUF, 0);
					
					if (len > 0)
						printf("接收消息成功:'%s',共%d个字节的数据\n",buf, len);
					else 
					{
						if (len < 0)
							printf("消息接收失败!错误代码是%d,错误信息是'%s'\n",errno, strerror(errno));
						else
							printf("对方退出了,聊天终止\n");
						break;
					}
				}
			}
		}
		
		close(new_fd);
		
		/* 处理每个新连接上的数据收发结束 */
		printf("还要和其它连接聊天吗?(no->退出)");
		fflush(stdout);
		bzero(buf, MAXBUF + 1);
		fgets(buf, MAXBUF, stdin);
		if (!strncasecmp(buf, "no", 2)) 
		{
			printf("终止聊天!\n");
			break;
		}
	}
	
	close(sockfd);
	return 0;
}

客户端源代码如下:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#define MAXBUF 1024
/************关于本文档********************************************
// *filename: ssync-client.c
*purpose: 演示网络异步通讯,这是客户端程序
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 21:32
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
	int sockfd, len;
	struct sockaddr_in dest;
	char buffer[MAXBUF + 1];
	fd_set rfds;
	struct timeval tv;
	int retval, maxfd = -1;
	if (argc != 3) 
	{
		printf("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",argv[0], argv[0]);
		exit(0);
	}
	
	/* 创建一个 socket 用于 tcp 通信 */
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
	{
		perror("Socket");
		exit(errno);
	}
	
	/* 初始化服务器端(对方)的地址和端口信息 */
	bzero(&dest, sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port = htons(atoi(argv[2]));
	if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) 
	{
		perror(argv[1]);
		exit(errno);
	}
	
	/* 连接服务器 */
	if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) 
	{
		perror("Connect ");
		exit(errno);
	}
	
	printf("\n准备就绪,可以开始聊天了……直接输入消息回车即可发信息给对方\n");
	while (1) 
	{
		/* 把集合清空 */
		FD_ZERO(&rfds);
		
		/* 把标准输入句柄0加入到集合中 */
		FD_SET(0, &rfds);
		maxfd = 0;
		
		/* 把当前连接句柄sockfd加入到集合中 */
		FD_SET(sockfd, &rfds);
		if (sockfd > maxfd)
			maxfd = sockfd;
		
		/* 设置最大等待时间 */
		tv.tv_sec = 1;
		tv.tv_usec = 0;
		
		/* 开始等待 */
		retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
		if (retval == -1) 
		{
			printf("将退出,select出错! %s", strerror(errno));
			break;
		} else if (retval == 0) 
		{
			/* printf("没有任何消息到来,用户也没有按键,继续等待……\n"); */
			continue;
		} 
		else 
		{
			if (FD_ISSET(sockfd, &rfds)) 
			{
				/* 连接的socket上有消息到来则接收对方发过来的消息并显示 */
				bzero(buffer, MAXBUF + 1);
				
				/* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
				len = recv(sockfd, buffer, MAXBUF, 0);
				if (len > 0)
					printf("接收消息成功:'%s',共%d个字节的数据\n",	buffer, len);
				else 
				{
					if (len < 0)
						printf("消息接收失败!错误代码是%d,错误信息是'%s'\n",errno, strerror(errno));
					else
						printf("对方退出了,聊天终止!\n");
					break;
				}
			}
			
			if (FD_ISSET(0, &rfds)) 
			{
				/* 用户按键了,则读取用户输入的内容发送出去 */
				bzero(buffer, MAXBUF + 1);
				fgets(buffer, MAXBUF, stdin);
				if (!strncasecmp(buffer, "quit", 4)) 
				{
					printf("自己请求终止聊天!\n");
					break;
				}
				
				/* 发消息给服务器 */
				len = send(sockfd, buffer, strlen(buffer) - 1, 0);
				if (len < 0) 
				{
					printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",buffer, errno, strerror(errno));
					break;
				} 
				else
					printf("消息:%s\t发送成功,共发送了%d个字节!\n",buffer, len);
			}
		}
	}
	
	/* 关闭连接 */
	close(sockfd);
	return 0;
}

linux与windows间socket通信实例

linux端:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#define MYPORT 3333
#define BACKLOG 10
int main()
{
 int sockfd, new_fd;
 struct sockaddr_in my_addr;
 struct sockaddr_in their_addr;
 socklen_t sin_size;
 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    perror("socket");
    exit(1);
 }
 my_addr.sin_family = AF_INET;
 my_addr.sin_port = htons(MYPORT);
 my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 bzero(&(my_addr.sin_zero),0);
 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1) {
    perror("bind");
    exit(1);
 }
 if (listen(sockfd, BACKLOG) == -1) {
    perror("listen");
    exit(1);
 }
 while(1) {
 sin_size = sizeof(struct sockaddr_in);
 if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr,&sin_size)) == -1) {
    perror("accept");
    continue;
 }
 printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));
 if (!fork()) {
 if (send(new_fd, "Hello, world!\n", 14, 0) == -1)
 perror("send");
 close(new_fd);
 exit(0);
 }
 close(new_fd);
 while(waitpid(-1,NULL,WNOHANG) > 0);
 }
}

window端:

// client.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include<Winsock2.h>
//#include<Winsock.h>
//#include <windows.h> 
#include<stdio.h>
#pragma comment (lib,"WS2_32.lib")

#include<stdlib.h>
//using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	int i;
	char recvBuffer[255] = {0};
	//WORD wVersionRequested; //typedef unsigned short WORD; 2字节
	WSADATA wsaData;
	//WSADATA 包含了Windows Socket执行的信息。
	int err;
	//wsaData.wVersion =MAKEWORD(1,1);
	//这个宏创建一个被指定变量连接而成的WORD变量。返回一个WORD变量。
	//第一个是socket库版本,第二个是取得的版本号。
	//err=WSAStartup(wVersionRequested,&wsaData); //return 0 if successful
	err=WSAStartup(MAKEWORD(1,1),&wsaData); //return 0 if successful
	if(err!=0){
		printf("Call WSAStart ERROR!");
		exit(1);
	}   //终止对WinSock库的使用
	//if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wHighVersion)!=1)
	//{ 
	//  WSACleanup();
	//  exit(0);
	//}
	//typedef unsigined int   SOCKET;//创建用与监听的套接字
	SOCKET SocketClient=socket(AF_INET,SOCK_STREAM,0);       //0表示让系统自己选择协议
	//定义地址结构体//填入服务器端的ip地址和端口号 
	SOCKADDR_IN addrSrv; 
	//转换为TCP/IP network byte order //32bit  
	addrSrv.sin_addr.S_un.S_addr=inet_addr("172.17.51.81"); //ip 172.17.51.81
	addrSrv.sin_family=AF_INET;                              //family address
	addrSrv.sin_port=htons(3333);                            //16bit端口号
	printf("Connect to server...\n");
	i=connect(SocketClient,(sockaddr *)&addrSrv,sizeof(SOCKADDR_IN)); //指向要建立连接的数据结构  
	if(i<0){
		printf("%i\n",WSAGetLastError());
		printf("连接到172.17.51.81:3333错误!");
		exit(1);
	}
	recv(SocketClient,recvBuffer,255,0);
	printf("%s\n",recvBuffer);   //send(socketClient,recvBuffer,20,0);
	closesocket(SocketClient);
	WSACleanup();
	return 0;
}

C语言SOCKET网络编程 根据主机名和域名获取IP地址

在C语言SOCKET编程中,往往需要通过 主机名/域名 来求得IP地址。比例说,我们想连接上百度服务器,但是不可能人工先去查它的IP,然后在写进代码里。万一IP变了呢,那代码不是要重写?所以,这一章,主要介绍如何通过主机名/域名来求服务器IP地址。
首先认识一个表示主机信息的结构体:


struct hostent{
    char* h_name;    //主机的正式名称
    char** h_aliases:    //主机的备用名称表
    int h_addrtype;    //地址类型, 比如"AF_INET"
    int h_length;    //地址长度,大家目前使用的都是32位
    char** h_addr_list;    //主机地址表
#define h_addr h_addr_list[0];    //主机地址表中的首个地址
}

h_addrtype 这是表示主机所使用的地址类型的定义,在C语言winsock2.h头文件中以“AF_”开头的宏定义就是它的值。如果使用的是IPV4,则值为”AF_INET”,如果是IPV6,值为“AF_INET4”。
h_length 地址的长度,一般都用IPV4中,所以都是32位/4字节。
h_addr_list 在一个主机名中,往往有很多对应的IP地址。当我们通过使用主机名查IP时,就会把返回的所有IP地址保存在主机地址表中。我们一般使用h_addr来取得第一个地址就行了。
在C语言的SOCKET网络编程中,我们使用gethostbyname()函数来获取主机信息,它的函数原型如下:

struct hostent* gethostbyname(const char* hostname );

hostname是个包含主机名/域名的字符串,函数通过给定的主机名,返回一个包含主机名称和主机地址信息的指针结构体。
如果我们想要返回百度服务器的主机信息,则:


char baidu_hostname[]="www.baidu.com"
struct hostent* baidu_hostent;
baidu_hostent = gethostbyname( baidu_hostname );

取得IP地址后,使用下面方法把IP地址打印到标准输出上:

struct in_addr baidu_addr;
baidu_addr.s_addr = *(unsigned long * ) baidu_hostent->h_addr;
printf("baidu_IPV4 : %s " , inet_ntoa( baidu_addr ) );

in_addr 是一个用来表示32位IPV4网络地址的结构体,s_addr是个使用unsigned long数据格式来表示一个IPV4 地址的结构成员。
inet_ntoa() 将指定的网络地址转换成 以点隔字符串表示的IPV4地址,例如“192.168.1.101”就是个点隔字符串网络地址。


//函数原型
char* inet_ntoa( struct in_addr);

通过以上方法,以后就可以直接使用主机名/域名来获取IPV4地址,而再不用人工去查找。
下面是关于本期讲解内容的总结的源代码:

#include
#include
//通过改变以下宏定义来修改要解析的主机名
#define HOSTNAME "www.dingguofeng.com"

 

int main( int argc , char* argv[])
{
int ret;
struct hostent *host;
struct in_addr addr;
//启动socket服务
WSADATA wsaData;
ret = WSAStartup( 0x101 , &wsaData);
//如果启动成功 ,返回值为0
if( ret != 0 )
{
printf("WSAStartup error:%d " , WSAGetLastError() );
return 1;
}
//获取主机信息
host = gethostbyname( HOSTNAME );
if( host == NULL )
{
printf("gethostbyname error:%d" , WSAGetLastError() );
return 1;
}
addr.s_addr = *(unsigned long * )host->h_addr;
printf("主机名 :%sn" , host->h_name);
printf("主机地址: %sn " , inet_ntoa( addr ) );
WSACleanup();
return 0;
}

一个简单的UDP服务端和客户端程序

写来一个UDP发送和接受数据包的程序.

客户端代码:

client_main.c:
/*
* @file client_main.c
* @brief client of the UDP.
* @author xiaou
*/

#include // for sockaddr_in
#include // for socket
#include // for socket
#include // for close()
#include
#include
#include

int main(int argc, char *argv[])
{
if(argc < 2) { printf("One argument at least!\n"); return -1; } int res = 0; int sock_fd = socket(PF_INET, SOCK_DGRAM, 0); if(sock_fd < 0) { printf("error: Create Socket Failed!"); return -1; } struct sockaddr_in sock_addr; sock_addr.sin_family = AF_INET; sock_addr.sin_port = htons(4321); sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); memset(sock_addr.sin_zero, 0x00, 8); try { const char * tos = argv[1]; int toslen = strlen(tos) + 1; struct sockaddr_in to = sock_addr; uint32_t tolen = sizeof(struct sockaddr); printf("\nSending for data...\n"); if((toslen = sendto(sock_fd, tos, toslen, 0, (struct sockaddr *)&to, tolen)) == -1) { printf("error: Sending Failed!"); res = -1; throw res; } else { char s[256]; strncpy(s, tos, toslen); printf("Sended data:\n[%s]\n\n", s); } } catch(...) { printf("error catched~"); } close(sock_fd); return res; }

服务端代码:

server_main.c:

/*
* @file server_main.c
* @brief server of the UDP.
* @author xiaou
*/

#include // for sockaddr_in
#include // for socket
#include // for socket
#include // for close()
#include
#include
#include

int main(int argc, char *argv[])
{
int res = 0;
int sock_fd = socket(PF_INET, SOCK_DGRAM, 0);
if(sock_fd < 0) { printf("error: Create Socket Failed!"); return -1; } struct sockaddr_in sock_addr; sock_addr.sin_family = AF_INET; sock_addr.sin_port = htons(4321); sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); memset(sock_addr.sin_zero, 0x00, 8); try { if(bind(sock_fd, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr)) != 0) { printf("error: Bind Socket Failed!"); res = -1; throw res; } char recvs[256]; int recvslen; struct sockaddr_in from; uint32_t fromlen; while(1) { printf("\nWaiting for data...\n"); fromlen = sizeof(struct sockaddr); if((recvslen = recvfrom(sock_fd, recvs, 256, 0, (struct sockaddr *)&from , &fromlen)) == -1) { printf("error: Receiving Failed!"); res = -1; throw res; } else { if(recvslen < 256) *(recvs+recvslen) = '\0'; else *(recvs+255) = '\0'; printf("Received data:\n[%s]\n",recvs); } } } catch(...) { printf("error catched~"); } close(sock_fd); return res; }

Makefile代码:

Makefile:

CC=g++
CFLAGS=-Wall

OutPath4UDPClient=$(HOME)/bin/test4UDPClient
OutPath4UDPServer=$(HOME)/bin/test4UDPServer

all:
$(CC) $(CFLAGS) -o $(OutPath4UDPClient) client_main.c
$(CC) $(CFLAGS) -o $(OutPath4UDPServer) server_main.c

clean:
rm -rf $(OutPath4UDPClient)
rm -rf $(OutPath4UDPServer)