在谢希仁的《计算机网络》一书中,详尽的学习了网络协议七层塔。也明白了在TCP/IP协议中,可靠性是由传输层来保证的,而传输层的两大协议UDP与TCP,都是在基于网络层IP协议的基础上首次提供端到端的通信。其中,UDP是使用数据报提供服务的,而TCP则提供可靠的流服务。UDP提供的是不可靠的服务(相当于篮球运动员,直接将球扔向篮筐,不关注结果),TCP提供的是可靠的服务(投球不中,将一直投,知道投中为止)。在网络传输过程中,TCP的可靠性很大部分是由重传与确认机制来保证的。要了解更多的知识可以看我的下一篇文章《Windows Sockets网络编程(1)TCP select & thread》、也可以去关注一下《Windows Sockets网络编程(2)TCP Stream拆分、拼接》了解更多TCP流分割知识。
目录:
“谁忽视了TCP,谁注定要推倒重来”——FTP创始人James Van Bokkelen.
How to create a tcp connection
client
WSAStartup
这个函数主要是用来加载Socket库,俩参数,
WSAStartup(
_In_ WORD wVersionRequired,
_Out_ LPWSADATA lpWSAData);
wVersionRequired,指明需要的Socket版本;一般使用宏MAKEWORD(2,2)。
lpWSAData,是一个输出变量,指系统实际返回的库信息。
SOCKET _tcp_socket;
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0){
perror("WSASartup error !");
}
socket
创建socket,函数原型是这样滴,
socket (
_In_ int af,
_In_ int type,
_In_ int protocol);
- af 协议族,常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域Socket)、AF_ROUTE等。
- type Socket类型,常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。
- TCP ,流式Socket(SOCK_STREAM)是一种面向连接的Socket,针对于面向连接的TCP服务应用。
- UDP,数据报式Socket(SOCK_DGRAM)是一种无连接的Socket,对应于无连接的UDP服务应用。
- protocol 指定协议,常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
注意:type和protocol不可以随意组合,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当第三个参数为0时,会自动选择第二个参数类型对应的默认协议。
_tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_tcp_socket == INVALID_SOCKET){
perror("socket error !");
WSACleanup();
}
sockaddr_in
我更喜欢将变量定义为xxx_config,因为在我看来,这很大程度上就相当于一个配置文件,
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
- sin_family 地址家族,通常大多用的是都是AF_INET,代表TCP/IP协议族。
- sin_port 端口,这里必须采用网络数据格式,
- htons ,将端口号由主机字节序转换为网络字节序的整数值。
- sin_addr 存储IP地址,这里服务端和客户端是有所区别的。
- inet_addr 客户端使用,将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。
- htonl(INADDR_ANY) 服务端使用,INADDR_ANY表示任何IP地址。
SOCKADDR_IN remote_config;
remote_config.sin_port = htons(port);
remote_config.sin_family = AF_INET;
remote_config.sin_addr.S_un.S_addr = inet_addr(addr);
connect
TCP客户端使用它,通过调用connect建立一个连接(虚电路),
int PASCAL FAR connect (
_In_ SOCKET s,
_In_reads_bytes_(namelen) const struct sockaddr FAR *name,
_In_ int namelen);
无需赘言,按照以下方式调用。如果调用失败(SOCKET_ERROR),不要重新调用connect,最好closesocket然后socket重新得到一个新的,继而进行connect。
如果客户端通过WSAGetLastError获得返回值(10061),可能是,
- 服务端没有运行。
- IP端口设置有误。
另外,一般UDP客户端不需要调用调用connect,但也并不是不可以。这里,如果调用两次及以上次数connect函数,将造成(10048)错误。