socket网络编程基础

IP地址结果

在winsock中,APP通过SOCKADDR_IN结构来指定IP和Port信息,
其中的sin_zero只充当填充项,以使SOCKADDR_IN结构和SOCKADDR结构的长度一样。
unsigned long inet_addr(const char FAR* cp)用于将一个点分IP转换成一个32位无符号长整数(按网络字节顺序)。

字节顺序

在computer中把IP & Port指定成多字节数时,是按主机字节(host-byte)顺序进行的,
但在网络上指定IP & Port,标准指定必须使用big-endian即网络字节(network-byte)顺序表示。

主机字节顺序转成网络字节顺序:

u_long WSAAPI htonl( _In_ u_long hostlong);
int WSAAPI WSAHtonl(_In_  SOCKET s,
  _In_  u_long hostlong,
  _Out_ u_long *lpnetlong
);
u_short WSAAPI htons(_In_ u_short hostshort);
int WSAAPI WSAHtons(_In_  SOCKET  s,
  _In_  u_short hostshort,
  _Out_ u_short *lpnetshort
);

网络字节顺序转主机字节顺序:

u_long WSAAPI ntohl(_In_ u_long netlong);
int WSAAPI WSANtohl(_In_  SOCKET s,
  _In_  u_long netlong,
  _Out_ u_long *lphostlong
);
u_short WSAAPI ntohs(_In_ u_short netshort);
int WSAAPI WSANtohs(_In_  SOCKET  s,
  _In_  u_short netshort,
  _Out_ u_short *lphostshort
);

IP地址和主机名

IP地址不便于记忆,通常都使用主机名,使用如下这些地址&名称解析函数,
可以将主机名(如www.somewebsite.com)解析为IP地址、服务名称(如FTP)和端口号:
getaddrinfo,
getnameinfo,
gethostbyaddr,
gethostbyname,
gethostname,
getprotobyname,
getprotobynumber,
getservbyname,
getservbyport等,
同时还有这些函数对应的异步版本,如:
WSAAsyncGetHostByAddr,
WSAAsyncGetHostByName,
WSAAsyncGetProtoByName,
WSAAsyncGetProtoByNumber,
WSAAsyncGetServByName,
WSAAsyncGetServByPort等

创建socket

有两个函数可以创建套接字:

SOCKET WSAAPI socket(
  _In_ int af,//IPv4为AF_INET
  _In_ int type,//tcp为SOCK_STREAM,udp为SOCK_DGRAM
  _In_ int protocol//tcp为IPPROTO_TCP,udp为IPPROTO_UDP
);
SOCKET WSASocket(
  _In_ int                af,
  _In_ int                type,
  _In_ int                protocol,
  _In_ LPWSAPROTOCOL_INFO lpProtocolInfo,
  _In_ GROUP              g,
  _In_ DWORD              dwFlags
);
//为控制套接字的选项和行为,提供了4个API:
setsockopt
getsockopt
ioctlsocket
WSAIoctl

绑定

int bind(
  _In_ SOCKET                s,
  _In_ const struct sockaddr *name,
  _In_ int                   namelen
);
//最常见的错误是WSAEADDRINUSE:如果使用的是tcp/ip,那么表示该IP&Port已经被占用了,或者该IP&Port处于TIME_WAIT状态
//对一个已经bind的套接字调用bind,将返回WSAEFAULT错误

监听

int listen(
  _In_ SOCKET s,
  _In_ int    backlog//队列长度,超出队列时请求将被拒绝,连接段收到WSAECONNREFUSED错误
);
//最常见的错误是WSAEINVAL,表示在调用listen之前没有调用bind
//另外listen也可能接受到WSAEADDRINUSE错误,该错误通常发生在bind调用

接受连接

接受连接的API:accept, WSAAccept, AcceptEx.

SOCKET accept(
  _In_    SOCKET          s,//bindsocket
  _Out_   struct sockaddr *addr,
  _Inout_ int             *addrlen
);
//当监听套接字为异步或者非阻塞模式,并且没有连接被接受时,最常见的错误是WSAEWOULDBLOCK

服务器端过程

//1.初始化winsock
WSAStartup(MAKEWORD(2,2), &wsaData);
//2.创建监听套接字
lstSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//3.建立SOCKADDR_IN地址结构
SOCKADDR_IN srvAddr;
srvAddr.sin_faminly = AF_INET;
srvAddr.sin_port = htons(6188);
//4.绑定
bind(lstSocket, (SOCKADDR*)&srvAddr, sizeof(srvAddr));
//5.监听客户端连接
listen(lstSocket, 5);
//6.接受新连接(通常循环接受多个连接)
clientSocket = accept(lstSocket, (SOCKADDR*)&clientAddr, &clientAddrLen);
//7.在clientSocket上收发数据
//8.使用完关闭clientSocket
closesocket(clientSocket);
//9.关闭lstSocket
closesocket(lstSocket);
//10.释放
WSACleanup();

客户端过程

1)创建socket
2)建立SOCKADDR地址结构,指定服务器IP&Port
3)调用connect, WSAConnect或者ConnectEx建立客户端和服务器的连接

int connect(
  _In_ SOCKET                s,
  _In_ const struct sockaddr *name,
  _In_ int                   namelen
);
//常见错误

客户端的完整过程如下:

//1.初始化winsock
WSAStartup(MAKEWORD(2,2), &wsaData);
//2.创建客户端套接字
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//3.建立SOCKADDR_IN地址结构,用于连接服务器
SOCKADDR_IN srvAddr;
srvAddr.sin_faminly = AF_INET;
srvAddr.sin_port = htons(6188);
srvAddr.sin_addr.s_addr = inet_addr("136.149.3.28");
//4.用套接字创建一个到服务器的连接
connect(s, (SOCKADDR*)&srvAddr, sizeof(srvAddr));
//5.使用套接字s收发数据
//6.使用完后,关闭套接字
closesocket(s);
//7.清理
WSACleanup();

TCP状态

1)对于每个套接字而言,它的初始状态都是CLOSED.

2)若服务器套接字同本地IP&Port绑定起来,并在它上面进行监听,那么套接字的状态就是LISTEN状态.

3)若客户机初始化了一个连接,就会向服务器发送一个SYN包,同时将客户机套接字状态置为SYN_SENT.

4)服务器收到SYN包后,会发送一个SYN_ACK包响应(如果服务器一直不发送SYN_ACK包,客户机就会超时,并返回CLOSED状体),服务器的套接字状态变为SYN_RCVD;

5)客户机需要用一个ACK包对它(SYN_ACK包)进行响应,此时客户机的套接字将处于ESTABLISHED状态;这个ACK包将服务器套接字的状态变成ESTABLISHED.

6) 14…

数据传输

所有收发数据的缓冲区都属于简单的char类型(面向字节的数据),它可以包含任何原始数据,这些原始数据是二进制,还是字符型,是无关紧要的。
发送:send/WSASend
接收:recv/WSARecv
它们出错返回值都是SOCKET_ERROR,最常见的错误是WSAECONNABORTED和WSAECONNRESET,两者都和正在被关闭相关(要么由于超时被关闭,要么由于通信方正在关闭连接). 另一个常见的错误是WSAEWOULDBLOCK,一般出现在非阻塞模式或异步状态时,表示函数暂时不能完成。

你可能感兴趣的:(socket网络编程基础)