C++网络通信:基于TCP面向连接的网络通信方式

C++基于TCP的网络通信实现

TCP客户端服务器模型的通信原理图

一、服务端

1、socket函数

功能:为通信创建一个端点,并返回该端点对应的文件描述符,文件描述符的使用原则是最小未分配原则。

#include <sys/types.h> 
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
参数1:协议族,常用的协议族如下
Name
Purpose Man page
AF_UNIX, AF_LOCAL
本地通信,同一主机的多进程通信
查看 man unix
AF_INET
提供IPv4的相关通信方式
查看 man ip
AF_INET6
提供IPv6的相关通信方式
查看 man 7 ipv6
参数2:通信类型,指定通信语义,常用的通信类型如下
SOCK_STREAM
支持TCP面向连接的通信协议
SOCK_DGRAM
支持UDP面向无连接的通信协议
参数3:通信协议,当参数2中明确指定特定协议时,参数3可以设置为0,但是有多个协议共同使用 时,需要用参数3指定当前套接字确定的协议。
返回值:成功返回创建的端点对应的文件描述符,失败返回-1并置位错误码

2、bind函数

功能:为套接字分配名称,给套接字绑定ip地址和端口号

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数1:要被绑定的套接字文件描述符
参数2:通用地址信息结构体,对于不同的通信域而言,使用的实际结构体是不同的,该结构体的目
的是为了强制类型转换,防止警告
通信域为:AF_INET而言,ipv4的通信方式
struct sockaddr_in {
    sa_family_t sin_family; /* 地址族: AF_INET */
    in_port_t sin_port; /* 端口号的网络字节序 */
    struct in_addr sin_addr; /* 网络地址 */
};

/* Internet address. */
struct in_addr {
    uint32_t s_addr; /* ip地址的网络字节序 */
};

通信域为:AF_UNIX而言,本地通信

struct sockaddr_un {
    sa_family_t sun_family; /* 通信域:AF_UNIX */
    char sun_path[UNIX_PATH_MAX]; /* 通信使用的文件 */
};

参数3:参数2的大小,可以用sizeof计算

返回值:成功返回0,失败返回-1并置位错误码

3、listen函数

功能:将套接字设置成被动监听状态
#include <sys/types.h> 
#include <sys/socket.h>

int listen(int sockfd, int backlog);
参数1:套接字文件描述符
参数2:挂起队列能够增长的最大长度,一般为128
返回值:成功返回0,失败返回-1并置位错误码

4、accept函数

功能:阻塞等待客户端的连接请求,如果已连接队列中有客户端,则从连接队列中拿取第一个,并创建一个用于通信的套接字
#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数1:服务器套接字文件描述符
参数2:通用地址信息结构体,用于接受已连接的客户端套接字地址信息的
参数3:接收参数2的大小
返回值:成功发那会一个新的用于通信的套接字文件描述符,失败返回-1并置位错误码

5、recvsend数据收发

(1)recv函数

功能:从套接字中读取消息放入到buf

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数1:通信的套接字文件描述符
参数2:要存放数据的起始地址
参数3:读取的数据的大小
参数4:读取标识位,是否阻塞读取
        0:表示阻塞等待
        MSG_DONTWAIT:非阻塞
返回值:可以是大于0:表示成功读取的字节个数
                可以是等于0:表示对端已经下线(针对于TCP通信)
                -1:失败,并置位错误码

(2)send函数

功能:向套接字文件描述符中将buf这个容器中的内容写入

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数1:通信的套接字文件描述符
参数2:要发送的数据的起始地址
参数3:发送的数据的大小
参数4:读取标识位,是否阻塞读取
        0:表示阻塞等待
        MSG_DONTWAIT:非阻塞
返回值:成功返回发送字节的个数
        -1:失败,并置位错误码

6、close函数

功能:关闭套接字文件描述符

#include <unistd.h>

int close(int fd);

参数:要关闭的套接字文件描述符
返回值:成功返回0,失败返回-1并置位错误码

7、服务器端代码实现

#include<myhead.h>
#define SER_IP "192.168.137.138"
#define SER_PORT 8888
 
int main(int argc, const char *argv[]){
    //1、创建用于连接的套接字文件描述符
        //参数1:AF_INET表示使用的是ipv4的通信协议
        //参数2:SOCK_STREAM表示使用的是tcp通信
        //参数3:由于参数2指定了协议,参数3填0即可
    int sfd=socket(AF_INET,SOCK_STREAM,0);
    if(sfd==-1){
        perror("socker error");
        return -1;
    }
    printf("socket sfd = %d\n",sfd);

    //2、绑定ip地址和端口号
        //2.1 填充要绑定的ip地址和端口号结构体
    sockaddr_in sin;
    sin.sin_family=AF_INET;  //通信域
    sin.sin_port=htons(SER_PORT); //转换为网络字节序的端口号
    sin.sin_addr.s_addr=inet_addr(SER_IP); //转换为网络字节序的ip

        //2.2 绑定工作
            //参数1:要被绑定的套接字文件描述符
            //参数2:要绑定的地址信息结构体,需要进行强制类型转换,防止警告
            //参数3:参数2的大小
    if(bind(sfd,(sockaddr*)&sin,sizeof(sin))==-1){
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    //3、启动监听
        //参数1:要启动监听的文件描述符
        //参数2:挂起队列的长度
    if(listen(sfd,128)==-1){
        perror("listen error");
        return -1;
    }
    printf("listen success\n");
    
    sockaddr_in scin;
    socklen_t len=sizeof(scin);
    //4、阻塞等待客户端的连接请求
    int newfd=accept(sfd,(sockaddr*)&scin,&len);
    if(newfd==-1){
        perror("accept error");
        return -1;
    }
    printf("[%s,%d]:connection successful\n",\
            inet_ntoa(scin.sin_addr),ntohs(scin.sin_port));

    //5、数据收发
    char buf[128];
    while(1){
        bzero(&buf,sizeof(buf)); //将buf数组中清零

        int res=recv(newfd,buf,sizeof(buf),0);  //从客户端读取数据到buf
        if(res==0){  //此时说明客户端下线
            printf("[%s,%d]:leave\n",\
                inet_ntoa(scin.sin_addr),ntohs(scin.sin_port));
            break;
        }
        printf("[%s,%d]:%s\n",\
            inet_ntoa(scin.sin_addr),ntohs(scin.sin_port),buf);
        strcat(buf,"*_*");  
        if(send(newfd,buf,strlen(buf),0)==-1){  //将数据从buf中发送到客户端
            perror("send error");
            return -1;
        }
        printf("send sussessful\n");
    }
 
    //6、关闭套接字文件描述符
    close(newfd);
    close(sfd);

    return 0;
}

二、客户端

其他函数均在上文有介绍,这里不过多赘述

1、connect 函数

功能:将指定的套接字,连接到给定的地址上
#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数1:要连接的套接字文件描述符
参数2:通用地址信息结构体
参数3:参数2的大小
返回值:成功返回0,失败返回-1并置位错误码

2、客户端代码实现

注意:客户端中绑定ip地址和端口号并不是必要项,如果不绑定,系统会自动分配端口号。

#include<myhead.h>
#define SER_IP "192.168.137.138"
#define CLI_IP "192.168.137.138"
#define SER_PORT 8888
#define CLI_PORT 9999
 
int main(int argc, const char *argv[]){
    //1、创建用于通信的客户端套接字描述符
    int cfd=socket(AF_INET,SOCK_STREAM,0);
    if(cfd==-1){
        perror("socket error");
        return -1;
    }

    //2、绑定ip地址和端口号(可选)
    //2.1 填充要绑定的地址信息结构体
    sockaddr_in cin;
    cin.sin_family=AF_INET;
    cin.sin_addr.s_addr=inet_addr(CLI_IP);
    cin.sin_port=htons(CLI_PORT);
    //2.2 绑定工作
    if(bind(cfd,(sockaddr*)&cin,sizeof(cin))==-1){
        perror("bind error");
        return -1;
    }

    //3、连接服务器
    //3.1 填充要连接的服务器的地址信息结构体
    sockaddr_in sin;
    sin.sin_family=AF_INET;
    sin.sin_addr.s_addr=inet_addr(SER_IP);
    sin.sin_port=htons(SER_PORT);
    //3.2 连接工作
    if(connect(cfd,(sockaddr*)&sin,sizeof(sin))==-1){
        perror("connect error");
        return -1;
    } 

    //4、数据收发
    char buf[128];
    while(1){
        bzero(buf,sizeof(buf));

        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]=0;

        if(send(cfd,buf,strlen(buf),0)==-1){
            perror("send error");
            return -1;
        }

        if(recv(cfd,buf,sizeof(buf),0)==-1){
            perror("recv error");
            return -1;
        }

        printf("receive message:%s\n");
    }

    //5、关闭描述符
    close(cfd);

    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值