windows socket编程总结

windows的网络编程接口没有像linux那么丰富,功能也要少很多,下面针对几个主要的接口做一下介绍:

1.  int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);

这个函数比较简单,目的是初始化socket,所有其他socket接口调用之前都要调用这个接口,调用成功会返回0,调用失败返回非0值。

例:

    WSADATA wsaData;
    if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
    {                               */
        printf("WSAStartup failed with error: %d\n", err);
        return 1;
    }


2. int WSACleanup(void);

此函数与 WSAStartup 相对,是释放socket库资源,到所有的socket函数后面使用,一般在程序结束的地方调用。


3. SOCKET socket(int af,int type,int protocol);

此函数为创建一个SOCKET对象,针对TCP和UDP传入的参数会有点不一样,调用失败会返回INVALID_SOCKET。

例:TCP:SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

UDP:SOCKET s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);


4. int connect(SOCKET s, const  struct  sockaddr*name, intnamelen); 

函数调用失败返回SOCKET_ERROR.

此函数在TCP中的使用:

a.此函数调用成功客户端与服务器之间就建立了一条链接。

b.若TCP客户没有收到SYN分节的响应,则返回ETIMEOUT错误。

c.若TCP客户收到RST响应,则返回ECONNREFUSED错误。

d.若TCP客户connect失败,则必须关闭socket重新创建以后才能再次调用connect函数。

e.调用此函数的TCP状态变化:CLOSED->SYN_SENT->ESTABLISHED。

阻塞模式:

(1)如果TCP服务端没有打开,则connect会立马返回SOCKET_ERROR(-1),调用WSAGetLastError会返回WSAECONNREFUSED(10061)错误。

(2)如果TCP服务端运行正常(服务端必须调用监听函数(listen)以后,TCP/IP内核才会接收TCP客户端的连接),则connect会立马返回0,表示连接成功。

非阻塞模式:

(1)如果TCP服务端没有打开,则connect会立马返回SOCKET_ERROR(-1),调用WSAGetLastError会返回WSAEWOULDBLOCK(10035)错误。

(2)如果TCP服务端运行正常,则connect会立马返回SOCKET_ERROR(-1),调用WSAGetLastError会返回WSAEWOULDBLOCK(10035)错误。

由以上可知,如果将socket设置为非阻塞,通过返回值是没有办法判断connect是否连接成功此时要判断connect是否调用成功,需要先调用select,发现描述符变成可写以后,则connect成功,否则失败。



例.阻塞模式调用:

    struct sockaddr_in clientService;
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
    clientService.sin_port = htons(27015);
    // Connect to server.    
    connect(s, (SOCKADDR *) & clientService, sizeof (clientService));
非阻塞模式调用:
	FD_SET mask;
	u_long value=1;
	TIMEVAL timeout;
       struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(27015);
serveraddr.sin_addr.s_addr =
inet_addr("127.0.0.1");
	ioctlsocket(sSocket,FIONBIO,&value);//设置为非阻塞
	connect(sSocket,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
	timeout.tv_sec=2;
	timeout.tv_usec=0;
	FD_ZERO(&mask);
	FD_SET(sSocket,&mask);
	value=select(NULL,NULL,&mask,NULL,&timeout);
	if(value && value!=SOCKET_ERROR)
			//连接成功
		value = 0;
		ioctlsocket(sSocket,FIONBIO,&value);//设置为阻塞
	}
	else
	{
		shutdown(sSocket,SD_BOTH);
		closesocket(sSocket);
		sSocket = 0;
	}


此函数在UDP中的使用:

a.UDP中也能调用此函数,但是并没有建立一条真正的链接,只是限定UDP的通信对端的IP和端口。由于UDP有了对端的IP和端口,因此发送和接收数据的时候也可以使用send和recv函数,后面会介绍这2个函数。


5. SOCKET accept(SOCKET s,struct  sockaddr  *addr,int  *addrlen);

此函数为TCP服务器用来接收TCP客户端的连接的。

如果将 s 监听的socket设置阻塞模式的,此函数会一直阻塞到与客户端连接到来为止。

如果将 s 监听的socket设置为非阻塞模式,此函数调用失败会返回INVALID_SOCKET,如果调用WSAGetLastError函数返回WSAEWOULDBLOCK(10035)表示还没有TCP客户端来连接TCP服务器。

如果监听的 s 为非阻塞模式,则收到的socket也为非阻塞模式。

如果监听的 s 为阻塞模式,则收到的socket也为阻塞模式。


例:阻塞模式:SOCKET sockClient=accept(sockListen,NULL,NULL);//此函数返回表示已经有客户端连接到来,所以sockClient不会为INVALID_SOCKET.

非阻塞模式:SOCKETsockClient=accept(sockListen,NULL,NULL);
if(INVALID_SOCKET==sockClient)//表示此次调用没有客户端连接到来
{
printf("TCP_Server accept failed!\n");
continue;
}


6.int listen(SOCKETs,intbacklog);

此函数为TCP服务器用来将 SOCKET s由主动模式变为被动模式。 backlog 为TCP协议栈中已经完成3次握手和正在进行3次握手的连接数。比如通常将backlog设置成5,则最多同一时间只能有5个客户端连接过来。等其中有一连接已经被accept接口取出以后,又可以有新的客户端连接进来。

函数调用失败返回:SOCKET_ERROR。

例:if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR)

        printf("listen function failed with error: %d\n", WSAGetLastError());


7.int bind(SOCKETs,const struct sockaddr*name,intnamelen);

此函数为 SOCKET s 关联一个本地地址。调用失败,返回SOCKET_ERROR。

如果不调用bind函数,当调用connect(TCP客户端)和listen(TCP服务端)时,内核就要为相应的套接字选择一个临时端口。指定端口0为未指定端口。bind被调用时选择一个临时端口,若未指定IP地址(INADDR_ANY),则内核将等到套接字已经连接(TCP)或已在套接字上发出数据报(UDP)时选择IP。例:

    struct service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr("127.0.0.1");
    service.sin_port = htons(27015);
    // Bind the socket.
    iResult = bind(ListenSocket, (SOCKADDR *) &service, sizeof (service));
    if (iResult == SOCKET_ERROR) {}

8.intsendto(SOCKET s,const char  * buf,int len,int flags,const struct sockaddr * to,int tolen);

int recvfrom(SOCKET s,char * buf,int len,int flags,struct sockaddr * from,int * fromlen);

int send(SOCKET s,const char * buf,int len,int flags);

阻塞模式:

(1)如果对端没有接收,并且TCP/IP缓冲区也已经满,则send函数会阻塞,直到有缓冲区存放为止。

(2)如果对端关闭socket,则send函数返回SOCKET_ERROR(-1),调用WSAGetLastError函数返回WSAECONNABORTED(10053)或者WSAECONNRESET(10054)错误。

非阻塞模式:

(1)如果对端没有接收,并且TCP/IP缓冲区也已经满,则send函数会返回SOCKET_ERROR(-1),调用WSAGetLastError函数返回WSAEWOULDBLOCK(10035)错误。

(2)如果对端关闭socket,则send函数返回SOCKET_ERROR(-1),调用WSAGetLastError函数返回WSAECONNABORTED(10053)或者WSAECONNRESET(10054)错误。



int recv(SOCKET s,char * buf,int len,int flags);

阻塞模式:

(1)recv会一直等待,如果有数据到来则接收数据,返回接收的数据大小。

(2)recv会一直等待,如果对端关闭了socket,则recv会返回0.

非阻塞模式:

(1)recv会立马返回,如果没有任何数据能接收,则返回SOCKET_ERROR(-1),调用WSAGetLastError函数则返回WSAEWOULDBLOCK(10035)错误。

(2)recv会立马返回,如果对端关闭了socket,则recv会返回0.

(3)recv会立马返回,如果TCP有数据接收,则直接返回接收到的数据大小。


以上4个函数我TCP和UDP用来接收和发送数据的,在调用这几个接口之前可以调用ioctlsocket 函数将这几个函数变成非阻塞式的。

返回值为真实发送和接收的数据大小。


9.intshutdown(SOCKET s,int how);

此函数为关闭连接的接收部分或者发送部分或者发送接收都关闭,函数调用失败返回:SOCKET_ERROR。

how参数对应下表:

Value Meaning
SD_RECEIVE 0

关闭接收这一部分

SD_SEND 1

关闭发送这一部分

SD_BOTH 2

关闭发送和接收


10. int closesocket(SOCKET s);

默认行为:把套接字标记为已关闭,然后返回到调用进程,TCP发送完已经排队的数据,发送完毕以后发生正常的TCP 4分组终止序列,减少对应套接字的一个引用计.关闭发送和接收。可以通过调用setsocketopt函数来改变这个默认行为。函数调用失败返回:SOCKET_ERROR。


11.int getsockname(SOCKET s,struct sockaddr * name,int * namelen);
int getpeername(SOCKET s,struct sockaddr * name,int * namelen);

此函数的作用:查看内核选择的临时端口和IP地址,返回与套接字有关的本地协议地址和外地协议地址,TCP客户没有调用bind就直接调用connect,用getsockname来返回内核赋予该连接的本地IP地址和本地端口号.函数调用失败返回:SOCKET_ERROR.


12.//主机字节序和网络字节序的转换
u_short htons(u_short hostshort);
u_long htonl(u_long hostlong);
u_short ntohs(u_short netshort);
u_long ntohl(u_long netlong);


13.IPV4字符串和整形(为网络字节序)之间的转换
unsigned long inet_addr(const char * cp);
char  * inet_ntoa(struct in_addr in);


14 . int ioctlsocket(SOCKET s,long cmd,u_long FAR* argp); 
 此函数为设置connect,accept,recv,recvfrom是否为阻塞模式。如果调用失败,返回:SOCKET_ERROR 。

例:
u_long mode = 0;
ioctlsocket(s,FIONBIO,&mode);
控制为阻塞方式。

u_long mode = 1;
ioctlsocket(s,FIONBIO,&mode);
控制为非阻塞方式。 


14.设置获取获取socket的配置项。

int getsockopt(SOCKET s,int level,int optname,char * optval,int * optlen);
int setsockopt(SOCKET s,int level,int optname,const char * optval,int optlen);

由于这2个函数的参数输入情况比较多,所以要想知道具体使用,请参考一下链接: SetSockOpt使用


15.int select( int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout );

The select function returns the total number of socket handles that are ready and contained in the fd_set structures, zero if the time limit expired, or SOCKET_ERROR if an error occurred. If the return value is SOCKET_ERROR, WSAGetLastError can be used to retrieve a specific error code.

Error codeMeaning
WSANOTINITIALISEDA successful WSAStartup call must occur before using this function.
WSAEFAULTThe Windows Sockets implementation was unable to allocate needed resources for its internal operations, or the readfds, writefds, exceptfds, or timeval parameters are not part of the user address space.
WSAENETDOWNThe network subsystem has failed.
WSAEINVALThe time-out value is not valid, or all three descriptor parameters were NULL.
WSAEINTRA blocking Windows Socket 1.1 call was canceled through WSACancelBlockingCall.
WSAEINPROGRESSA blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.
WSAENOTSOCKOne of the descriptor sets contains an entry that is not a socket.

readfds:

  • If listen has been called and a connection is pending, accept will succeed.
  • Data is available for reading (includes OOB data if SO_OOBINLINE is enabled).
  • Connection has been closed/reset/terminated.

writefds:

  • If processing a connect call (nonblocking), connection has succeeded.
  • Data can be sent.

exceptfds:

  • If processing a connect call (nonblocking), connection attempt failed.
  • OOB data is available for reading (only if SO_OOBINLINE is disabled).


对TCP和UDP的一些总结:

TCP是基于流模式的,发送端经过多次发送的数据,接收端也许一次就能全部接收。

UDP是基于报式的,发送端每次发送了多大的数据包,接收端就会接收到多大的数据包,如果因为发送端发送的某个数据包太大,接收端的数据缓冲区太小,则recvfrom函数会返回-1。表示此函数调用失败。所以建议UDP接收缓冲区要设置的大一点。防止出现以上错误。


下面是我写的一个TCP和UDP通信demo,里面测试了以上的部分接口,有些接口调用被注释了,要测试的话要去掉注释。

TCP和UDP通信demo















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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值