bool bsocket_init() //初始化socket 调用

{

#ifdef _WIN32

WSADATA wsad;

WORD wVersionReq;

wVersionReq
=MAKEWORD(1,1); //清求不低于1.1版本的Winsock

if(WSAStartup(wVersionReq,&wsad))

return false;

return true;

#else

signal(SIGPIPE, SIG_IGN);
//忽略SIGPIPE信号

return true;

#endif

}


void bsocket_cleanup() //结束socket 调用

{

#ifdef _WIN32

WSACleanup();

#else

signal(SIGPIPE, SIG_DFL);
//恢复SIGPIPE信号

return;

#endif

}

其它移植问题
关闭socket

Windows中关闭socket的调用为closesocket(socket sock_in); Linux为close(socket sock_in);
Socket地址长度类型

Socket的许多调用中均用到socket地址长度做为参数,该参数在Windows中为int类型,在Linux中为socklen_t类型。
无效的socket

Windows中调用socket()和accept()时,如果返回的是无效的socket,则该值为INVALID_SOCKET,Linux中为-1。
调用错误返回

Windows中除上述的socket()和accept()以及几个少数的调用外,大部分调用在出错时返回值为SOCKET_ERROR,Linux中为-1。
多线程编程
线程与进程的不同

当把一个可执行程序装载在内存中后,不管你是你是准备它或是已经执行它或是被暂停执行,它都被称为一个进程。你可以称一个进程为一个可执行程序在内存中的实例。虽然一个进程可以复制自己,也可以调用其它进程或与其它进程通信,全基本上进程是由操作系统直接管理的资源。每一个进程均有一个与其它进程独立的操作系统运行环境,比如环境变量、当前目录等。

线程是一种可执行体,它表示对CPU运行时间的占有权,线程也是操作系统中的一种资源,它的创建、中止最终要由操作系统来实现。但线程每一个线程更多表现为由一个进程直接来管理。每一个线程均有一个与其它线程不同的硬件运行环境,比如各CPU中通用寄存器和状态寄存器的值。

一个进程的创建需要操作系统做大量工作,如设置环境变量、装裁可执行代码、分配进程资源等,进程间的切换也需做大量工作。而线程的创建的切换相对来说要简单得多,操作也能够以最快的速度来实现不同执行体之间的切换。

另一方面,由于所有线程共享一个进程空间,线程间的通信变得十分简单;而进程之间由于各自内存相互隔离,进程之间通信只能通过管道、发送系统消息或信号来实现。
线程冲突与数据保护

由于同一进程中的线程由操作系统并发执行。当一个进程中的许多线程要修改某些公用变量时就存在数据保护问题。解决这个问题的通常办法是向操作系统申请一个线程同步对象,操作系统只允许同时有一个少数个线程访问受保护的数据。
Win32中的线程
线程同步

Win32中可用于线程同步的对象有CRITICAL_SECTION, Event, Mutex, Semaphore和Waitable timer等。 其中CRITICAL_SECTION比较适于解决在单一进程多线程的冲突问题。

与CRITICAL_SECTION有关的几个函数为:

    *      InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

该函数初始化一个CRITICAL_SECTION。

    *      DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

该函数清除一个CRITICAL_SECTION。

    *      EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

该函数使本线程取得CRITICAL_SECTION控制权,代码进入保护状态。

    *      LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

该函数使本线程放弃CRITICAL_SECTION控制权,代码退出保护状态。
创建线程

Win32中最常用的创建线程的函数为CreateThread(),该函数的格式为:

HANDLE CreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes,

DWORD dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress,

LPVOID lpParameter,

DWORD dwCreationFlags,

LPDWORD lpThreadId);


    *      lpThreadAttributes: 输入参数。指定是否本次函数返回的线程句柄可以被子进程继承,可以为NULL。在CPR/CPE两个产品下载版和网页版的开发过程代码使用NULL应可满足需要。
    *      dwStackSize : 输入参数。指定指定的线程堆栈大小,可以为0,如果为0,使用默认堆栈大小。在CPR/CPE两个产品下载版和网页版的开发过程代码使用0应可满足需要。
    *      lpStartAddress : 输入参数。指向线程函数,该函数包含线程的执行代码。
    *      lpParameter : 输入参数。指定要传给线程的参数块内存地址。
    *      lpThreadId : 输出参数。该参数在调用结束后将包含被创建线程的ID。

Linux/Unix中的线程

当前各版本的Linux和Unix本身并不支持线程,但在Linux/Unix各版本均自带pthread线程包及它们的开发运行头文件和库文件。

与Win32类似,pthread包中用于线程同步的对象也有多种,在我们的开发应用中,使用pthread_mutex_t即可满足需求。与pthread_mutex_t相关的几个函数为:

    *

      pthread_mutex_init(pthread_mutex_t * p, pthread_mutexattr_t * pa);

该函数初始化一个pthread_mutex_t,其中pa参数可以设置pthread_mutex_t的属性。与Win32中多线程的机制稍有不同,在默认情况下,pthread中对同一线程的多次保护请求会造成互锁,以我们产品开发的情况来看,要修改pthread的属性方可满足实际需要,但pthread中设置多线程同步属性的方法的可移植性不高。对同一线程多次申请保护的问题可以记录线程ID的办法来解决。

    *      pthread_mutex_destroy(pthread_mutex_t * p);

清除一个已初始化的pthread_mutex_t。

    *      pthread_mutex_lock(pthread_mutex_t * p);

请求取得pthread_mutex_t控制权,进入代码保护。

    *      pthread_mutex_unlock(pthread_mutex_t * p);

放弃一个pthread_mutex_t控制权,退出代码保护。
可移植的线程代码

(参见程序实例)
程序实例
#include <stdio.h>


#ifdef _WIN32

#include
<winsock.h>

#define socklen_t int

#else

#include
<netdb.h>

#include
<sys/types.h>

#include
<sys/socket.h>

#include
<netinet/in.h>

#include
<arpa/inet.h>

#include
<unistd.h>

#include
<string.h>

#include
<stdlib.h>

#include
<pthread.h>

#include
<signal.h>

#define closesocket(_x) close(_x)

#endif


#ifdef _WIN32

#define THREAD_PROC WINAPI

#define THREAD_RETURN DWORD

#define THREAD_PARAM void *

#define THREAD_ID DWORD

#define CPO CRITICAL_SECTION

#define CPO_Init(_x) InitializeCriticalSection(&_x)

#define CPO_Dele(_x) DeleteCriticalSection(&_x)

#define CPO_Enter(_x) EnterCriticalSection(&_x)

#define CPO_Leave(_x) LeaveCriticalSection(&_x)

#else

struct CPO { pthread_mutex_t m; pthread_t t;};

#define THREAD_PROC

#define THREAD_RETURN void *

#define THREAD_PARAM void *

#define THREAD_ID pthread_t

#define CPO_Init(_x) { _x.t=0; pthread_mutex_init(&_x.m, NULL); }

#define CPO_Dele(_x) { pthread_mutex_destroy(&_x.m); }

#define CPO_Enter(_x) while(true) \

{ \

if(_x.t==0) \

{ \

pthread_mutex_lock(
&_x.m); \

_x.t
=pthread_self(); \

break;\

}\

if(pthread_self()==_x.t)\

break; \

pthread_mutex_lock(
&_x.m); \

_x.t
=pthread_self();\

break; \

}

#define CPO_Leave(_x) { pthread_mutex_unlock(&_x.m); _x.t=0; }

#endif


typedef THREAD_RETURN THREAD_PROC THREAD_FUNCTION(THREAD_PARAM thread_param);


#ifdef _WIN32

THREAD_ID bcreate_thread(THREAD_FUNCTION pfun,
void * pparam)

{

THREAD_ID tid;

if(CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pfun, (LPVOID)pparam, 0, &tid)==NULL)

return (THREAD_ID)-1;

return tid;

}

#else

THREAD_ID bcreate_thread(THREAD_FUNCTION pfun,
void * pparam)

{

THREAD_ID tid;

if(pthread_create(&tid, NULL, (void * (*)(void *))pfun, pparam)<1)

return (THREAD_ID)-1;

pthread_detach(tid);

return tid;

}

#endif


#ifndef SOCKET

#define SOCKET int

#endif


#ifndef SOCKET_ERROR

#define SOCKET_ERROR -1

#endif


#ifndef INVALID_SOCKET

#define INVALID_SOCKET -1

#endif


bool bsocket_init()

{

#ifdef _WIN32

WSADATA wsad;

WORD wVersionReq;

wVersionReq
=MAKEWORD(1,1);

if(WSAStartup(wVersionReq,&wsad))

return false;

return true;

#else

signal(SIGPIPE, SIG_IGN);

return true;

#endif

}


void bsocket_cleanup()

{

#ifdef _WIN32

WSACleanup();

#else

signal(SIGPIPE, SIG_DFL);

return;

#endif

}


#define WEB_SERVER_PORT 90

#define WEB_SERVER_IP "127.0.0.1"

#define HTTP_HEAD "HTTP/1.0 200 OK\x0d\x0a""Content-type: text/html\x0d\x0a\x0d\x0a"

#define WELCOME_HTML "<html>\n<body>\n<B>Welcome to my Website!</B><hr>\n<center>g_hits: %d</center>\n</body>\n</html>\n"


CPO g_cpo;

int g_hits;


THREAD_RETURN THREAD_PROC do_accept(THREAD_PARAM param)

{

SOCKET sock
=((SOCKET *)param)[0];

char ptmp[2048];

recv(sock, ptmp,
2048, 0);


CPO_Enter(g_cpo);

int a=g_hits;

sprintf(ptmp, WELCOME_HTML, a
++);

g_hits
=a;

CPO_Leave(g_cpo);


send(sock, HTTP_HEAD, strlen(HTTP_HEAD),
0);

send(sock, ptmp, strlen(ptmp),
0);

closesocket(sock);

return 0;

}


main(
int argc,char ** argv)

{

char perror[1024];

SOCKET s,rs;

sockaddr_in sin,rsin;

socklen_t slen;

bool result=false;

perror[
0]=0;


CPO_Init(g_cpo);

g_hits
=0;

if(!bsocket_init())

{

strcpy(perror,
"Can't init socket!");

goto error_out;

}

s
=socket(PF_INET,SOCK_STREAM,0);

if(s==INVALID_SOCKET)

{

strcpy(perror,
"Can't create socket!");

goto error_out;

}


sin.sin_family
=AF_INET;

sin.sin_port
=htons(WEB_SERVER_PORT);

sin.sin_addr.s_addr
=inet_addr(WEB_SERVER_IP);

slen
=sizeof(sin);

if(bind(s,(sockaddr *)&sin,slen)==SOCKET_ERROR)

{

strcpy(perror,
"Can't bind socket!");

goto error_out;

}

if(listen(s,5)==SOCKET_ERROR)

{

strcpy(perror,
"Can't listen on this socket!");

goto error_out;

}


printf(
"web server running......\n");

slen
=sizeof(rsin);

while(true)

{

rs
=accept(s,(sockaddr *)&rsin,&slen);

if(rs==INVALID_SOCKET)

{

strcpy(perror,
"accept() a INVALID_SOCKET!");

break;

}

bcreate_thread(do_accept,
&rs);

}


result
=true;


error_out:

if(s!=INVALID_SOCKET) closesocket(s);

if(rs!=INVALID_SOCKET) closesocket(rs);

if(!result) { printf(perror); printf("\n"); }

CPO_Dele(g_cpo);

bsocket_cleanup();


return 0;

}
 

相关文章: