Windows Socket Programming 网络编程系列 简单客户端与服务器

摘要:
iphlpapi.h>使用namespacestd;&&书信电报;标准;wsaData.imax套接字<<//0返回0;getaddrinfouseschaarandaddrinfo.Syntaxformedrinfo;

Windows Socket 2
A socket handle can optionally be a file handle in Windows Sockets 2.
A socket handle from a Winsock provider can be used with other non-Winsock functions such as ReadFile, WriteFile, ReadFileEx, and WriteFileEx.

library: ws2_32.lib(set in Project->Linker->Input->Additional Dependencies)
header: winsock2.h(contains winsock functions, structures and definitions)
ws2tcpip.h contains new functions and structures used to retrieve ip address.
Iphlpapi.h header file is required if an application is using the IP Helper APIs.
Note: #include line for Iphlpapi.h should after the #include line for winsock2.h. windows.h is internally included in winsock2.h. So it is not necessary to include windows.h
For historical reasons, the Windows.h header defaults to including the Winsock.h header file for Windows Sockets 1.1.
So if you include windows.h file will confilt with the socket 2 defined in winsock2.h. In order to prevent this confliction, using WIN32_LEAN_AND_MEAN macro to stop windows.h define windows socket 1.1.

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <iostream>
using namespace std;
int main() {
WSADATA wsaData;
int iRet = 0;
iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iRet != 0)
{
cout<<::GetLastError()<<endl;
return 1;
}
cout<<std::hex<<wsaData.wVersion<<endl; // 202
cout<<std::hex<<wsaData.wHighVersion<<endl; // 202
cout<<std::dec<<wsaData.szDescription<<endl; // WinSock 2.0
cout<<wsaData.szSystemStatus<<endl; // Running
cout<<wsaData.iMaxSockets<<endl; // 0
return 0;
}

Both gai_strerror and WSAGetLastError can return the error code for the last operation for invoking the api in windows socket program, the later is thread-safe.

GetAddInfo is a macro for GetAddrInfoW or getaddrinfo by whether UNICODE or _UNICODE is defined. The parameter type for this macro should use TCHAR and ADDRINFOT.
GetAddrInfoW uses wchar_t and addrinfoW as its parameter; getaddrinfo uses char and addrinfo.
Syntax for getaddrinfo:

int WSAAPI getaddrinfo(
__in const char* nodename,
__in const char* servname,
__in const struct addrinfo* hints,
__out struct addrinfo** res
);
nodename is for host name or ip string(dotted-decimal address ipv4 and hex address ipv6), e.g. “127.0.0.1” for ipv4, “localhost” for host name. If it contains “localhost” or “..localmachine”, all registered addresses are returned.
servname is for the port number, e.g. “11223”.
hints is the addrinfo structure which limit the result of elements in res parameter.
resis the addrinfo structure list, which contains all elements matches the hints.
Syntax for addrinfo:
typedef struct addrinfo
{
int ai_flags; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST
int ai_family; // AF_INET(IPV4), AF_INET6(IPV6), AF_NETBIOS(NETBIOS), AF_BTM(BLUE TOOTH)…
int ai_socktype; // SOCK_STREAM(TCP), SOCK_DGRAM(UDP), SOCK_RAW(RAW)…
int ai_protocol; // 0 or IPPROTO_xxx for IPv4 and IPv6
size_t ai_addrlen; // Length of ai_addr
char * ai_canonname; // Canonical name for nodename
__field_bcount(ai_addrlen) struct sockaddr * ai_addr; // Binary address
struct addrinfo * ai_next; // Next structure in linked list
}
ADDRINFOA, *PADDRINFOA;
  • A value of AI_PASSIVE for ai_flag indicates the socket returned by bind function as Server end, set node name to be null; Otherwise, the socket returned by connect function for connection-oriented protocol(TCP), or connect, sendto or send functions for a connectionless protocol as client and set nodemane and servname as variable.
  • A value of AF_UNSPEC for ai_family indicates the caller will accept any protocol family. Be aware that AF_UNSPEC and PF_UNSPEC are the same. Note that AF_ prefix addresses are identical to PF_ prefix protocol family.
  • A value of zero for ai_socktype indicates the caller will accept any socket type.
  • A value of zero for ai_protocol indicates the caller will accept any protocol.
  • The ai_addrlen member must be set to zero.
  • The ai_canonname member must be set to NULL.
  • The ai_addr member must be set to NULL.
  • The ai_next member must be set to NULL.
Tips:
For retrieving regarding ipv4, set ai_family as AF_INET;
For retrieving TCP, set ai_socktype as SOCK_STREAM;
If ai_family and ai_socktype are specified for IPV4 or IPV6, ai_protocol should be set as IPROTO_TCP/IPROTO_UDP;
For more details about addrinfo, refer MSDN.
So it is a good manner to zero the content of addrinfo before using it.

All information returned by the GetAddrInfoW function pointed to by the ppResult parameter is dynamically allocated, call FreeAddrInfo to release the memory.
Syntax for FreeAddrInfo asci version
void freeaddrinfo(
__in struct addrinfo* ai
);
Note:This continues to free it until ai_next is null.
 
sockaddr, sockaddr_in, in_addr for ipv4;
struct sockaddr {
ushort sa_family;
char sa_data[14];
};


struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
Note: struct key word could be ignore with windows vista and later. Otherwise, should keep it.
Defferences between sockaddr and sockaddr_in
  • sa_family is address family, like “AF_XXX”, for internet, using AF_INET. sin_family is the same as sa_family. sin_family must be AF_INET.
  • If sa_family is AF_INET, sa_data in sockaddr isequal to sin_addr and sin_zero in sockaddr_in. sin_zero Padding to make structure the same size as SOCKADDR. So sockaddr could be used for other protocol; sockaddr_in just for internet protocal(TCP/UDP).
  • both of them in network byte order.
sockaddr_in6, sockaddr_in6_old, in6_addr for ipv6;
struct sockaddr_in6 {
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
struct in6_addr sin6_addr;
u_long sin6_scope_id;
};

typedef struct sockaddr_in6 SOCKADDR_IN6;
typedef struct sockaddr_in6 *PSOCKADDR_IN6;
typedef struct sockaddr_in6 FAR *LPSOCKADDR_IN6;



struct sockaddr_in6_old {
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
struct in6_addr sin6_addr;
};
sin6_family in sockaddr_in6 and sockaddr_in6_old must be AF_INET6.
SOCKADDR_STORAGE
typedef struct sockaddr_storage {
ADDRESS_FAMILY ss_family;
CHAR __ss_pad1[_SS_PAD1SIZE];
__int64 __ss_align;
CHAR __ss_pad2[_SS_PAD2SIZE];
} SOCKADDR_STORAGE, *PSOCKADDR_STORAGE;
SOCKADDR_STORAGE can support all family address like sockaddr. And it can contain either IPV4 or IPV6 protocol.
 
SOCKET
Definition
typedef UINT_PTR        SOCKET;
Initialize
SOCKET socket = INVALID_SOCKET;
INVALID_SOCKET is defined in winsock2.h as –1.
Retrieve
socket function
using the first result matched with hints passed in GetAddrInfo function in this way.
SOCKET WSAAPI socket(
_In_ int af,
_In_ int type,
_In_ int protocol
);
you can use the address result retrieving from GetAddrInfo funciton, like this.
_srvsck = socket(ret->ai_family, ret->ai_socktype, ret->ai_protocol);
Or directly define the address family, socket type and protocol type.
_srvsck = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bind function
The bind function associates a local address with a socket.
int bind(
_In_ SOCKET s,
_In_ const struct sockaddr *name,
_In_ int namelen
);
you can use the address result retrieving from GetAddrInfo funciton, like this.
iRet = bind( _srvsck, ret->ai_addr, (int)ret->ai_addrlen);
Or just directly define an SOCKADDR_IN structure and then bind it.
sockaddr_in undef;
ZeroMemory(&undef, sizeof(sockaddr_in));
undef.sin_family = AF_INET;
undef.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
undef.sin_port = ::htons(11221);
iRet = bind(_srvsck, (sockaddr*)&undef, sizeof(sockaddr_in));
if using specified address and port in the address info, should use getsockname function to get address info.
undef.sin_addr.S_un.S_addr = INADDR_ANY;
iRet = bind(_srvsck, (sockaddr*)&undef, sizeof(sockaddr_in));
sockaddr_in _info;
ZeroMemory(&_info, sizeof(sockaddr_in));
int _len = sizeof(sockaddr_in);
iRet = ::getsockname(_srvsck, (sockaddr*)&_info, &_len);
if (iRet == SOCKET_ERROR)
{
iRet = WSAGetLastError();
wcout<<"Failed to getsockname:. (ErrCode: "<<iRet<<")."<<endl;
}
else
{
cout<<"length of sock address:"<<_len<<endl;
char * _paddr = ::inet_ntoa(_info.sin_addr);
if (_paddr != nullptr)
cout<<_paddr<<endl;
else
cout<<"empty ip address"<<endl;
int _port = _info.sin_port;
_port = ::htons(_info.sin_port);
cout<<"port:"<<_port<<endl;
}

listen function
The listen function places a socket in a state in which it is listening for an incoming connection.
int listen(
_In_ SOCKET s,
_In_ int backlog
);
If set backlog to SOMAXCONN, the underlying service provider responsible for socket s will set the backlog to a maximum reasonable value. If a connection request arrives and the queue is full, the client will receive an error with an indication of WSAECONNREFUSED. If the listen function is called on an already listening socket, it will return success without changing the value for the backlog parameter.
accept function
The accept function permits an incoming connection attempt on a socket.
SOCKET cltSck = INVALID_SOCKET;
SOCKADDR_IN cltAddr;
int cltAddrLen = sizeof(SOCKADDR_IN);
cltSck = accept(_srvsck, (SOCKADDR*)&cltAddr, &cltAddrLen);
if (cltSck == INVALID_SOCKET)
{
iRet = ::WSAGetLastError();
wcout<<"Failed to accept: "<<gai_strerror(iRet)<<" (ErrCode: "<<iRet<<")."<<endl;
break;
}
The accept function is used with connection-oriented socket types such as SOCK_STREAM. Set the second and third argument as nullptr, then no information about the remote address of the accepted socket is returned.
connect function
The connect function establishes a connection to a specified socket.
ADDRINFOT *res = nullptr, hints;
ZeroMemory(&hints, sizeof(ADDRINFOT));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

iRet = GetAddrInfo(_straddr.c_str(), _strport.c_str(), &hints, &res);
_cltSck = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
iRet = connect(_cltSck, res->ai_addr, res->ai_addrlen);

recvfrom function
The recvfrom function receives a datagram and stores the source address.
iRet = recvfrom(cltSck, (char*)msg, sizeof(TCHAR)*MAX_PATH, 0, (PSOCKADDR)&senderSck, &senderSckSize);
If no error occurs, recvfrom returns the number of bytes received. If the connection has been gracefully closed, the return value is zero. Otherwise, a value of SOCKET_ERROR is returned.
send function
The send function is used to write outgoing data on a connected socket.
send(cltSck, (char*)sendmsg, (::_tcslen(sendmsg) + 1) * 2, 0);

For complete code for server and client, please click the link.

APPENDIX

Winsock Kernel Socket Categories

Basic Sockets
Basic sockets are used only to get and set transport stack socket options or to perform socket I/O control operations. Basic sockets cannot be bound to a local transport address and do not support sending or receiving network data.
Listening Sockets
Listening sockets are used to listen for incoming connections from remote transport addresses. The functionality of a listening socket includes all of the functionality of a basic socket.
Datagram Sockets
Datagram sockets are used to send and receive datagrams. The functionality of a datagram socket includes all of the functionality of a basic socket.
Connection-Oriented Sockets
Connection-oriented sockets are used to send and receive network data over established connections. The functionality of a connection-oriented socket includes all of the functionality of a basic socket.

AF_INET and AF_INET6

 AF_INETAF_INET6
Socket Address StructureSOCKADDR_INSOCKADDR_IN6
Socket TypeSOCK_STREAM
SOCK_DGRAM
SOCK_RAW
SOCK_STREAM
SOCK_DGRAM
SOCK_RAW
Combinations
Basic Sockets
SOCK_STREAM + IPPROTO_TCP
SOCK_DGRAM + IPPROTO_UDP
SOCK_RAW + IPPROTO_XXX
Listening Sockets
SOCK_STREAM + IPPROTO_TCP
Datagram Sockets
SOCK_DGRAM + IPPROTO_UDP
SOCK_RAW + IPPROTO_XXX
Connection-Oriented Sockets
SOCK_STREAM + IPPROTO_TCP
Same as AF_INET

System supported socket catalog

On Windows XP and later, the following command can be used to list the Windows Sockets catalog to determine the service providers installed and the address family, socket type, and protocols that are supported.
netsh winsock show catalog

免责声明:文章转载自《Windows Socket Programming 网络编程系列 简单客户端与服务器》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Windows7旗舰版磁盘分区详解—附分区步骤截图EIGRP 超强总结下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

内核如何启动根文件系统?

当u-boot開始运行bootcmd命令,就进入Linux内核启动阶段。与u-boot类似,普通Linux内核的启动过程也能够分为两个阶段,但针对压缩了的内核如uImage就要包含内核自解压过程了。本文以linux-2.6.37版源代码为例分三个阶段来描写叙述内核启动全过程。第一阶段为内核自解压过程,第二阶段主要工作是设置ARM处理器工作模式、使能MMU、...

【LinearGradientBrush】线性渐变笔刷

本示例演示如何使用 LinearGradientBrush 类来绘制带有线性渐变的区域。在下面的示例中,Rectangle 的Fill 是用从黄色依次过渡到红色、蓝色和浅绿色的对角线性渐变来绘制的。 XAML <!-- This rectangle is painted with a diagonal linear gradient. -->...

怎么在vue中引入layui

新项目想用layui框架,学习了把前辈是怎么引入layui的,这里记录下 1.index.html要引入layui.js文件 <script src="/static/layui/layui.js" type="text/javascript" charset="utf-8"></script> 2.main.js文件要配置好lay...

WPF数据绑定(四)

1、DataTemplate 上一部分已经讲了itemsource绑定,功能虽然实现了但是还是有点土,内容太单一了,如果能够修改listbox的界面,让更多的元素展示出来就完美了。 DataTemplate就可以实现这个。 1 <TabItem Header="DataTemplate"> 2...

springboot中使用h2数据库(内存模式)

使用H2的优点,不需要装有服务端和客户端,在项目中包含一个jar即可,加上初始化的SQL就可以使用数据库了 在springboot中引入,我的版本是2.1.4,里面就包含有h2的版本控制 <!-- 集成h2数据库 --> <dependency> <groupId>c...

【深度学习系列】用Tensorflow实现经典CNN网络Vgg

  上周我们讲了经典CNN网络AlexNet对图像分类的效果,2014年,在AlexNet出来的两年后,牛津大学提出了Vgg网络,并在ILSVRC 2014中的classification项目的比赛中取得了第2名的成绩(第一名是GoogLeNet,也是同年提出的)。在论文《Very Deep Convolutional Networks for Large...