windows socket函数详解

windows下cs模型
0:函数库头文件

#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib ")

1:WSAStartup   初始化Ws2_32.dll的函数
 WSAStartup 函数用于初始化供进程调用的Winsock相关的dll

int WSAStartup(
2   __in          WORD wVersionRequested,
3   __out         LPWSADATA lpWSAData
4 );

参数
标识了用户调用的Winsock的版本号。高字节指明辅版本编号,低字节指明主版本编号。通常使用MAKEWORD来生成一个版本号。 当前Winsock sockets的版本号为2.2,用到的dll是 Ws2_32.dll。
lpWSAData
指向WSADATA结构体的指针,lpWSAData返回了系统对Windows Sockets 的描述。
返回值
如果调用成功,WSAStartup 函数返回0。否则,将返回五种错误代码之一。但绝对不能使用WSAGetLastError获取错误代码。

WSAData wsa;
2     if (::WSAStartup(MAKEWORD(2,2),&wsa) != 0)
3     {
4         cout<<"WSAStartup error"<<endl;
5         return 0;
6     }

2:WSACleanup   释放Ws2_32.dl的l函数
该函数释放对Winsock链接库的调用

int WSACleanup(void);

返回值0表示正常退出,返回值SOCKET_ERROR表示异常。返回值是SOCKET_ERROR,可以调用 WSAGetLastError.查看错误代码。需要注意的是,在多线程环境下,WSACleanup 函数将终止所有线程的socket操作

3:socket   创建socket的函数

SOCKET WSAAPI socket(
2   __in          int af,
3   __in          int type,
4   __in          int protocol
5 );

指明地址簇类型,常用的地址簇如下,其余地址簇在Winsock2.h中定义。
af
AF_UNSPEC(未指明)、AF_INET(IPv4)、AF_NETBIOS(NETBIOS地址簇)、AF_INET6(IPv6)、AF_IRDA(Infrared Data Association (IrDA)地址簇)、AF_BTM(Bluetooth)。
type
SOCK_STREAM(流套接字,使用TCP协议)、SOCK_DGRAM(数据报套接字,使用UDP协议)、SOCK_RAW(原始套接字)、SOCK_RDM(提供可靠的消息数据报文,reliable message datagram)、SOCK_SEQPACKET(Provides a pseudo-stream packet based on datagrams,在UDP的基础上提供了伪流数据包)。
protocol
指明数据传输协议,该参数取决于af和type参数的类型。protocol参数在Winsock2.h and Wsrm.h定义。通常使用如下3中协议
IPPROTO_TCP(TCP协议,使用条件,af是AF_INET or AF_INET6、type是SOCK_STREAM )
IPPROTO_UDP(UDP协议,使用条件,af是AF_INET or AF_INET6、type是SOCK_DGRAM)
IPPROTO_RM(PGM(Pragmatic General Multicast,实际通用组播协议)协议,使用条件,af是AF_INET 、type是SOCK_RDM)
返回值
如果不出错,socket函数将返回socket的描述符(句柄),否则,将返回INVALID_SOCKET。

 SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if (s == INVALID_SOCKET) {
    int er = WSAGetLastError();
    return 0;
    }     

4:bind   服务端将socket与地址关联
bind函数将socket关联一个本地地址。

1 int bind(
2   __in          SOCKET s,
3   __in          const struct sockaddr* name,
4   __in          int namelen
5 ); 

s 指定一个未绑定的socket。
name 指向sockaddr地址的指针,该结构含有IP和PORT
namelen 参数name的字节数。
无错误返回0,又错误返回SOCKET_ERROR。

 1 sockaddr_in service;
 2   service.sin_family = AF_INET;
 3   service.sin_addr.s_addr = inet_addr("127.0.0.1");
 4   service.sin_port = htons(27015);
 5 
 6   //----------------------
 7   // Bind the socket.
 8   if (bind( ListenSocket, (SOCKADDR*) &service,sizeof(service)) == SOCKET_ERROR) {
 9     closesocket(ListenSocket);
10     return;
11   }

htons(host to unsigned short)和htonl(host to unsigned long)

各个机器cpu对数据存储和表示的方法不通,intel机器用littele-endian存数据,而IBM机器用big-endian存数据。网络协议为取消这种差异,一致采用big-endian方式。htons用于将unsigned short的数值从littele-endian转换为big-endian,由于short只有2字节,常用语port数值的转换。htons用于将unsigned long的数值从littele-endian转换为big-endian,long有4字节,常用于ipv4地址的转换。

1 u_short htons( __in     u_short hostshort);
2 u_long htonl(__in      u_long hostlong);

inet_addr和inet_ntoa

inet_addr用于将ipv4格式的字符串转换为unsigned long的数值。inet_ntoa用于将struct in_addr的地址转换为ipv4格式的字符串。

1 unsigned long inet_addr(  __in    const char* cp);
2 char* FAR inet_ntoa(  __in     struct   in_addr in);

INADDR_ANY
用INADDR_ANY来配置IP地址,意味着不需要知道当前服务器的IP地址。对于多网卡的服务器,INADDR_ANY允许你的服务接收一个服务器上所有网卡发来的数据。下面例子也有写,如果某个socket使用INADDR_ANY和8000端口,那么它将接收该所有网卡传来的数据。而其他socket将无法再使用8000端口。

 1     sockaddr_in service;
 2     ZeroMemory((char *)&service,sizeof(sockaddr_in));
 3     service.sin_family = AF_INET;
 4     //service.sin_addr.S_un.S_addr  =/*INADDR_ANY*/ inet_addr("127.0.0.1");
 5     service.sin_addr.s_addr  = INADDR_ANY;
 6     service.sin_port = htons(8278);
 7     if (bind(s,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR)
 8     {
 9         int er = WSAGetLastError();
10         closesocket(s);
11     }

4:listen   服务端网络监听

int listen(
  __in          SOCKET s,
  __in          int backlog
);

s socket描述符,该socket是一个未连接状态的socket
backlog 挂起连接的最大长度,如果该值设置为SOMAXCONN,负责socket的底部服务提供商将设置该值为最大合理值。并没有该值的明确规定。
返回值 没有错误发生将返回0,否则返回SOCKET_ERROR

5:accept   服务端connect接收
s listen函数用到的socket。accept函数将创建连接。
addr 指向通信层连接实体地址的指针。addr 的格式取决于bind函数内地址簇的类型。
addrlen addr的长度。
返回值 如果不发生错误,accept将返回一个新的SOCKET描述符,即新建连接的socket句柄。否则,将返回INVALID_SOCKET。传进去的addrlen应该是参数addr的长度,返回的addrlen是实际长度。

1     SOCKET ac = accept(s,NULL,NULL);
2     if (ac == INVALID_SOCKET)
3     {
4         int er = WSAGetLastError();
5         closesocket(s);
6     }

6:connect   客户端请求服务端连接

1 int connect(
2   __in          SOCKET s,
3   __in          const struct sockaddr* name,
4   __in          int namelen
5 );

s 当前未处于连接状态的socket
name 与bind函数的name参数类似,指明待连接的地址
namelen name长度
返回值 0表示正确,否则,将返回SOCKET_ERROR。如果是阻塞式的socket连接,返回值代表了连接正常与失败

1         sockaddr_in server;
2         server.sin_family = AF_INET;
3         server.sin_port = htons(8828);
4         server.sin_addr.s_addr = inet_addr("127.0.0.1");
5         if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR)
6         {
7             break;
8         }  

7:send、recv   发送接收数据

1 int send(
2   __in          SOCKET s,
3   __in          const char* buf,
4   __in          int len,
5   __in          int flags
6 );
1 int recv(
2   __in          SOCKET s,
3   __out         char* buf,
4   __in          int len,
5   __in          int flags
6 );

s 一个已经连接的socket描述符
buf 数据buffer
len 待发送(接受)数据长度
flags send(recv)函数的发送(接收)数据方式。
send:
1:MSG_DONTROUTE(告诉内核,目标主机在本地网络,不用查路由表)
2:MSG_OOB(指明发送的是带外信息)
3: MSG_DONTWAIT(将单个I/O操作设置为非阻塞模式)
4:0:与write无异
recv:
1:0:常规操作,与read()相同
2:MSG_OOB:指明发送的是带外信息
3:MSG_PEEK:可以查看可读的信息,在接收数据后不会将这些数据丢失
4:MSG_WAITALL:通知内核直到读到请求的数据字节数时,才返回。
返回值:
send的返回值标识已发送数据的长度,这个值可能比参数len小,这也意味着数据缓冲区没有全部发出去,要进行后续处理。返回SOCKET_ERROR标识send出错。
recv的返回值标识已接收数据的长度。如果连接已关闭,返回值将是0。返回SOCKET_ERROR标识recv出错。

//客户端例子
 1         sockaddr_in server;
 2         server.sin_family = AF_INET;
 3         server.sin_port = htons(8828);
 4         server.sin_addr.s_addr = inet_addr("127.0.0.1");
 5         if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR)
 6         {
 7             break;
 8         }
 9         str += "windows socket test!";
10         while (1)
11         {
12             int len = send(cnetsocket,str.c_str(),str.length(),0);
13             cout<<"send data:"<<str.c_str()<<" ,length = "<<str.length()<<endl;
14             if (len < str.length())
15             {
16                 cout<<"data send uncompleted"<<endl;
17                 str = str.substr(len+1,str.length());
18                 len = send(cnetsocket,str.c_str(),str.length(),0);
19                 cout<<"send data uncomplete,send remaining data :"<<str.c_str()<<" ,length = "<<str.length()<<endl;
20             }
21             else if (len == SOCKET_ERROR)
22             {
23                 break;
24             }
25             Sleep(1);
26         }
//服务端例子
 1         SOCKET acp = accept(listensocket,NULL,NULL);
 2         if (acp == INVALID_SOCKET)
 3         {
 4             cout<<"accept error,error code "<<WSAGetLastError()<<endl;
 5             break;
 6         }
 7         while (1)
 8         {
 9             char buf[1024];
10             int len = recv(acp,buf,1024,0);
11             if (len == 0)
12             {
13                 cout<<"connection has been closed "<<endl;
14                 break;
15             }
16             else if (len == SOCKET_ERROR)
17             {
18                 cout<<"recv error,error code "<<WSAGetLastError()<<endl;
19                 break;
20             }
21             else
22             {
23                 char* outbuf = new char[len+1];
24                 memcpy(outbuf,buf,len);
25                 outbuf[len] = 0;
26                 cout<<"recv data,"<<outbuf<<endl;
27                 delete outbuf;
28             }
29         }    

8:closesocket   关闭socket

1 int closesocket(
2   __in          SOCKET s
3 );

s 需要关闭的socket描述符
返回值 如果无错误发生,函数返回0。否则,返回SOCKET_ERROR。

9:shutdown   禁止在socket收发数据

1 int shutdown(
2   __in          SOCKET s,
3   __in          int how
4 );

s 需要操作的socket描述符
how 1:SD_RECEIVE,不允许在socket上recv数据。
2:SD_SEND,不允许在socket上send数据。
3:SD_BOTH,不允许在socket上recv和send数据。
返回值
无错误返回0,否则返回SOCKET_ERROR,错误码可被WSAGetLastError调用。
 shutdown并不关闭socket,只是禁止掉socket的recv或send行为。为保证数据收发完整性,在关闭socket之前应调用shutdown。

10 :getsockname 获取本地IP和PORT

1 int getsockname(
2   __in          SOCKET s,
3   __out         struct sockaddr* name,
4   __in_out      int* namelen
5 );

s socket文件描述符
name 指向结构体sockaddr的指针,接收socket的地址
namelen name字节数
返回值 无错误返回0,否则返回SOCKET_ERROR,错误码可被WSAGetLastError调用。

11 :getpeername 获取对端IP和PORT

1 int getpeername(
2   __in          SOCKET s,
3   __out         struct sockaddr* name,
4   __in_out      int* namelen
5 );

参考getsockname

12:ioctlsocket 控制套接口的模式。可用于任一状态的任一套接口

int ioctlsocket( int s, long cmd, u_long * argp);

s 一个标识套接口的描述符。
cmd: 对套接口s的操作命令。
argp: 指向cmd命令所带参数的指针。
返回值 成功后,ioctlsocket()返回0。否则的话,返回-1错误,应用程序可通过WSAGetLastError()获取相应错误代码。

cmd:FIONBIO:允许或禁止套接口s的非阻塞模式。
FIONREAD:确定套接口s自动读入的数据量。
SIOCATMARK:确实是否所有的带外数据都已被读入。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值