您的位置:首页 > 理论基础 > 计算机网络

socket 建立连接与三次握手的对应关系

2017-08-04 11:44 423 查看

Socket 与 TCP 协议

socket 用于网络编程,通过 TCP/UDP 协议建立通信。而面试中也常常问及 Socket 和三次握手的对应关系。一般人都认为是这样的关系。(最初也是这样设计的)



connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。


SYN Flood 攻击

但是实际上,出于安全考虑,为了避免太过容易的 SYN Flood 攻击 ,客户端仅仅发出大量的 socket 连接请求,而服务器需要分配太多的 socket ,导致资源耗尽 。

实际上在 accept() 函数之前,已经完成了三次握手过程,这在一定程度上可以减少 SYN Flood 攻击。

实验测试

通过模拟 socket 建立连接的代码(代码在文末),模拟了简单的 socket 连接过程,同时启动 wireshark ,进行抓包,设置过滤规则 tcp.port = 7000 。

这里先在服务器端的 accept () 函数之前,增加了 while(1){}; 死循环,即不执行 accept() 函数。

在启动服务器和客户端后,抓取到的报文如下,可以看到,已经完成了三次握手。



在关闭客户端 socket 之后,仅仅进行了两次握手,因为服务端并没有执行 accept 函数分配 socket.



socket 通信代码

//服务器端代码
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <thread>
#include <iostream>
#define PORT 7000
#define QUEUE 20
int conn;
void thread_task() {
}

int main() {
//printf("%d\n",AF_INET);
//printf("%d\n",SOCK_STREAM);
int ss = socket(AF_INET, SOCK_STREAM, 0);
//printf("%d\n",ss);
struct sockaddr_in server_sockaddr;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(PORT);
//printf("%d\n",INADDR_ANY);
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(ss, (struct sockaddr* ) &server_sockaddr, sizeof(server_sockaddr))==-1) {
perror("bind");
exit(1);
}
if(listen(ss, QUEUE) == -1) {
perror("listen");
exit(1);
}

struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
///成功返回非负描述字,出错返回-1
while(1){}; // 不执行后面语句,仅仅监听客户端连接而不分配 socket
conn = accept(ss, (struct sockaddr*)&client_addr, &length);
if( conn < 0 ) {
perror("connect");
exit(1);
}

char buffer[1024];
//创建另外一个线程
//std::thread t(thread_task);
//t.join();
//char buf[1024];
//主线程
while(1) {

// memset(buf, 0 ,sizeof(buf));
// if(fgets(buf, sizeof(buf),stdin) != NULL) {
//     send(conn, buf, sizeof(buf), 0);
// }

memset(buffer, 0 ,sizeof(buffer));
int len = recv(conn, buffer, sizeof(buffer), 0);
if(strcmp(buffer, "exit\n") == 0) break;
printf("%s", buffer);
//必须要有返回数据, 这样才算一个完整的请求
send(conn, buffer, len , 0);
}
close(conn);
close(ss);
return 0;
}


// 客户端
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>

#define MYPORT  7000
#define BUFFER_SIZE 1024

int main()
{
///定义so

int sock_cli = socket(AF_INET,SOCK_STREAM, 0);

printf("%d\n",sock_cli);
///定义sockaddr_in
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(MYPORT);  ///服务器端口
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  ///服务器ip
//连接服务器,成功返回0,错误返回-1
if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("connect");
exit(1);
}

char sendbuf[BUFFER_SIZE];
char recvbuf[BUFFER_SIZE];
while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
send(sock_cli, sendbuf, strlen(sendbuf),0); ///发送
if(strcmp(sendbuf,"exit\n")==0)
break;
recv(sock_cli, recvbuf, sizeof(recvbuf),0); ///接收
fputs(recvbuf, stdout);

memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
}

close(sock_cli);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  socket tcp
相关文章推荐