Socket与TCP状态

三次握手与四次挥手大家都比较清楚,下面是TCP状态转移图

Socket与TCP状态_第1张图片

配和下面的图更加容易理解各个状态的变化:

Socket与TCP状态_第2张图片

 

socket的创建,它就是可读、可写、可控制、可关闭的文件描述符。

        int socket(int domain,int type,int protocol);

domain参数告诉系统使用哪个底层协议族,type参数指定服务类型,protocol参数是在前两个参数构成的协议集合下,再选择一个具体的协议。 socket系统调用成功时返回一个 socket 文件描述符。

 

  socket的命名,也就是socket地址的绑定,使用bind函数:

            int bind(int sockfd,const struct sockaddr*my_addr,socklen_t  addrlen);
bind my_addr 所指的 socket 地址分配给未命名的 sockfd 文件描述 符,addrlen 参数指出该 socket地址的长度。在服务器程序中,我们通常要命名socket,因为只有命名后客户端才能知道该如何连接它。客户端则通常不需要命名socket,而是采用匿名方式,即使用操作系统自动分配的socket地址。
顺带提一下sockaddr,它是socket地址的结构体,下面是IPv4的专用结构体
struct sockaddr_in
{
        sa_family_t sin_family;/*地址族: AF_INET*/
        u_int16_t sin_port;/*端口号,要用网络字节序表示 */
        struct in_addr sin_addr;/*IPv4地址结构体 */也就是地址
};
所有专用 socket 地址 类型的变量在实际使用时都需要转化为通用socket 地址类型 sockaddr (强制转换即可)。
首先,客户端必须进入监听状态,也就是调用listen函数
        int listen(int sockfd,int backlog);
sockfd 参数指定被监听的 socket backlog参数提示内核监听队列的最大长度。好了我们监听完成,进入SYN_RCVD状态,等待链接请求。
int accept(int sockfd,struct sockaddr*addr,socklen_t*addrlen);
等待连接函数,sockfd 参数是执行过 listen 系统调用的监听socket,addr参数用来获取被接受连接的远端socket地址,该socket地址的长度由addrlen参数指出。accept只是从监听队列中取出连接,而不论连接处于何种状态。被取出后与客户端建立连接,服务端的该套接字进入 ESTABLISHED状态。
int connect(int sockfd,const struct sockaddr*serv_addr,socklen_t  addrlen);
客户端主动通过connect函数与服务端建立连接,sockfd参数由socket系统调用返回一个socket。serv_addr参数是服务器监听的socket地址,addrlen参数则指定这个地址的长度。连接成功则进入ESTABLISHED状态。
        三次握手完成,客户端程序主动connect进入SYN_SEND状态发送请求,服务端开启监听进入SYN_RECV状态,这两个状态都十分短暂。客户端connect成功后进入连接建立状态,服务端也在收到响应报文后进入连接建立状态。
下面就是四次挥手了,对于socket直接close就是四次挥手,不过,close系统调用并非总是立即关闭一个连接,而是将fd的引用计数减1。只有当fd的引用计数为0时,才真正关闭连接。你想马上关闭就用shutdown函数
int shutdown(int sockfd,int howto);
sockfd 参数是待关闭的 socket, howto 参数决定了 shutdown的行为。shutdown能够分别关闭socket上的读(SHUT_RD)或写(SHUT_WR),或者都关闭(SHUT_RDWR)。而close在关闭连接时只能将socket上的读和写同时关闭。
        都说到这了,再说一下这四次挥手吧。客户端主动关闭发出FIN报文进入FIN_WAIT1状态,服务端收到FIN后发出响应报文进入CLOSE_WAIT状态,再向客户端发出FIN报文进入LAST_WAIT。客户端收到第一个响应报文进入FIN_WAIT2状态,再收到服务端的分手消息并发出响应进入TIME_WAIT状态,2个最大传播消息服务端无消息就CLOSE,服务端收到最后的响应也CLOSE。
#include
#pragma comment(lib,"ws2_32.lib")
#include
#include
using namespace std;

int main() {
	//版本使用
	WORD wVersion = MAKEWORD(2, 1);
	//打开网络ku
	WSADATA wSockMsg;
	WSAStartup(wVersion, &wSockMsg);
	//创建socket
	SOCKET socketserver = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//ipv4专用地址
	sockaddr_in si;
	si.sin_family = AF_INET;
	si.sin_port = htons(8888);
	inet_pton(AF_INET, "127.0.0.1", (void*)&si.sin_addr.S_un.S_addr);

	bind(socketserver, (struct sockaddr*)&si, sizeof(si));

	listen(socketserver, SOMAXCONN);

	sockaddr_in clientMsg;
	int nlen = sizeof(clientMsg);
	SOCKET socketClient = accept(socketserver, (sockaddr*)&clientMsg, &nlen);


	closesocket(socketClient);
	closesocket(socketserver);
	//清理网络库
	WSACleanup();
	return 0;

}

你可能感兴趣的:(tcp/ip,网络,网络协议)