网络编程基础之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;
}
相关推荐
米高梅狮子5 小时前
03.网络类服务实践
linux·运维·服务器·网络·kubernetes·centos·openstack
June`5 小时前
网络编程时内核究竟做了什么???
linux·服务器·网络
原来是猿5 小时前
腾讯云服务器端口开放完全指南
服务器·网络·腾讯云
你的保护色6 小时前
【无标题】
java·服务器·网络
楼兰公子6 小时前
RK3588 + Linux7.0.3 网络工程调试错误速查手册
linux·网络·3588
Elnaij6 小时前
Linux系统与系统编程(9)——自设计shell与基础IO
linux·服务器
IpdataCloud6 小时前
稳定的企业级IP数据接口怎么选?可用性指标+离线库高可用方案
运维·网络·tcp/ip
HMS工业网络6 小时前
如何解决使用TwinCAT时EtherCAT网络出现“Sync Manager Watchdog”报错
网络·网络协议·网络安全
IMPYLH6 小时前
Linux 的 unexpand 命令
linux·运维·服务器·bash
想唱rap6 小时前
IO多路转接之poll
服务器·开发语言·数据库·c++