Linux网络编程(2)SOCKET编程与TCP三次握手

linu环境下Socket通信流程
实际上是文件(内核的缓冲区)操作

服务端
socket tcp service

创建套接字
□ int lfd = socket
绑定本地IP和端口(本地ip和端口存储在sockaddr_in结构体中)
□ struct sockaddr_in serv;
□ serv.port = htons(port);
□ serv.IP= htonl(INADDR_ANY);(INADDR_ANY宏将适配当前IP)
//需要进行大端转小段的操作,即将主机字节区转网络字节区
□ bind(lfd, &serv, sizeof(serv));
监听
□ listen(lfd, 128);
128为同时能够监听到的最大连接个数
等待并接收连接请求
□ struct sockaddr_in client;
□ int len = sizeof(client);
□ int cfd = accept(lfd, &client, &len);
cfd - 用于通信的,接收和发送数据使用的文件描述符为cfd并不是lfd
通信
□ 接收数据: read/recv
□ 发送数据: write/send
关闭
□ close(lfd);
□ close(cfd);

客户端
创建套接字
int fd = socket
连接服务器
struct sockaddr_in server;
server.port
server.ip=(int) (需要将点分十进制转换成int类型)
server.family
connect(fd,&server,sizeof(server));
通信
□ 接收数据: read/recv
□ 发送数据: write/send
关闭
□ close(lfd);
□ close(cfd);

例子:
在本机上实现SERVER和CLIENT通信,从C发送字符串给S,S收到后将其转换成大写并回复,C将
接收到的字符输出

server服务端源码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


int main(int argc, const char* argv[])
{
    // 创建用于监听的套节字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd == -1)
    {
        perror("socket error");
        exit(1);
    }

    // 绑定
    struct sockaddr_in serv_addr;
    // init
    memset(&serv_addr, 0, sizeof(serv_addr));
    // bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET; // 地址族协议  ipv4
    serv_addr.sin_port = htons(9999);   // 本地端口, 需要转换为大端
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 0 是用本机的任意IP

    int ret = bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if(ret == -1)
    {
        perror("bind error");
        exit(1);
    }

    // 设置监听
    ret = listen(lfd, 64);
    if(ret == -1)
    {
        perror("listen error");
        exit(1);
    }

    // 等待并接受连接请求
    struct sockaddr_in cline_addr;
    socklen_t clien_len = sizeof(cline_addr);
    int cfd = accept(lfd, (struct sockaddr*)&cline_addr, &clien_len);
    if(cfd == -1)
    {
        perror("accept error");
        exit(1);
    }
    
    char ipbuf[64];
    // int -> char*
    printf("cliient ip: %s, port: %d\n",
           inet_ntop(AF_INET, &cline_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
           ntohs(cline_addr.sin_port));

    // 通信
    while(1)
    {
        // 先接收数据
        char buf[1024] = {0};
        int len = read(cfd, buf, sizeof(buf));
        if(len == -1)
        {
            perror("read error");
            break;
        }
        else if(len > 0)
        {
            // 顺利读出了数据
            printf("read buf = %s\n", buf);
            // 小写 -》 大写
            for(int i=0; i

client客户端源码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// tcp client
int main(int argc, const char* argv[])
{
    // 创建套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1)
    {
        perror("socket error");
        exit(1);
    }

    // 连接服务器
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(9999);
    inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
    int ret = connect(fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if(ret == -1)
    {
        perror("connect error");
        exit(1);
    }

    // 通信
    while(1)
    {
        // 写数据
        // 接收键盘输入
        char buf[512];
        fgets(buf, sizeof(buf), stdin);
        // 发送给服务器
        write(fd, buf, strlen(buf)+1);

        // 接收服务器端的数据
        int len = read(fd, buf, sizeof(buf));
        printf("read buf = %s, len = %d\n", buf, len);
    }
    return 0;
}

TCP的三次握手介绍
Linux网络编程(2)SOCKET编程与TCP三次握手_第1张图片Linux网络编程(2)SOCKET编程与TCP三次握手_第2张图片

标志位介绍
SYN: 请求建立连接
ACK: 应答
FIN: 断开连接
连接需要三次握手:在实际的socket编程中,这三次握手会自动完成,并不需要我们进行实际操作

第一次握手
客户端
携带标志位: SYN
可以携带数据()
随机产生32为序号

服务器
检测SYN值是否为1
服务器:

第一次握手是由客户端发起的连接请求,在第一次发送的数据包中在标志位中将含有SYN信号,请求与服务器建立连接,并含有随机产生的32位序号,可以携带数据,在服务器接收到该数据包后,将对该数据包进行校验,首先校验SYN位是否为1,是则第一次握手成功

第二次握手

服务器
ACK 标志位 + 确认序号
客户端随机序号+1
发起一个连接请求
SYN+32随机序号

客户端
检测标志位: 1
校验: 确认序号是否正确

在第一次握手成功后,服务器将回复一个数据包,在该数据包中的标志位为ACK,应答客户端的第一个数据包接收成功,紧接着是一个32位的确认信号,该信号是在第一次接收的数据包中的32位随机序号加1进位,除了确认序号ACK外,服务器会在该数据包中产生一个SYN信号以及32位的随机序号,可以携带数据,请求与客户端建立连接,在客户端接收到该数据包后,依次检测ACK信号,确认信号,SYN信号是否正确,如果无误,则第二次握手成功

第三次握手
在前两次握手成功的基础上,客户端继续向服务器发送数据包,包含应答信号ACK和上一次服务器产生的随机序号进1位确认序号,服务器检测ACK是否为1,校验确认序号是否正确,如果无误,则客户端和服务器端正式建立连接

客户端:
应答信号ACK
32位确认序号

服务器
检测ACK是否为1
校验确认序号是否正确

四次挥手

挥手过程即为客户端和服务端断开连接的请求过程,哪一端主动断开连接都可以,需要一个标志位FIN,和对方最后发送ACK相应信号时携带的确认序号。

挥手过程:以客户端断开请求为例,
第一次挥手
客户端:
发送断开连接的请求,在该数据包中含有:
FIN+序号(对方最后回复ACK时携带的确认序号)
ACK+序号
服务端:
检验SYN值是否为1,
ACK的作用:告诉对方之前发送的数据接收了多少

第二次挥手:
在接收到第一次挥手发送的包后,服务器回复一个确认的数据包,包含ACK+确认序号(FIN对应的序号+1)。

服务器:
给client确认数据包
ACK+确认编号
FIN对应的序号+1+携带数据大小

客户端:
检测ACK值和确认序号

第三次挥手
第三次挥手是由对方发起的,在前两次挥手成功后,对方也将发送一个断开连接请求的数据包,包含FIN+序号,ACK+序号,收到该数据包后自身将进行检验

服务器:
发送断开连接的请求
FIN+序号
ACK+序号

客户端
数据检测

第四次挥手
过程与第二次大致一致

在程序代码中,客户端和服务端都调用了colse()方法后,四次挥手的过程就算是成功了。Linux网络编程(2)SOCKET编程与TCP三次握手_第3张图片

文章主要摘录自传智Linux网络编程的讲义,并记录下自己学习过程中的一些理解

你可能感兴趣的:(学习笔记)