u_long htonl(u_long hostlong); 举例Qhtonl(0)=0 htonl(80)= 13421772803、将unsigned longC|络字节序转换位主机字节顺序,是上面函数的逆函数?
u_long ntohl(u_long netlong); 举例Qntohl(0)=0 ntohl(1342177280)= 804、将L的unsigned shortD{换ؓ|络字节序(16?Q原因同2Q?
u_short htons(u_short hostshort); 举例Qhtonl(0)=0 htonl(80)= 204805、将unsigned shortC|络字节序转换位主机字节顺序,是上面函数的逆函数?
u_short ntohs(u_short netshort); 举例Qntohs(0)=0 ntohsl(20480)= 806、将用点分割的IP地址转换位一个in_addrl构的地址Q这个结构的定义见笔?一)Q实际上是一个unsigned long倹{计机内部处理IP地址可是不认识如192.1.8.84之类的数据?
unsigned long inet_addr( const char FAR * cp ); 举例Qinet_addr("192.1.8.84")=1409810880 inet_addr("127.0.0.1")= 16777343如果发生错误Q函数返回INADDR_NONE倹{?br>
char FAR * inet_ntoa( struct in_addr in ); 举例Qchar * ipaddr=NULL; char addr[20]; in_addr inaddr; inaddr. s_addr=16777343; ipaddr= inet_ntoa(inaddr); strcpy(addr,ipaddr);q样addr的值就变ؓ127.0.0.1?br>注意意不要修改返回值或者进行释攑֊作。如果函数失败就会返回NULL倹{?br>
int getsockname(SOCKET s, struct sockaddr FAR * name, int FAR * namelen ); s为套接字 name为函数调用后获得的地址? namelen为缓冲区的大?9、获取与套接字相q的端地址l构Q?br>
int getpeername(SOCKET s, struct sockaddr FAR * name, int FAR * namelen ); s为套接字 name为函数调用后获得的端地址? namelen为缓冲区的大?10、获取计机名:
int gethostname( char FAR * name, int namelen ); name是存放计机名的~冲? namelen是缓冲区的大? 用法Q? char szName[255]; memset(szName,0,255); if(gethostname(szName,255)==SOCKET_ERROR) { //错误处理 } q回gؓQszNmae="xiaojin"11、根据计机名获取主机地址Q?
struct hostent FAR * gethostbyname( const char FAR * name ); name机名? 用法Q? hostent * host; char* ip; host= gethostbyname("xiaojin"); if(host->h_addr_list[0]) { struct in_addr addr; memmove(&addr, host->h_addr_list[0]Q?); //获得标准IP地址 ip=inet_ ntoa (addr); } q回gؓQhostent->h_name="xiaojin" hostent->h_addrtype=2 //AF_INET hostent->length=4 ip="127.0.0.1"
int select( int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR *exceptfds, const struct timeval FAR * timeout );◆先来看看涉及到的结构的定义Q?br>a?d_setl构Q?br>
#define FD_SETSIZE 64? typedef struct fd_set { u_int fd_count; /* how many are SET? */ SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */ } fd_set;fd_count为已讑֮socket的数?br>fd_array为socket列表QFD_SETSIZE为最大socket数量Q徏议不于64。这是微软徏
struct timeval { long tv_sec; /* seconds */ long tv_usec; /* and microseconds */ };tv_sec为时间的U倹{?br>tv_usec为时间的毫秒倹{?br>q个l构主要是设|select()函数的等待|如果该l构讄?0,0)Q则select()函数
fd_set fdread; //FD_ZERO定义 // #define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0) FD_ZERO(&fdread); FD_SET(s,&fdread)Q?//加入套接字,详细定义Lwinsock2.h if(select(0,%fdread,NULL,NULL,NULL)>0 { //成功 if(FD_ISSET(s,&fread) //是否存在fread中,详细定义Lwinsock2.h { //是可ȝ } }
◆I/O操作函数Q主要用于获取与套接字相关的操作参数?
int ioctlsocket(SOCKET s, long cmd, u_long FAR * argp );s为I/O操作的套接字?br>cmd为对套接字的操作命o?br>argp为命令所带参数的指针?br>
//定套接字自动读入的数据? #define FIONREAD _IOR(''''f'''', 127, u_long) /* get # bytes to read */ //允许或禁止套接字的非d模式Q允ؓ?Q禁止ؓ0 #define FIONBIO _IOW(''''f'''', 126, u_long) /* set/clear non-blocking i/o */ //定是否所有带外数据都已被d #define SIOCATMARK _IOR(''''s'''', 7, u_long) /* at oob mark? */3、WSAAsynSelect模型Q?br>WSAAsynSelect模型也是一个常用的异步I/O模型。应用程序可以在一个套接字上接收以
int WSAAsyncSelect( SOCKET s, HWND hWnd, u_int wMsg, long lEvent);s为需要事仉知的套接字
#define FD_READ_BIT 0 #define FD_READ (1 << FD_READ_BIT) #define FD_WRITE_BIT 1 #define FD_WRITE (1 << FD_WRITE_BIT) #define FD_OOB_BIT 2 #define FD_OOB (1 << FD_OOB_BIT) #define FD_ACCEPT_BIT 3 #define FD_ACCEPT (1 << FD_ACCEPT_BIT) #define FD_CONNECT_BIT 4 #define FD_CONNECT (1 << FD_CONNECT_BIT) #define FD_CLOSE_BIT 5 #define FD_CLOSE (1 << FD_CLOSE_BIT)用法Q要接收d通知Q?
int nResult= WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE)Q? if(nResult==SOCKET_ERROR) { //错误处理 }取消通知Q?br>
int nResult= WSAAsyncSelect(s,hWnd,0Q?)Q?当应用程序窗口hWnd收到消息ӞwMsg.wParam参数标识了套接字QlParam的低字标?br>了网l事Ӟ高字则包含错误代码?br>
#define WSAEVENT HANDLE #define LPWSAEVENT LPHANDLE WSAEVENT WSACreateEvent( void );该函数的q回gؓ一个事件对象句柄,它具有两U工作状态:已传?signaled)和未传信
int WSAEventSelect( SOCKET s,WSAEVENT hEventObject,long lNetworkEvents );s为套接字
BOOL WSAResetEvent( WSAEVENT hEvent );
HeventZ件对?br>
成功q回TRUEQ失败返回FALSE?br>
d、等待网l事件来触发事g句柄的工作状态:
DWORD WSAWaitForMultipleEvents( DWORD cEvents, const WSAEVENT FAR * lphEvents, BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable );
lpEventZ件句柄数l的指针
cEventZؓ事g句柄的数目,其最大gؓWSA_MAXIMUM_WAIT_EVENTS
fWaitAll指定{待cdQTRUEQ当lphEvent数组重所有事件对象同时有信号时返回;
FALSEQQ一事g有信号就q回?br>dwTimeout为等待超Ӟ毫秒Q?br>fAlertable为指定函数返回时是否执行完成例程
对事件数l中的事件进行引用时Q应该用WSAWaitForMultipleEvents的返回|减去
预声明值WSA_WAIT_EVENT_0Q得到具体的引用倹{例如:
nIndex=WSAWaitForMultipleEvents(…); MyEvent=EventArray[Index- WSA_WAIT_EVENT_0];
e、判断网l事件类型:
int WSAEnumNetworkEvents( SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents );
s为套接字
hEventObject为需要重讄事g对象
lpNetworkEvents录网l事件和错误代码Q其l构定义如下Q?/p>
typedef struct _WSANETWORKEVENTS { long lNetworkEvents; int iErrorCode[FD_MAX_EVENTS]; } WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
f、关闭事件对象句柄:
BOOL WSACloseEvent(WSAEVENT hEvent);
调用成功q回TRUEQ否则返回FALSE?br>
◆先看定义:
typedef unsigned int u_int;
typedef u_int SOCKET;◆Socket相当于进行网l通信两端的插座,只要Ҏ的Socket和自qSocket有通信联接Q双方就可以发送和接收数据了。其定义cM于文件句柄的定义?/p>
◆Socket有五U不同的cdQ?/p>
1、流式套接字(stream socket)
定义Q?/p>
#define SOCK_STREAM 1 式套接字提供了双向、有序的、无重复的以及无记录边界的数据流服务Q适合处理大量数据。它是面向联l的Q必d立数据传输链路,同时q必d传输的数据进行验证,保数据的准性。因此,pȝ开销较大?/p>
2?数据报套接字(datagram socket)
定义Q?/p>
#define SOCK_DGRAM 2 数据报套接字也支持双向的数据,但不保证传输数据的准性,但保留了记录边界。由于数据报套接字是无联接的Q例如广播时的联接,所以ƈ不保证接收端是否正在侦听。数据报套接字传输效率比较高?/p>
3、原始套接字(raw-protocol interface)
定义Q?/p>
#define SOCK_RAW 3 原始套接字保存了数据包中的完整IP_前面两种套接字只能收到用h据。因此可以通过原始套接字对数据q行分析?br>其它两种套接字不常用Q这里就不介l了?/p>
◆Socket开发所必须需要的文g(以WinSock V2.0Z)Q?/p>
头文ӞWinsock2.h
库文ӞWS2_32.LIB
动态库QW32_32.DLL
一些重要的定义
1、数据类型的基本定义Q这个大家一看就懂?/p>
typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned int u_int;
typedef unsigned long u_long;2?|络地址的数据结构,有一个老的和一个新的的Q请大家留意Q如果想知道Z么,
请发邮glBill Gate。其实就是计机的IP地址Q不q一般不用用点分开的IP?br>址Q当然也提供一些{换函数?/p>
?旧的|络地址l构的定义,Z?字节的联合:
struct in_addr {
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
//下面几行省略,反正没什么用处?br>};其实完全不用q么ȝQ请看下?
?新的|络地址l构的定义:
非常单,是一个无W号长整?unsigned long。D个例子:IP地址?27.0.0.1的网l地址是什么呢Q请看定义:
#define INADDR_LOOPBACK 0x7f0000013?套接字地址l构
(1)、sockaddrl构Q?/p>
struct sockaddr {
u_short sa_family; /* address family */
char sa_data[14]; /* up to 14 bytes of direct address */
};sa_family为网l地址cdQ一般ؓAF_INETQ表Csocket在Internet域中q行通信Q该地址l构随选择的协议的不同而变化,因此一般情况下另一个与该地址l构大小相同的sockaddr_inl构更ؓ常用Qsockaddr_inl构用来标识TCP/IP协议下的地址。换句话_q个l构是通用socket地址l构Q而下面的sockaddr_in是专门针对Internet域的socket地址l构?/p>
(2)、sockaddr_inl构
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};sin _family为网l地址cdQ必设定ؓAF_INET。sin_port为服务端口,注意不要使用已固定的服务端口Q如HTTP的端?0{。如果端口设|ؓ0Q则pȝ会自动分配一个唯一端口。sin_addrZ个unsigned long的IP地址。sin_zero为填充字D,Ua用来保证l构的大?/p>
?常用的用点分开的IP地址转换为unsigned longcd的IP地址的函敎ͼ
unsigned long inet_addr(const char FAR * cp )用法Q?/p>
unsigned long addr=inet_addr("192.1.8.84")?如果sin_addr讄为INADDR_ANYQ则表示所有的IP地址Q也x有的计算机?/p>
#define INADDR_ANY (u_long)0x000000004?L地址Q?/p>
先看定义Q?/p>
struct hostent {
char FAR * h_name; /* official name of host */
char FAR * FAR * h_aliases; /* alias list */
short h_addrtype; /* host address type */
short h_length; /* length of address */
char FAR * FAR * h_addr_list; /* list of addresses */
#define h_addr h_addr_list[0] /* address, for backward compat */
};
h_nameZ机名字?br>h_aliasesZ机别名列表?br>h_addrtype为地址cd?br>h_length为地址cd?br>h_addr_list为IP地址Q如果该L有多个网卡,包括地址的列表。另外还有几个类似的l构Q这里就不一一介绍了?/p>
5?常见TCP/IP协议的定义:
#define IPPROTO_IP 0
#define IPPROTO_ICMP 1
#define IPPROTO_IGMP 2
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
#define IPPROTO_RAW 255 具体是什么协议,大家一看就知道了?/p>
套接字的属?/p>
Z灉|使用套接字,我们可以对它的属性进行设定?/p>
1?属性内容:
//允许调试输出
#define SO_DEBUG 0x0001 /* turn on debugging info recording */
//是否监听模式
#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */
//套接字与其他套接字的地址l定
#define SO_REUSEADDR 0x0004 /* allow local address reuse */
//保持q接
#define SO_KEEPALIVE 0x0008 /* keep connections alive */
//不要路由出去
#define SO_DONTROUTE 0x0010 /* just use interface addresses */
//讄为广?br>#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */
//使用环回不通过g
#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */
//当前拖g?br>#define SO_LINGER 0x0080 /* linger on close if data present */
//是否加入带外数据
#define SO_OOBINLINE 0x0100 /* leave received OOB data in line */
//用LINGER选项
#define SO_DONTLINGER (int)(~SO_LINGER)
//发送缓冲区长度
#define SO_SNDBUF 0x1001 /* send buffer size */
//接收~冲区长?br>#define SO_RCVBUF 0x1002 /* receive buffer size */
//发送超时时?br>#define SO_SNDTIMEO 0x1005 /* send timeout */
//接收时旉
#define SO_RCVTIMEO 0x1006 /* receive timeout */
//错误状?br>#define SO_ERROR 0x1007 /* get error status and clear */
//套接字类?br>#define SO_TYPE 0x1008 /* get socket type */2?dsocket属性:
int getsockopt(SOCKET s, int level, int optname, char FAR * optval, int FAR * optlen)s为欲d属性的套接字。level为套接字选项的别,大多数是特定协议和套接字专有的。如IP协议应ؓ IPPROTO_IP?/p>
optname取选项的名U?br>optval为存N项值的~冲区指针?br>optlen为缓冲区的长度用法:
int ttl=0; //dTTL?br>int rc = getsockopt( s, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl));
//来自MS platform SDK 20033?讄socket属性:
int setsockopt(SOCKET s,int level, int optname,const char FAR * optval, int optlen)s为欲讄属性的套接字?br>level为套接字选项的别,用法同上?br>optname|选项的名U?br>optval为存N项值的~冲区指针?br>optlen为缓冲区的长?/p>
用法Q?/p>
int ttl=32; //讄TTL?br>int rc = setsockopt( s, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)); 套接字的使用步骤
1、启动WinsockQ对Winsock DLLq行初始化,协商Winsock的版本支持ƈ分配必要?br>资源。(服务器端和客LQ?/p>
int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData )
wVersionRequested为打加载Winsock的版本,一般如下设|:
wVersionRequested=MAKEWORD(2,0)
或者直接赋|wVersionRequested=2
LPWSADATA为初始化Socket后加载的版本的信?定义如下Q?br>typedef struct WSAData {
WORD wVersion;
WORD wHighVersion;
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYS_STATUS_LEN+1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
} WSADATA, FAR * LPWSADATA;如果加蝲成功后数据ؓQ?/p>
wVersionQ?表示加蝲版本?.0?br>wHighVersionQ?14表示当前pȝ支持socket最高版本ؓ2.2?br>szDescription="WinSock 2.0"
szSystemStatus="Running"表示正在q行?br>iMaxSocketsQ?表示同时打开的socket最大数Qؓ0表示没有限制?br>iMaxUdpDgQ?表示同时打开的数据报最大数Qؓ0表示没有限制?br>lpVendorInfo没有使用Qؓ厂商指定信息预留。该函数使用ҎQ?/p>
WORD wVersion=MAKEWORD(2,0);
WSADATA wsData;
int nResult= WSAStartup(wVersion,&wsData);
if(nResult !=0)
{
//错误处理
}2、创建套接字Q(服务器端和客LQ?/p>
SOCKET socket( int af, int type, int protocol );
af为网l地址cdQ一般ؓAF_INETQ表C在Internet域中使用?br>type为套接字cdQ前面已l介l了?br>protocol为指定网l协议,一般ؓIPPROTO_IP。用法:
SOCKET sock=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(sock==INVALID_SOCKET)
{
//错误处理
}3、套接字的绑定:本地地址l定到所创徏的套接字上。(服务器端和客LQ?/p>
int bind( SOCKET s, const struct sockaddr FAR * name, int namelen )
s为已l创建的套接字?br>name为socket地址l构Qؓsockaddrl构Q如前面讨论的,我们一般用sockaddr_in
l构Q在使用再强制{换ؓsockaddrl构?br>namelen为地址l构的长度?br>用法Q?/p>
sockaddr_in addr;
addr. sin_family=AF_INET;
addr. sin_port= htons(0); //保证字节序
addr. sin_addr.s_addr= inet_addr("192.1.8.84")
int nResult=bind(s,(sockaddr*)&addr,sizeof(sockaddr));
if(nResult==SOCKET_ERROR)
{
//错误处理
}4?套接字的监听Q(服务器端Q?/p>
int listen(SOCKET s, int backlog )sZ个已l定但未联接的套接字?br>backlog为指定正在等待联接的最大队列长度,q个参数非常重要Q因为服务器一般可
以提供多个连接?br>用法Q?/p>
int nResult=listen(s,5) //最?个连?br>if(nResult==SOCKET_ERROR)
{
//错误处理
}5、套接字{待q接:Q(服务器端Q?/p>
SOCKET accept( SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen )s为处于监听模式的套接字?br>sockaddr为接收成功后q回客户端的|络地址?br>addrlen为网l地址的长度?/p>
用法Q?/p>
sockaddr_in addr;
SOCKET s_d=accept(s,(sockaddr*)&addr,sizeof(sockaddr));
if(s==INVALID_SOCKET)
{
//错误处理
}6、套接字的连l:两个套接字q结h准备通信。(客户端)
int connect(SOCKET s, const struct sockaddr FAR * name, int namelen )s为欲q结的已创徏的套接字?br>name为欲q结的socket地址?br>namelen为socket地址的结构的长度?/p>
用法Q?/p>
sockaddr_in addr;
addr. sin_family=AF_INET;
addr. sin_port=htons(0); //保证字节序
addr. sin_addr.s_addr= htonl(INADDR_ANY) //保证字节序
int nResult=connect(s,(sockaddr*)&addr,sizeof(sockaddr));
if(nResult==SOCKET_ERROR)
{
//错误处理
}7、套接字发送数据:Q服务器端和客户端)
int send(SOCKET s, const char FAR * buf, int len, int flags )s为服务器端监听的套接字?br>buf为欲发送数据缓冲区的指针?br>len为发送数据缓冲区的长度?br>flags为数据发送标记?br>q回gؓ发送数据的字符数?/p>
◆这里讲一下这个发送标讎ͼ下面8中讨论的接收标记也一P
flag取值必Mؓ0或者如下定义的l合Q?表示没有Ҏ行ؓ?/p>
#define MSG_OOB 0x1 /* process out-of-band data */
#define MSG_PEEK 0x2 /* peek at incoming message */
#define MSG_DONTROUTE 0x4 /* send without using routing tables */
MSG_OOB表示数据应该带外发送,所谓带外数据就是TCP紧急数据?br>MSG_PEEK表示使有用的数据复制到缓冲区内,但ƈ不从pȝ~冲区内删除?br>MSG_DONTROUTE表示不要包路由出去?/p>
用法Q?/p>
char buf[]="xiaojin";
int nResult=send(s,buf,strlen(buf));
if(nResult==SOCKET_ERROR)
{
//错误处理
}8?套接字的数据接收Q(客户端)
int recv( SOCKET s, char FAR * buf, int len, int flags )s为准备接收数据的套接字?br>buf为准备接收数据的~冲区?br>len为准备接收数据缓冲区的大?br>flags为数据接收标记?br>q回gؓ接收的数据的字符数?/p>
用法Q?/p>
char mess[1000];
int nResult =recv(s,mess,1000,0);
if(nResult==SOCKET_ERROR)
{
//错误处理
}9、中断套接字q接Q通知服务器端或客L停止接收和发送数据。(服务器端和客LQ?/p>
int shutdown(SOCKET s, int how)s为欲中断q接的套接字?br>How为描q禁止哪些操作,取gؓQSD_RECEIVE、SD_SEND、SD_BOTH?/p>
#define SD_RECEIVE 0x00
#define SD_SEND 0x01
#define SD_BOTH 0x02用法Q?/p>
int nResult= shutdown(s,SD_BOTH);
if(nResult==SOCKET_ERROR)
{
//错误处理
}10?关闭套接字:释放所占有的资源。(服务器端和客LQ?/p>
int closesocket( SOCKET s )s为欲关闭的套接字?/p>
用法Q?/p>
int nResult=closesocket(s);
if(nResult==SOCKET_ERROR)
{
//错误处理
}
本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/coffeemay/archive/2006/08/05/1023149.aspx