Linux学习之网络编程(TCP三次握手四次挥手)

言之者无罪,闻之者足以戒。 - “诗序”

1、三次握手:

看一下三次握手的框图:

(1)、服务器必须准备好接受外来连接

(2)、客户端调用connect来主动打开一个连接,此时客户端TCP将会发送一个SYN分节

(3)、服务器必须确认客户的SYN

(4)、客户必须确认服务器的SYN

下面我们看一下wireshark

对照着这张图我们就可以理解上面的框图中,三次握手的概念了。

下面我们来看一下server的程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#define MAX_LISTEN_QUE 5

int main(int argc,char *argv[])
{
        int listenfd,sockfd,opt=1;
        struct sockaddr_in server,client;
        char buf[200];
        socklen_t len;
        int timep;
        int ret;
        //创建套接字
        listenfd = socket(AF_INET,SOCK_STREAM,0);
        if(listenfd < 0)
        {
                perror("Create socket fail");
                return -1;
        }
        //设置套结字关联的选项
        if((ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) < 0)
        {
                perror("Error ,set socket reuse addr failed");
                return -1;
        }
        //清空结构体中的变量值
        bzero(&server, sizeof(server));
        //初始化结构体的变量
        server.sin_family = AF_INET;//IPv4
        server.sin_port   = htons(8888);//主机转换到网络
        server.sin_addr.s_addr  = htonl(INADDR_ANY);//同上(使用这个宏套接字可以绑定到所有的
端口)
        //获取结构体地址长度
        len = sizeof(struct sockaddr);
        //绑定服务器ip地址和端口到套接字
        if(bind(listenfd, (struct sockaddr *)&server, len)<0)
        {
                perror("bind error.");
                return -1;
        }
        //设置服务器的最大连接数
        listen(listenfd, MAX_LISTEN_QUE);

        while(1)
        {
           //等待客户端请求,如果请求到来,返回一个新的socket
           //服务器和客户端利用新的socket来通信
            sockfd = accept(listenfd, (struct sockaddr *)&client, &len);
            if(sockfd < 0)
            {
                perror("accept error.");
                return -1;
            }
        }
            sleep(10);
            //显示系统的当前时间 
            timep = time(NULL);
            //将数据按照一定的格式转换之后复制到buf
            //ctime返回一个表示当地时间的字符串
            snprintf(buf, sizeof(buf), "%s", ctime(&timep));
            //向套接字中写入buf存储的时间
            write(sockfd, buf, strlen(buf));
            //打印存储的字节数
            printf("Bytes:%d\n", strlen(buf));
            //打印套接字的文件描述符
            printf("sockfd=%d\n", sockfd);
            printf("IP:0x%x, Port:%d\n",ntohl(client.sin_addr.s_addr),ntohs(client.sin_port));
            sleep(10);
            //关闭套接字
            //close(sockfd);

        return 0;
}

在运行程序进行抓包的时候,sever函数我们是在:Linux系统下先运行,在通过windows系统下的命令提示符中连接sever。

备注:在windows系统下的搜索中输入:cmd就可以看到命令提示符程序

打开之后输入:telnet 192.168.177.133 8888就可以连接sever了(192.168.177.133是我的Linux的服务器地址,8888是我设置的 端口号)。

帮助:如果你输入上述命令之后提示:telnet不是内部或外部命令也不是可运行的程序或批处理,解决办法可以参考下面的文章

https://blog.csdn.net/haijing1995/article/details/66475546

注意:在启动客户端和服务器端的时候,要先启动服务器端之后再启动客户端,然后进行抓包。使用windows下的命令提示符一旦启动程序是不会自己退出的。需要我们强制退出

2、四次挥手:

看一下四次挥手的框图:

这里我们还是先用上面所用到的命令提示符来用wireshark来抓包:

对照着这张图我们就可以理解上面的框图中,四次挥手的概念了。

下面我们在Linux中重复操作一下上面的过程:

先贴出server的程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#define MAX_LISTEN_QUE 5

int main(int argc,char *argv[])
{
        int listenfd,sockfd,opt=1;
        struct sockaddr_in server,client;
        char buf[200],read_buf[100];
        int bytes=0;
        socklen_t len;
        int timep;
        int ret;
        //创建套接字
        listenfd = socket(AF_INET,SOCK_STREAM,0);
        if(listenfd < 0)
        {
                perror("Create socket fail");
                return -1;
        }
        //设置套结字关联的选项
        if((ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) < 0)
        {
                perror("Error ,set socket reuse addr failed");
                return -1;
        }
        //清空结构体中的变量值
        bzero(&server, sizeof(server));
        //初始化结构体的变量
        server.sin_family = AF_INET;//IPv4
        server.sin_port   = htons(8888);//主机转换到网络
        server.sin_addr.s_addr  = htonl(INADDR_ANY);//同上(使用这个宏套接字可以绑定到所有的
端口)
        //获取结构体地址长度
        len = sizeof(struct sockaddr);
        //绑定服务器ip地址和端口到套接字
        if(bind(listenfd, (struct sockaddr *)&server, len)<0)
        {
                perror("bind error.");
                return -1;
        }
        //设置服务器的最大连接数
        listen(listenfd, MAX_LISTEN_QUE);
        while(1)
        {
           //等待客户端请求,如果请求到来,返回一个新的socket
           //服务器和客户端利用新的socket来通信
            sockfd = accept(listenfd, (struct sockaddr *)&client, &len);
            if(sockfd < 0)
            {
                perror("accept error.");
                return -1;
            }

            //显示系统的当前时间 
            timep = time(NULL);
            //将数据按照一定的格式转换之后复制到buf
            //ctime返回一个表示当地时间的字符串
            snprintf(buf, sizeof(buf), "%s", ctime(&timep));
            //向套接字中写入buf存储的时间
            write(sockfd, buf, strlen(buf));
            //打印存储的字节数
            printf("Bytes:%d\n", strlen(buf));
            //打印套接字的文件描述符
            printf("sockfd=%d\n", sockfd);
            printf("IP:0x%x, Port:%d\n",ntohl(client.sin_addr.s_addr),ntohs(client.sin_port));
            bytes = read(sockfd,read_buf,100);
            if(bytes < 0)
            {
                printf("read err\n");
                return -1;
            }
            if(bytes == 0)
             {
                printf("client connection closed\n");
                return 0;
             }
//              sleep(10);
            //关闭套接字
            close(sockfd);
        }
        return 0;
}

接下来看一下client的程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h> 
#include <fcntl.h>

int main(int argc,char *argv[]) 
{
        int sockfd; 
        struct sockaddr_in servaddr;
        char buf[100];//存储读取的内容
        int bytes;//存储读取的字节数
        
        if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//创建套接字
        {
                printf("socket error\n");
                return -1; 
        }
        //结构体中成员变量初始化为0     
        bzero(&servaddr,sizeof(servaddr));
        //初始化成员变量
        //初始化成员变量
        servaddr.sin_family = AF_INET;//IPv4
        servaddr.sin_addr.s_addr = inet_addr("192.168.177.133");//转换为地址格式
        servaddr.sin_port = htons(8888);//主机序转换到网络序

        if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0)//绑定服务器
ip和端口到套结字
        {
                perror("connect error");
                return -2;
        }
        //存储读取的数据和读取的字节数
        bytes == read(sockfd,buf,100);
        if(bytes < 0)
        {
                printf("Error ,read failed\n");
                return -3;
        }
        //如果读取的字节数为0 ,就说明连接已经关闭了
        if(0 == bytes)
        {
                printf("Server close connection\n");
                return -4;

        }
        //打印读取的字节数和读取的内容
        printf("read bytes %d\n",bytes);
        printf("Time: %s\n",buf);
        //关闭套结字
        close(sockfd);
        return 0;
}

之后看一下程序的运行结果:

猜你喜欢

转载自blog.csdn.net/weixin_42994525/article/details/83501754