网络编程基础之TCP基本通信

TCP特性

TCP(Transmission Control Protocol,传输控制协议) ,是TCP/IP协议族中用于传输层 的协议,特点是面向连接、可靠、基于字节流,能够保障数据不丢失、重复、无序,但数据传输效率较低,且数据收发不同步。

TCP服务器端代码实现

cpp 复制代码
//服务器端代码实现
#include <stdio.h>    // printf
#include <iostream>    // std::cout
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>    //定义socket相关的函数和数据结构
#include <sys/types.h>    //系统基础数据类型定义头文件(定义Unix/Linux 系统中用于描述「对象类型」的基本数据类型)
#include <string.h>    //调strcat bzero strlen函数
#include <unistd.h>    //调close函数
#define SERVER_PORT 8888    //服务器端口号
#define SERVER_IP "192.168.23.128"    //服务器端IP地址

int main(){
    //1.创建用于连接的套接字文件描述符--socket
    int sfd = socket(AF_INET,SOCK_STREAM,0);    //sfd全称socket file descriptor(套接字文件描述符)
    //参数1:AF_INET表示使用IPv4协议族
    //参数2:SOCK_STREAM表示使用面向连接的TCP协议
    //参数3:协议类型,通常设置为0,表示使用默认协议(参数2指定了协议时参数3填写0即可)
    //返回值:成功返回sfd(socket文件描述符),失败返回-1
    if(sfd == -1){
        perror("socket error");
        return -1;
    }
    printf("socket success sfd = %d\n",sfd);


    //2.绑定ip地址和端口号--bind
    //2.1填充要绑定的ip地址和端口号结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;    //通信域
    sin.sin_port = htons(SERVER_PORT);    //端口号(使用htons将2字节整数转换为网络字节序)
    sin.sin_addr.s_addr = inet_addr(SERVER_IP);    //IP地址(使用inet_addr将点分十进制字符串转换为网络字节序)
    
    //2.2绑定工作
    //参数1:要被绑定的套接字文件描述符
    //参数2:要绑定的地址信息结构体,需要进行强制类型转换,防止警告
    //参数3:参数2的大小
    //返回值:成功返回0,失败返回-1并置位错误码
    if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1){
        perror("bind error");
        return -1;
    }
    printf("bind success\n");


    //3.启动监听--listen
    //参数1:要启动监听的文件描述符
    //参数2:挂起队列的长度
    //返回值:成功返回0,失败返回-1并置位错误码
    if(listen(sfd, 128) == -1){
        perror("listen error");
        return -1;
    }
    printf("listen success\n");


    //4.阻塞等待客户端的连接请求--accept
    //定义变量用于接收客户端地址信息结构体
    struct sockaddr_in cin;    //用于接收地址信息结构体
    socklen_t socklen = sizeof(cin);    //用于接收地址信息的长度

    //参数1:服务器套接字文件描述符
    //参数2:用于接收客户端地址信息结构体的容器,如果不接收,也可以填NULL
    //参数3:接收参数2的大小,如果参数2为NULL,则参数3也是NULL
    //返回值:成功则返回一个新的用于通信的套接字文件描述符(即newfd),失败返回-1并置位错误码
    int newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);    //新的用于通信的套接字文件描述符
    if(newfd == -1){
        perror("accept error");
        return -1;
    }
    printf("[%s:%d]已连接成功!\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));    //客户端IP地址 客户端端口号


    //5.数据收发--recv/send
    char rbuf[128] = "";    //数据容器
    while(1){
        //清空容器中的内容
        bzero(rbuf,sizeof(rbuf));

        //从套接字中读取消息--recv
        //参数1:通信的套接字文件描述符
        //参数2:要存放数据的起始地址
        //参数3:读取的数据的大小
        //参数4:读取标识位,是否阻塞读取(0表示阻塞等待,MSG_DONTWAIT表示非阻塞)
        //返回值:大于0表示成功读取的字节个数,等于0表示对端已下线,等于-1表示失败,并置位错误码
        int res = recv(newfd, rbuf, sizeof(rbuf), 0);
        if(res == 0){
            printf("对端已下线\n");
            break;
        }
        printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), rbuf);    //客户端IP地址 客户端端口号
    
        //处理收到的数据并送回给客户端
        strcat(rbuf, "_");

        //将消息发送给客户端--send
        //参数1:通信的套接字文件描述符
        //参数2:要发送的数据的起始地址
        //参数3:发送的数据的大小
        //参数4:读取标识位,是否阻塞读取(0表示阻塞等待,MSG_DONTWAIT表示非阻塞)
        //返回值:大于0表示成功发送的字节个数,等于0表示对端已下线,等于-1表示失败,并置位错误码
        if(send(newfd, rbuf, strlen(rbuf), 0) == -1){
            perror("send error");
            return -1;
        }
        printf("发送成功!\n");
    }


    //6.关闭套接字--close
    //参数:要关闭的套接字文件描述符,类型为int
    //返回值:成功返回0,失败返回-1并置位错误码
    close(newfd);
    close(sfd);

    return 0;
}

TCP客户端代码实现

cpp 复制代码
//客户端代码实现
#include <stdio.h>    // printf
#include <iostream>    // std::cout
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>    //定义socket相关的函数和数据结构
#include <sys/types.h>    //系统基础数据类型定义头文件(定义Unix/Linux 系统中用于描述「对象类型」的基本数据类型)
#include <string.h>    //调strcat bzero strlen函数
#include <unistd.h>    //调close函数
#define SERVER_PORT 8888    //服务器端口号
#define SERVER_IP "192.168.23.128"    //服务器端IP地址
#define CLIENT_PORT 9999    //客户端口号
#define CLIENT_IP "192.168.23.128"    //客户端IP地址

int main(){
    //1.创建用于连接的套接字文件描述符--socket
    int cfd = socket(AF_INET,SOCK_STREAM,0);    //客户套接字文件描述符
    if(cfd == -1){
        perror("socket error");
        return -1;
    }
    printf("socket success cfd = %d\n",cfd);


    //2.绑定ip地址和端口号--bind
    //2.1填充要绑定的ip地址和端口号结构体
    struct sockaddr_in cin;
    cin.sin_family = AF_INET;    //通信域
    cin.sin_port = htons(CLIENT_PORT);    //端口号(使用htons将2字节整数转换为网络字节序)
    cin.sin_addr.s_addr = inet_addr(CLIENT_IP);    //IP地址(使用inet_addr将点分十进制字符串转换为网络字节序)
    
    //2.2绑定工作(可选)
    if(bind(cfd,(struct sockaddr*)&cin,sizeof(cin)) == -1){
        perror("bind error");
        return -1;
    }
    printf("bind success\n");


    //3.连接服务器--connect
    //3.1填充要连接的服务器地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;    //通信域
    sin.sin_port = htons(SERVER_PORT);    //端口号
    sin.sin_addr.s_addr = inet_addr(SERVER_IP);    //服务器端IP地址

    //3.2连接工作
    //参数1:要连接的套接字文件描述符
    //参数2:通用地址信息结构体
    //参数3:参数2的大小
    //返回值:成功返回0,失败返回-1并置位错误码
    if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) == -1){
        perror("connect error");
        return -1;
    }
    printf("连接服务器成功!\n");


    //4.数据收发--recv/send
    char wbuf[128] = "";    //数据容器
    while(1){
        //清空容器中的内容
        bzero(wbuf,sizeof(wbuf));

        //从终端获取数据
        fgets(wbuf, sizeof(wbuf), stdin);
        wbuf[strlen(wbuf) - 1] = 0;    //将换行改成'\0'

        //将数据发送给服务器
        if(send(cfd, wbuf, sizeof(wbuf), 0) == -1){
            perror("send error");
            return -1;
        }

        //接收服务器发送过来的消息
        if(recv(cfd, wbuf, sizeof(wbuf), 0) == 0){
            printf("对端已经下线\n");
            break;
        }
        printf("收到服务器消息为:%s\n", wbuf);
    }


    //5.关闭套接字--close
    close(cfd);

    return 0;
}
相关推荐
女神下凡26 分钟前
芯参谋(95):嵌入式文件系统_开发指南
linux·服务器·arm开发·驱动开发·设计规范
@兽血沸腾1 小时前
Windows系统安装Jenkins部署应用至远程Windows服务器操作指南
运维·服务器·jenkins
Java成神之路-1 小时前
TCP Socket 与 UDP Socket
tcp/ip·计算机网络·udp
承渊政道4 小时前
Linux网络学习【Reactor反应堆模式】
linux·网络·学习·ubuntu·reactor
讯捷蓝达6 小时前
戴尔R720xd服务器卡死在 “Configuring Memory...Done“,换了内存还是卡?问题根本不在内存
运维·服务器
数字新视界9 小时前
动环监控信创适配在智能机房管理中的应用价值与未来展望
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
爱和冰阔落9 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
lisanmengmeng9 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
严肃小白兔10 小时前
Cloudflare 更新 API Token 管理:从最小权限到自动化凭据安全
网络·安全·自动化
Android系统攻城狮10 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频