Linux编程--网络编程TCP框架

本文详细介绍了TCP/IP协议的五层模型,包括应用层、传输层、网络层、数据链路层和物理层。接着,深入探讨了TCP通信的建立连接(三次握手)、数据传输和连接释放(四次挥手)的过程。此外,还解析了TCP编程中常用的函数,如socket、bind、listen、accept、connect、send和recv,并给出了服务器端和客户端的示例代码。
摘要由CSDN通过智能技术生成

目录

TCP/IP协议的五层模型

TCP通信的实现过程

TCP编程的函数详解

TCP编程的实现


TCP/IP协议的五层模型

TCP/IP协议是互联网的核心协议,它的五层模型分别为:

  1. 应用层:应用层协议处理网络应用程序之间的数据交换,如FTP、SMTP、HTTP等。

  2. 传输层:传输层协议为应用层提供可靠的端到端通信,如TCP和UDP。

  3. 网络层:网络层协议负责将分组从源节点传输到目的节点,如IP协议。

  4. 数据链路层:数据链路层协议负责在物理层上提供数据传输服务,如以太网协议。

  5. 物理层:物理层协议负责处理数据在物理媒介上传输的细节,如电信号和传输介质。

总体来说,TCP/IP五层模型是一种将通信协议按照功能分层的方法,从而为网络设计者提供了一个框架,用于在不同层次间进行协同工作。每一层都有自己特定的功能和职责,因此,TCP/IP协议可以实现不同的应用程序之间的通信。

TCP通信的实现过程

TCP通信的实现过程主要包括三个阶段:建立连接、数据传输和连接释放。

  1. 建立连接

在TCP通信开始之前,发送方和接收方需要先建立连接,以便在传输数据时能够互相识别。

a. 三次握手

发送方首先向接收方发送一个SYN(同步)包,用于请求建立连接。接收方收到SYN包后,回复一个ACK(确认)包,表示接收到请求。同时,接收方也向发送方发送一个SYN-ACK包,表示接收方准备好了建立连接。发送方收到SYN-ACK包后,回复一个ACK包,表示连接建立成功。

这个过程被称为“三次握手”,是为了确保双方都能正确地识别对方并建立连接。

b. 建立连接后的数据传输

连接建立后,发送方可以向接收方发送数据,数据会按照TCP协议进行分组、封装、校验和传输。接收方会根据TCP协议对数据进行解封、校验和重组,确保数据的正确性。

  1. 数据传输

在建立连接后,发送方可以向接收方发送数据。TCP协议保证数据的可靠性,通过以下几个步骤实现:

a. 分组

发送的数据会被切分成多个数据包,每个数据包称为分组。

b. 封装

在发送数据时,TCP会将每个分组封装成一个TCP报文段,添加报文头和报文尾,以便接收方进行识别和处理。

c. 校验

TCP协议会对每个TCP报文段进行校验和处理,确保数据的正确性。

d. 重传

如果发送的某个分组丢失,或者接收方没有正确地接收到某个分组,TCP协议会进行重传,确保数据能够被正确地传输。

  1. 连接释放

当数据传输完成后,发送方和接收方需要关闭连接,释放资源。

a. 四次挥手

发送方发送一个FIN包,表示数据已经发送完毕,准备关闭连接。接收方收到后回复一个ACK包,表示已经收到FIN包。当接收方也没有数据需要发送时,发送一个FIN包,请求断开连接。发送方收到FIN包后,回复一个ACK包,表示已经收到请求。这个过程被称为“四次挥手”,是为了确保双方都能正确地关闭连接。

b. 等待2MSL

在关闭连接后,需要等待2MSL(最长报文存活时间)的时间,确保所有的报文都被正确地传输完毕。过了2MSL时间后,连接才算真正关闭。

以上就是TCP通信的实现过程。通过这些步骤,TCP协议能够确保数据传输的可靠性和正确性,同时也能保证连接的有效性和安全性。

TCP编程的函数详解

TCP编程中常用的函数有:

socket()函数

函数原型:int socket(int domain, int type, int protocol)

该函数用于创建一个新的TCP套接字,成功时返回文件描述符,出错时返回 -1。

参数说明:

  • domain:协议族,通常使用 AF_INET(IPv4)或 AF_INET6(IPv6)。
  • type:套接字类型,通常使用 SOCK_STREAM(面向连接的套接字)。
  • protocol:协议类型,通常设为 0,由系统自动根据 domain 和 type 来选择协议。

bind()函数

函数原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

该函数用于将一个本地地址(IP地址和端口号)绑定到一个套接字,使得网络上的其他主机能够通过该套接字连接到这个本地地址。成功时返回 0,出错时返回 -1。

参数说明:

  • sockfd:需要绑定的套接字描述符。
  • addr:一个 sockaddr 类型的结构体,包含了要绑定的本地地址信息。
  • addrlen:addr 结构体的长度。

listen()函数

函数原型:int listen(int sockfd, int backlog)

该函数用于将一个套接字设为监听状态,使得该套接字能够接受来自其他主机的连接请求。成功时返回 0,出错时返回 -1。

参数说明:

  • sockfd:需要监听的套接字描述符。
  • backlog:请求队列的长度,即可以同时接受多少个连接请求。

accept()函数

函数原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

该函数用于接受一个等待的连接请求,并返回一个新的套接字描述符,该描述符用于和客户端进行数据通信。成功时返回新的套接字描述符,出错时返回 -1。

参数说明:

  • sockfd:需要接受连接请求的套接字描述符。
  • addr:一个 sockaddr 类型的结构体,用于存储与客户端连接的远程地址信息。
  • addrlen:addr 结构体的长度。

connect()函数

函数原型:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

该函数用于向指定的服务器地址发起连接请求。成功时返回 0,出错时返回 -1。

参数说明:

  • sockfd:需要连接的套接字描述符。
  • addr:一个 sockaddr 类型的结构体,包含了要连接的服务器地址信息。
  • addrlen:addr 结构体的长度。

send()函数

函数原型:ssize_t send(int sockfd, const void *buf, size_t len, int flags)

该函数用于向套接字发送数据。成功时返回发送的字节数,出错时返回 -1。

参数说明:

  • sockfd:需要发送数据的套接字描述符。
  • buf:指向待发送数据的缓冲区。
  • len:待发送数据的长度。
  • flags:发送数据的选项参数,通常设为 0。

recv()函数

函数原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags)

该函数用于从套接字接收数据。成功时返回接收的字节数,出错时返回 -1。

参数说明:

  • sockfd:需要接收数据的套接字描述符。
  • buf:指向接收数据的缓冲区。
  • len:缓冲区长度。
  • flags:接收数据的选项参数,通常设为 0。

close()函数

函数原型:close(int sockfd)

该函数用于关闭一个打开的套接字。

参数说明:

  • sockfd:需要接收数据的套接字描述符。

以上就是TCP编程中常用的函数及其参数的详细介绍。

TCP编程的实现

服务器端:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <pthread.h>
 
#define BACKLOG 5
 
void *ClinetHandle(void *arg);
int main(int argc, char *argv[])
{
    int fd, newfd;
    struct sockaddr_in addr, clint_addr;
    pthread_t tid;
    socklen_t addrlen = sizeof(clint_addr);
     
    if(argc < 3){
        fprintf(stderr, "%s<addr><port>\n", argv[0]);
        exit(0);
    }
 
    /*创建套接字*/
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd < 0){
        perror("socket");
        exit(0);
    }
    addr.sin_family = AF_INET;
    addr.sin_port = htons( atoi(argv[2]) );
    if ( inet_aton(argv[1], &addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid address\n");
        exit(EXIT_FAILURE);
    }
 
    /*地址快速重用*/
    int flag=1,len= sizeof (int); 
    if ( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1) { 
              perror("setsockopt"); 
                    exit(1); 
    } 
    /*绑定通信结构体*/
    if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){
        perror("bind");
        exit(0);
    }
    /*设置套接字为监听模式*/
    if(listen(fd, BACKLOG) == -1){
        perror("listen");
        exit(0);
    }
    while(1){
        /*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/
        newfd = accept(fd, (struct sockaddr *)&clint_addr, &addrlen);
        if(newfd < 0){
            perror("accept");
            exit(0);
        }
        printf("addr:%s port:%d\n", inet_ntoa(clint_addr.sin_addr), ntohs(clint_addr.sin_port) );
        pthread_create(&tid, NULL, ClinetHandle, &newfd);
        pthread_detach(tid);
    }
    close(fd);
    return 0;
}
void *ClinetHandle(void *arg){
    int ret;
    char buf[BUFSIZ] = {};
    int newfd = *(int *)arg;
    while(1){
        //memset(buf, 0, BUFSIZ);
        bzero(buf, BUFSIZ);
        ret = read(newfd, buf, BUFSIZ);
        if(ret < 0)
        {
            perror("read");
            exit(0);
        }
        else if(ret == 0)
            break;
        else
            printf("buf = %s\n", buf);
    }
    printf("client exited\n");
    close(newfd);
    return NULL;
}

客户端:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
 
#define BACKLOG 5
 
int main(int argc, char *argv[])
{
    int fd;
    struct sockaddr_in addr;
    char buf[BUFSIZ] = {};
 
    if(argc < 3){
        fprintf(stderr, "%s<addr><port>\n", argv[0]);
        exit(0);
    }
 
    /*创建套接字*/
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd < 0){
        perror("socket");
        exit(0);
    }
 
    addr.sin_family = AF_INET;
    addr.sin_port = htons( atoi(argv[2]) );
    if ( inet_aton(argv[1], &addr.sin_addr) == 0) {
        fprintf(stderr, "Invalid address\n");
        exit(EXIT_FAILURE);
    }
 
    /*向服务端发起连接请求*/
    if(connect(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){
        perror("connect");
        exit(0);
    }
    while(1){
        printf("Input->");
        fgets(buf, BUFSIZ, stdin);
        write(fd, buf, strlen(buf) );
    }
    close(fd);
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值