过年了。。。新年好。。

备份完2008年最后一个程序。。。。。来BLOG上写篇文章。。。

木马差不多已经做了一半了。。以后的功能再陆续实现吧。。实实在在的在家蹲了十天。。。也实实在在的地电脑旁做了十天。。虽然说没什么大的成就吧。。不过看着自己克服一个又一个困难。。还是蛮高兴的。。嘿嘿。。

2008再见!

2009你好!

常见的Windows系统目录简写环境变量

        所谓的Windows系统目录简写环境变量,就是为一些常用且固定的Windows系统目录的路径建立一个与之对应的相对简单的缩写,使之更容易输入或定位。


  Windows系统默认情况下都是安装在电脑C盘的Windows目录下,但这并不是固定的,如果你的系统不是安装在这个目录下,那么程序想要定位你的某个系统目录的话,就需要使用到目录简写环境变量了。使用这些环境变量,程序员或系统管理员无需事先了解你的系统安装位置,就能轻易的找到所要使用的系统目录路径。


  下面,我就来为大家介绍一些常见的Windows系统目录简写环境变量:


  %SYSTEMDRIVE%


  这代表的是Windows系统所在磁盘分区,也就是Windows系统所安装到的盘符根目录,通常就是C盘的根目录了。


  %HOMEDRIVE%


  这和上面介绍的%SYSTEMDRIVE%的功能是一样的。


  %SYSTEMROOT%


  它所指向的是Windows系统所在的目录,通常就是C:Windows。


  %WINDIR%


  和%SYSTEMROOT%的功能相同,指向Windows所在目录。


  %ProgramFiles%


  指向Program Files的路径,通常情况下是C:Program Files。


  %CommonProgramFiles%


  指向公用文件(Common Files)目录,通常是C:Program FilesCommon Files。


  %USERPROFILE%


  指向当前帐户的用户目录,通常是C:Documents and Settings当前用户名。


  %HOMEPATH%


  功能和上面的%USERPROFILE%是一样的。


  %ALLUSERSPROFILE%


  指向所有用户的用户目录,通常是C:Documents and SettingsAll Users。


  %APPDATA%


  指向当前用户的Application Data目录,通常是C:Documents and Settings当前用户名Application Data。


  很多文章都会介绍一个叫做%ALLAPPDATA%的变量,说它是指向C:Documents and SettingsAll UsersApplication Data的,但其实这个变量并不能使用。


  %TEMP%


  它指向的是当前用户的临时文件目录,通常是C:Documents and Settings当前用户名Local SettingsTemp。


  %TMP%


  与%TEMP%的指向相同。


  %ComSpec%


  指向C:WINDOWSSystem32cmd.exe,也就是命令提示符

MFC自定义消息四部曲

1、定义消息变量

2、添加消息映射

3、添加消息处理函数

4、实现消息处理函数


以Socket的Accept例:
1、定义消息变量

#define    WM_USER_ACCEPT    WM_USER+101

2、添加消息映射

BEGIN_MESSAGE_MAP(CSocketView, CDialog)
    //{{AFX_MSG_MAP(
CSocketView)

    ON_MESSAGE(WM_USER_ACCEPT,OnAccept)
    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

3、添加消息处理函数
//注意VS2005中的自定义消息需要有返回值,返回值的类型是:LRESULT。
//{{AFX_MSG(
CSocketView)

    afx_msg LRESULT OnAccept(WPARAM wParam, LPARAM lParam);
    //}}AFX_MSG

4、实现消息处理函数

void CSocketView::OnAccept(WPARAM wParam, LPARAM lParam){
//函数体

}

砰….PASS

又一个难题倒在我的脚下。。。。哈哈。。。。

困挠我好几天了。。一直解决不了。。昨天憋了一晚上终于搞定。。。庆祝一下。。

现象:一个CpropertySheet。里面放几个CPropertyPage。。。当按下右上角的close按钮时。。我要判断当前是否正在下载文件。。如果正在下载。。则提示用户是否强制退出。。。。

看着挺简单。。重载一个OnClose就行了。。。不过这个CPropertySheet有点变态。。不响应这个函数。。退出的时候也没有发WM_CLOSE事件。。而是只响应WM_DESTROY。。而响应这个消息不能满足我的要求。。。百度。。GOOGLE的搜啊。。。搜了半天。。也没结果。。什么OnApply()函数的呀。。。统统没用。。。我这可郁闷了。。看着一条重要信息。。这个东东响应SC_CLOSE消息。。不过这个消息是OnSysCommand()响应这个函数。。我试了试。。ClassWizard不能重载这个函数。。手工添加进去。。也提示CPropertySheet类没有成员函数OnSysCommand(),更郁闷了。。刚有点眉目了。。又搞不定了。。。灵感一来。。挡都挡不住。。找到了DefWindowProc这个默认的消息处理函数。。。重载一下。。先试一下。。代码如下

LRESULT CProp_Sheet::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

// TODO: Add your specialized code here and/or call the base class

if(WM_SYSCOMMAND==message&&wParam==SC_CLOSE)

      MessageBox(“test”);

    return CPropertySheet::DefWindowProc(message, wParam, lParam);

}

一个对话框弹出来了。。哈哈。。。成功。。

接下来该让用户选择是否强制退出了。。。

问题又来了。。我新开一个线程来下载文件。。如果用户选择强制退出。。则要把线程也终止了。。用TerminateThreade有点不厚道。。线程占用的资源不会被释放。。还是让它自己终止的比较好。。设定一个标志位IsDownLoadFile。。在线程开始的时候设定为TRUE,在线程结束的时候设为FALSE。。。如果用户选择强制退出。。则把IsDownLoadFile设为FALSE。。而下载文件的线程函数正好是一个循环。。在循环里判断IsDownLoadFile是否为TRUE。。如果为FALSE则说明用户强制退出。。看着挺简单。。问题又来了。。强制退的时候弹出个对话框DEBUG错误。。afxcmn.inl 第多少多少行出现错误。。一看是因为我在下载的时候还操作了主界面的CProgressBar和CStatic这两个控件。。主界面DefWindowProc执行完IsDownLoadFile=FALSE后。不等线程函数执行完即退出。。导致出错。。去网上找了点灵感。。又“砰”。。PASS。。。

主界面DefWindowProc执行完IsDownLoadFile=FALSE后Return FALSE,不让它向下执行。。则主界面不退出。。线程函数在最后判断是否为异常退出。。如果是则向主界面发送一个WM_SYSCOMMAND消息。消息参数为SC_CLOSE。。。终于正常了。。很曲折。。也可能是自己很菜。还是不熟练的原因。。。呵呵。。困难。PASS。。继续喽

常见Windows键盘按键虚拟码[转]

键盘常用ASCII码

ESC键 VK_ESCAPE (27)

回车键: VK_RETURN (13)

TAB键: VK_TAB (9)

Caps Lock键: VK_CAPITAL (20)

Shift键: VK_SHIFT ()

Ctrl键: VK_CONTROL (17)

Alt键: VK_MENU (18)

空格键: VK_SPACE (/32)

退格键: VK_BACK (8)

左徽标键: VK_LWIN (91)

右徽标键: VK_LWIN (92)

鼠标右键快捷键:VK_APPS (93)

Insert键: VK_INSERT (45)

Home键: VK_HOME (36)

Page Up: VK_PRIOR (33)

PageDown: VK_NEXT (34)

End键: VK_END (35)

Delete键: VK_DELETE (46)

方向键(←): VK_LEFT (37)

方向键(↑): VK_UP (38)

方向键(→): VK_RIGHT (39)

方向键(↓): VK_DOWN (40)

F1键: VK_F1 (112)

F2键: VK_F2 (113)

F3键: VK_F3 (114)

F4键: VK_F4 (115)

F5键: VK_F5 (116)

F6键: VK_F6 (117)

F7键: VK_F7 (118)

F8键: VK_F8 (119)

F9键: VK_F9 (120)

F10键: VK_F10 (121)

F11键: VK_F11 (122)

F12键: VK_F12 (123)

Num Lock键: VK_NUMLOCK (144)

小键盘0: VK_NUMPAD0 (96)

小键盘1: VK_NUMPAD0 (97)

小键盘2: VK_NUMPAD0 (98)

小键盘3: VK_NUMPAD0 (99)

小键盘4: VK_NUMPAD0 (100)

小键盘5: VK_NUMPAD0 (101)

小键盘6: VK_NUMPAD0 (102)

小键盘7: VK_NUMPAD0 (103)

小键盘8: VK_NUMPAD0 (104)

小键盘9: VK_NUMPAD0 (105)

小键盘.: VK_DECIMAL (110)

小键盘*: VK_MULTIPLY (106)

小键盘+: VK_MULTIPLY (107)

小键盘-: VK_SUBTRACT (109)

小键盘/: VK_DIVIDE (111)

Pause Break键: VK_PAUSE (19)

Scroll Lock键: VK_SCROLL (145)

[转]如何在工作线程中创建窗口?

在前面我们研究了使用AFX_MANAGE_STATE(AfxGetStaticModuleState())进行DLL间的资源切换,以及工作线程中创建Windows消息循环的原理,以为就可以搞定一切类似问题了…但是请看以下代码



DWORD CTestMFCDlg::ThreadFunc(PVOID yy)

{

CAboutDlg dlg;

dlg.DoModal();

return 0;

}


void CTestMFCDlg::OnOK()

{

::CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,NULL,NULL);

}



在VC++6.0上编译运行出现以下ASSERT。


void CWnd::AssertValid() const

{

……

CHandleMap* pMap = afxMapHWND();

……


CObject* p;

ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||

(p = pMap->LookupTemporary(m_hWnd)) != NULL);

ASSERT((CWnd*)p == this);   // must be us


MFC有一个全局的Hash表(通过afxMapHWND()获得),用于把HWND句柄与MFC的封装对象CWnd进行关联,这样就可以通过CWnd::FromHandle()等函数把CWnd对象Attach到一个已有的HWND句柄上,利用MFC的封装函数可以简化对HWND的直接操作。很显然,这里的Assert是因为CWnd对象根据自身的窗口句柄(m_hWnd)从Hash表里找到CWnd对象指针与对象的本身(this)并不相同!这说明,CWnd对象创建时注册到的Hash表与目前检索的Hash表并不是同一个。为什么会是这样的呢?


CHandleMap* PASCAL afxMapHWND(BOOL bCreate)

{

AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();

if (pState->m_pmapHWND == NULL && bCreate)

{

…….

pState->m_pmapHWND = new CHandleMap(RUNTIME_CLASS(CTempWnd),offsetof(CWnd, m_hWnd));

……

}


return pState->m_pmapHWND;



看来这个Hash表跟AfxGetModuleThreadState()有关,继续



AFX_MODULE_THREAD_STATE* AFXAPI AfxGetModuleThreadState()

{

return AfxGetModuleState()->m_thread.GetData();

}




AFX_MODULE_STATE* AFXAPI AfxGetModuleState()


{


_AFX_THREAD_STATE* pState = _afxThreadState;


AFX_MODULE_STATE* pResult;


if (pState->m_pModuleState != NULL)


{


// thread state’s module state serves as override


pResult = pState->m_pModuleState;


}


else


{


// otherwise, use global app state


pResult = _afxBaseModuleState.GetData();


}


ASSERT(pResult != NULL);


return pResult;


}




那么_afxThreadState是什么呢?




EXTERN_THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)




class _AFX_THREAD_STATE : public CNoTrackObject


{


public:


_AFX_THREAD_STATE();


virtual ~_AFX_THREAD_STATE();




// override for m_pModuleState in _AFX_APP_STATE


AFX_MODULE_STATE* m_pModuleState;


AFX_MODULE_STATE* m_pPrevModuleState;




#define THREAD_LOCAL(class_name, ident_name)


AFX_DATADEF CThreadLocal<class_name> ident_name;


#define EXTERN_THREAD_LOCAL(class_name, ident_name)


extern AFX_DATA THREAD_LOCAL(class_name, ident_name)




分析的结果是 extern CThreadLocal<_AFX_THREAD_STATE> _afxThreadState。




template<class TYPE>


class CThreadLocal : public CThreadLocalObject


{


AFX_INLINE TYPE* GetData()


{


TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject);


ASSERT(pData != NULL);


return pData;


}


AFX_INLINE operator TYPE*()


{ return GetData(); }


AFX_INLINE TYPE* operator->()


{ return GetData(); }




static CNoTrackObject* AFXAPI CreateObject()


{ return new TYPE; }






可以看出来了,_afxThreadState是一个全局的对象。通过该对象可以获得_AFX_THREAD_STATE对象,后者是线程相关的。CThreadLocalObject的代码不再分析,大概就是检查当前的线程私有数据,如果有则返回,否则创建新的对象(即_AFX_THREAD_STATE)。




继续看AfxGetModuleState(),大致的意思是获取与当前线程相关联的AFX_MODULE_STATE对象,如果没有则获取该进程的缺省AFX_MODULE_STATE对象。




PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)




class _AFX_BASE_MODULE_STATE : public AFX_MODULE_STATE




#define PROCESS_LOCAL(class_name, ident_name)


AFX_DATADEF CProcessLocal<class_name> ident_name;


#define EXTERN_PROCESS_LOCAL(class_name, ident_name)


extern AFX_DATA PROCESS_LOCAL(class_name, ident_name)




继续看AfxGetModuleThreadState(),在获得了AFX_MODULE_STATE对象之后,访问其m_thread成员。这又是一个线程相关的数据,可以获得AFX_MODULE_STATE在不同线程中的私有数据。




// AFX_MODULE_STATE (global data for a module)


class AFX_MODULE_STATE : public CNoTrackObject


{


// define thread local portions of module state


THREAD_LOCAL(AFX_MODULE_THREAD_STATE, m_thread)




现在简单总结一下,AfxGetModuleState()可以获得与执行线程关联的AFX_MODULE_STATE,而AfxGetModuleThreadState()可以获得与执行线程关联的AFX_MODULE_STATE与当前执行线程关联的AFX_MODULE_THREAD_STATE。看起来有点绕,再

时间转换

  毫秒时间单位,符号ms(英语:millisecond ).

  1毫秒等于一千分之一秒

  0.000 000 001 毫秒 = 1皮秒

  0.000 001 毫秒 = 1纳秒

  0.001 毫秒 = 1微秒

  1000 毫秒 = 1

协调世界时

  英文Coordinated Universal Time (UTC)

  法文Temps universel coordonné

  协调世界时,又称世界标准时间,简称UTC

  中国大陆采用ISO 8601-1988国标《数据元和交换格式信息交换日期和时间表示法》(GB/T 7408-1994)中称之为国际协调时间

  中国台湾采用CNS 7648的《资料元及交换格式–资讯交换–日期及时间的表示法》(与ISO 8601类似)称之为世界统一时间

  协调世界时是以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统。

【历史】

  国际原子时的准确度为每日数纳秒,而世界时的准确度为每日数毫秒。对于这种情况,一种称为协调世界时的折衷时标于1972年面世。为确保协调世界时与世界时相差不会超过0.9秒,在有需要的情况下会在协调世界时内加上正或负闰秒。因此协调世界时与国际原子时之间会出现若干整数秒的差别。位于巴黎的国际地球自转事务中央局负责决定何时加入闰秒。

【用处】

  这套时间系统被应用于许多互联网万维网的标准中,例如,网络时间协议就是协调世界时在互联网中使用的一种方式。

  在军事中,协调世界时区会使用“Z”来表示。又由于Z在无线电联络中使用“Zulu”作代称,协调世界时也会被称为”Zulu time“。

  中国大陆、中国香港、中国澳门、中国台湾、蒙古国新加坡马来西亚菲律宾西澳大利亚州的时间与UTC的时差均为+8,也就是UTC+8

  UTC

  美国联合技术公司(UNITED TECHNOLOGIES CORPORATION)简称UTC,是引领全球航空工业高科技及工业机械产品生产制造的高科技技术集团公司。2000年营业额为266亿美元。在美国《财富》杂志(FORTUNE)全美大企业排名为第64名;美国寿力公司是全球最大的螺杆式空压机生产厂家,作为排名在美国500家最大工业企业前200位之一胜特兰工业集团的子公司,专业从事螺杆式空压机的设计、研究、生产。自1965年始,作为该领域的开拓者,寿力公司已为当时螺杆式空压机的设计和生产制定了极高的水准,同时,寿力已被全世界公认为高品质压缩空气污染物清除设备,其产品包括固定式螺杆空气压缩机、移动式螺杆空气压缩机、螺杆真空泵、空气干燥机、精密过滤器、气动工具等,被广泛运用于各行各业。同时也是道琼斯(DOW JONES)基本股的主要成员之一。

  美国联合技术公司在全球拥有逾15万员工,生产及销售、服务网点遍及全球各角落。多年来,大力开拓亚太区市场,其旗下的多个产品如CARRIER的空调及制冷主机;OTIS电梯、电扶梯以及普惠公司(PRATT&WHITNEY)的飞机发动机,SIKORSKY的直升机以及美国联合技术公司的全资子公司寿力公司(SULLAIR)的寿力空压机等已深深扎根于全球市场,并为广大用户耳熟能详。美国联合技术公司对未来亚洲的投资、发展深具信心,并将为中国及亚洲的现代化作出积极贡献。

解决TCP网络传输“粘包”问题

原文出处:http://www.ciw.com.cn/


当前在网络传输应用中,广泛采用的是TCP/IP通信协议及其标准的socket应用开发编程接口(API)。TCP/IP传输层有两个并列的协议:TCP和UDP。其中TCP(transport control protocol,传输控制协议)是面向连接的,提供高可靠性服务。UDP(user datagram protocol,用户数据报协议)是无连接的,提供高效率服务。在实际工程应用中,对可靠性和效率的选择取决于应用的环境和需求。一般情况下,普通数据的网络传输采用高效率的udp,重要数据的网络传输采用高可靠性的TCP。

在应用开发过程中,笔者发现基于TCP网络传输的应用程序有时会出现粘包现象(即发送方发送的若干包数据到接收方接收时粘成一包)。针对这种情况,我们进行了专题研究与实验。本文重点分析了TCP网络粘包问题,并结合实验结果提出了解决该问题的对策和方法,供有关工程技术人员参考。

一、TCP协议简介

  TCP是一个面向连接的传输层协议,虽然TCP不属于iso制定的协议集,但由于其在商业界和工业界的成功应用,它已成为事实上的网络标准,广泛应用于各种网络主机间的通信。

  作为一个面向连接的传输层协议,TCP的目标是为用户提供可靠的端到端连接,保证信息有序无误的传输。它除了提供基本的数据传输功能外,还为保证可靠性采用了数据编号、校验和计算、数据确认等一系列措施。它对传送的每个数据字节都进行编号,并请求接收方回传确认信息(ack)。发送方如果在规定的时间内没有收到数据确认,就重传该数据。数据编号使接收方能够处理数据的失序和重复问题。数据误码问题通过在每个传输的数据段中增加校验和予以解决,接收方在接收到数据后检查校验和,若校验和有误,则丢弃该有误码的数据段,并要求发送方重传。流量控制也是保证可靠性的一个重要措施,若无流控,可能会因接收缓冲区溢出而丢失大量数据,导致许多重传,造成网络拥塞恶性循环。TCP采用可变窗口进行流量控制,由接收方控制发送方发送的数据量。

  TCP为用户提供了高可靠性的网络传输服务,但可靠性保障措施也影响了传输效率。因此,在实际工程应用中,只有关键数据的传输才采用TCP,而普通数据的传输一般采用高效率的udp。

二、粘包问题分析与对策

  TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。

  出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据(图1所示)。



图1



图2



图3


  粘包情况有两种,一种是粘在一起的包都是完整的数据包(图1、图2所示),另一种情况是粘在一起的包有不完整的包(图3所示),此处假设用户接收缓冲区长度为m个字节。

  不是所有的粘包现象都需要处理,若传输的数据为不带结构的连续流数据(如文件传输),则不必把粘连的包分开(简称分包)。但在实际工程应用中,传输的数据一般为带结构的数据,这时就需要做分包处理。

  在处理定长结构数据的粘包问题时,分包算法比较简单;在处理不定长结构数据的粘包问题时,分包算法就比较复杂。特别是如图3所示的粘包情况,由于一包数据内容被分在了两个连续的接收包中,处理起来难度较大。实际工程应用中应尽量避免出现粘包现象。

  为了避免粘包现象,可采取以下几种措施。一是对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;二是对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;三是由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。

  以上提到的三种措施,都有其不足之处。第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。

  一种比较周全的对策是:接收方创建一预处理线程,对接收到的数据包进行预处理,将粘连的包分开。对这种方法我们进行了实验,证明是高效可行的。

、编程与实现

  1.实现框架

  实验网络通信程序采用TCP/IP协议的socket api编程实现。socket是面向客户机/服务器模型的。TCP实现框架如图4所示。

图4

  2.实验硬件环境:

  服务器:pentium 350 微机

  客户机:pentium 166微机

  网络平台:由10兆共享式hub连接而成的局域网

  3.实验软件环境:

  操作系统:windows 98

  编程语言:visual c++ 5.0

  4.主要线程

  编程采用多线程方式,服务器端共有两个线程:发送数据线程、发送统计显示线程。客户端共有三个线程:接收数据线程、接收预处理粘包线程、接收统计显示线程。其中,发送和接收线程优先级设为thread_priority_time_critical(最高优先级),预处理线程优先级为thread_priority_above_normal(高于普通优先级),显示线程优先级为thread_priority_normal(普通优先级)。

  实验发送数据的数据结构如图5所示:

图5

  5.分包算法

  针对三种不同的粘包现象,分包算法分别采取了相应的解决办法。其基本思路是首先将待处理的接收数据流(长度设为m)强行转换成预定的结构数据形式,并从中取出结构数据长度字段,即图5中的n,而后根据n计算得到第一包数据长度。

  1)若n<m,则表明数据流包含多包数据,从其头部截取n个字节存入临时缓冲区,剩余部分数据依此继续循环处理,直至结束。


  2)若n=m,则表明数据流内容恰好是一完整结构数据,直接将其存入临时缓冲区即可。

  3)若n>m,则表明数据流内容尚不够构成一完整结构数据,需留待与下一包数据合并后再行处理。

  对分包算法具体内容及软件实现有兴趣者,可与作者联系。

四、实验结果分析

  实验结果如下:

  1.在上述实验环境下,当发送方连续发送的若干包数据长度之和小于1500b时,常会出现粘包现象,接收方经预处理线程处理后能正确解开粘在一起的包。若程序中设置了“发送不延迟”:(setsockopt (socket_name,ipproto_tcp,tcp_nodelay,(char *) &on,sizeof on) ,其中on=1),则不存在粘包现象。

  2.当发送数据为每包1kb~2kb的不定长数据时,若发送间隔时间小于10ms,偶尔会出现粘包,接收方经预处理线程处理后能正确解开粘在一起的包。

  3.为测定处理粘包的时间,发送方依次循环发送长度为1.5kb、1.9kb、1.2kb、1.6kb、1.0kb数据,共计1000包。为制造粘包现象,接收线程每次接收前都等待10ms,接收缓冲区设为5000b,结果接收方收到526包数据,其中长度为5000b的有175包。经预处理线程处理可得到1000包正确数据,粘包处理总时间小于1ms。

  实验结果表明,TCP粘包现象确实存在,但可通过接收方的预处理予以解决,而且处理时间非常短(实验中1000包数据总共处理时间不到1ms),几乎不影响应用程序的正常工作。

【转载】Socket中如何设置连接超时-通过测试(VC)

设置connect超时很简单,CSDN上也有人提到过使用select,但却没有一个令人满意与完整的答案。偶所讲的也正是select函数,此函数集成在winsock1.1中,简单点讲,”作用使那些想避免在套接字调用过程中被锁定的应用程序,采取一种有序的方式,同时对多个套接字进行管理”(《Windows网络编程技术》原话)。使用方法与解释请见《Windows网络编程技术》。

  在使用此函数前,需先将socket设置为非阻塞模式,这样,在connect时,才会立马跳过,同时,通常也会产生一个WSAEWOULDBLOCK错误,这个错误没关系。再执行select则是真正的超时。


WSADATA wsd;

SOCKET cClient;

int ret;

struct sockaddr_in server;

hostent *host=NULL;

if(WSAStartup(MAKEWORD(2,0),&wsd)){return 0;}

cClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

if(cClient==INVALID_SOCKET){return 0;}

//set Recv and Send time out

DWORD TimeOut=6000; //设置发送超时6秒

if(::setsockopt(cClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){

return 0;

}

TimeOut=6000;//设置接收超时6秒

if(::setsockopt(cClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){

return 0;

}

//设置非阻塞方式连接

unsigned long ul = 1;

ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul);

if(ret==SOCKET_ERROR)return 0;

//连接

server.sin_family = AF_INET;

server.sin_port = htons(25);

server.sin_addr .s_addr = inet_addr((LPCSTR)pSmtp);

if(server.sin_addr.s_addr == INADDR_NONE){return 0;}

connect(cClient,(const struct sockaddr *)&server,sizeof(server)); //立即返回

//select 模型,即设置超时

struct timeval timeout ;

fd_set r;

FD_ZERO(&r);

FD_SET(cClient, &r);

timeout.tv_sec = 15; //连接超时15秒

timeout.tv_usec =0;

ret = select(0, 0, &r, 0, &timeout);

if ( ret <= 0 )

{

::closesocket(cClient);

return 0;

}

//一般非阻塞模式套接比较难控制,可以根据实际情况考虑 再设回阻塞模式

unsigned long ul1= 0 ;

ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul1);

if(ret==SOCKET_ERROR){

::closesocket (cClient);

return 0;

}

原文:http://www.cnblogs.com/nemolog/archive/2006/03/12/348431.html

补充——关于Socket阻塞和非阻塞的区别:

简单点说:

阻塞就是干不完不准回来,   

非组赛就是你先干,我现看看有其他事没有,完了告诉我一声

我们拿最常用的send和recv两个函数来说吧…

比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执行成功并不代表数据已经成功的发送出去了,如果TCP/IP协议栈没有足够的可用缓冲区来保存你Copy过来的数据的话…这时候就体现出阻塞和非阻塞的不同之处了:对于阻塞模式的socket send函数将不返回直到系统缓冲区有足够的空间把你要发送的数据Copy过去以后才返回,而对于非阻塞的socket来说send会立即返回WSAEWOULDDBLOCK告诉调用者说:”发送操作被阻塞了!!!你想办法处理吧…”

对于recv函数,同样道理,该函数的内部工作机制其实是在等待TCP/IP协议栈的接收缓冲区通知它说:嗨,你的数据来了.对于阻塞模式的socket来说如果TCP/IP协议栈的接收缓冲区没有通知一个结果给它它就一直不返回:耗费着系统资源….对于非阻塞模式的socket该函数会马上返回,然后告诉你:WSAEWOULDDBLOCK—“现在没有数据,回头在来看看”