标签:启动失败 mamicode type 数据压缩 bsp soc accept 连接失败 访问
问题来源:面试中面试官会看到你的简历上写着熟悉网络、http、tcp协议等,那你真的了解他吗?今天它来了
一、网络协议:
说说TCP三次握手的过程?
第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
TCP的报文格式是怎么样的?
重要字段:
序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:
为什么要三次握手,两次不可以吗?
为了实现可靠数据传输,TCP协议的通信双方都必须维护一个序列号,以标识发送出去的数据包中,哪些是已经被对方收到的。三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。如果只是两次握手,至多只有连接发起方的起始序列号能被确认,另一方选择的序列号则得不到确认.
如果已经建立了连接,但是客户端突然出现故障了怎么办?
客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务端有一个保活计时器,每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,就关闭连接。
再说说TCP的四次挥手的过程?
由于TCP连接时全双工的,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
为什么要四次挥手,三次不可以吗?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
三、不使用API手动实现一个http的类:
服务端步骤:
|- WSAStartup函数初始化
|- 创建Socket
|- 用bind指定对象
|- listen设置监听
|- accept接收请求
|- send发送会话
|- closesocket关闭socket
客户端步骤:
|- WSAStartup函数初始化
|- 创建Socket
|- connect请求连接
|- send发送会话
|- closesocket关闭socket
服务端代码:
SOCKET clientSocket=socket(AF_INET,SOCK_STREAM,0); int send_len = 0; int recv_len = 0; //定义服务端套接字,接受请求套接字 SOCKET s_server; //服务端地址客户端地址 SOCKADDR_IN server_addr; //初始化套接字库 WSADATA wsadata; WSAStartup(0x22, &wsadata); //填充服务端地址信息 //填充服务端信息 server_addr.sin_family = AF_INET; server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); server_addr.sin_port = htons(80); std::string msgstr=""; //创建套接字 s_server = socket(AF_INET, SOCK_STREAM, 0); int result = bind(s_server,(struct sockaddr *) &server_addr,sizeof(server_addr)); if (result == SOCKET_ERROR) /* 绑定失败 */ { closesocket(s_server); printf("[Web] Fail to bind, error = %d\n", WSAGetLastError()); return -1; } if (listen(s_server,SOMAXCONN)==-1) { int err = GetLastError(); std::cout << "服务器启动失败!" << std::endl; std::cout << err << std::endl; } else { std::cout << "服务器启动成功!" << std::endl; } while(1){ SOCKADDR_IN from_addr; /* 客户端地址 */ socklen_t from_len = sizeof(from_addr); std::string reponse_data="404"; SOCKET acpt_soc = accept(s_server,(struct sockaddr *) &from_addr,&from_len); if (acpt_soc == INVALID_SOCKET) /* 接受失败 */ { int err = GetLastError(); printf("接收失败1", WSAGetLastError()); std::cout << err << std::endl; break; } char recv_buf [1025] = ""; recv_len = recv(acpt_soc,recv_buf,1025, 0); if (recv_len < 0) { std::cout << "接收失败2!" << std::endl; }else{ std::cout << "接收成功!" << std::endl; std::cout << recv_buf << std::endl; } send_len = send(acpt_soc,recv_buf,strlen(recv_buf), 0); if (send_len < 0) { std::cout << "发送失败!" << std::endl; closesocket(acpt_soc); }else{ std::cout << "发送成功!" << std::endl; } } //关闭套接字 closesocket(s_server); //释放DLL资源 WSACleanup();
客户端代码:
SOCKET clientSocket=socket(AF_INET,SOCK_STREAM,0); //printf("客户端嵌套字已经打开!\n"); int send_len = 0; int recv_len = 0; //定义服务端套接字,接受请求套接字 SOCKET s_server; //服务端地址客户端地址 SOCKADDR_IN server_addr; //初始化套接字库 WSADATA wsadata; WSAStartup(0x22, &wsadata); //填充服务端地址信息 //填充服务端信息 server_addr.sin_family = AF_INET; server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); server_addr.sin_port = htons(12580); string msgstr=""; //创建套接字 s_server = socket(AF_INET, SOCK_STREAM, 0); int nTimeout = 120000; //设置接收超时为1000ms if (SOCKET_ERROR == setsockopt(s_server, SOL_SOCKET, SO_RCVTIMEO, (char *)&nTimeout, sizeof(int))) { fprintf(stderr, "Set SO_RCVTIMEO error !\n"); } if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) { cout << "服务器连接失败!" << endl; closesocket(s_server); //释放DLL资源 WSACleanup(); return 0; } else { cout << "服务器连接成功!" << endl; } //发送,接收数据 cout << "开始传输桌面信息:"<< endl; while(1){ char send_buf [1025] = "111"; send_len = send(s_server,send_buf,strlen(send_buf), 0); if (send_len < 0) { cout << "发送失败!" << endl; closesocket(s_server); //释放DLL资源 WSACleanup(); return 0; }else{ cout << "发送成功!" << endl; } char recv_buf [1025] = ""; recv_len = recv(s_server,recv_buf,1025, 0); if (recv_len < 0) { cout << "接收失败!" << endl; closesocket(s_server); //释放DLL资源 WSACleanup(); return 0; }else{ cout <<recv_buf<< endl; cout << "接收成功!" << endl; } } //关闭套接字 closesocket(s_server); //释放DLL资源 WSACleanup();
欢迎大家留言交流,一块成长进步。
标签:启动失败 mamicode type 数据压缩 bsp soc accept 连接失败 访问
原文地址:https://www.cnblogs.com/xupeidong/p/12335525.html