4、网络编程——TCP相关的API及其实现的步骤

目录

一、API

1、listen(连接套接字)

2、accept(等待连接)

注意

3、send(向TCP套接字发送数据)

4、recv(从TCP套接字收取数据)

5、connect(连接对端监听套接字)

二、使用步骤

1、服务端

2、客户端

三、代码

1、服务器(接收端)

2、客户端(发送方)

一、API

1、listen(连接套接字)

将带连接套接字设置为监听套接字,并设置最大同时接收连接请求个数

int listen(int sockfd, int backlog);
参数:
    sockfd:待连接套接字
    backlog:最大同时接收连接请求个数
返回值:
    成功:0,并将 sockfd 设置为监听套接字
    失败:-1

由于历史原因,各种系统对 backlog 的理解并不一致,以 LINUX 为例,监听端能同时接收的 最大连接请求个数为 backlog+4

2、accept(等待连接)

​
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
    sockfd:监听套接字
    addr:通用地址结构体,用以存储对端地址(IP+PORT)
    addrlen:参数 addr 的存储区域大小
返回值:
    成功:已连接套接字(非负整数)
    失败:-1

​

注意

        该函数是用来等待客户端连接的, 如果有新的客户端连接上来那么该函数会返回一个新的sockfd 作为该客户端的连接套接字。

        如果把accept 写在循环体内部, 该函数会造成阻塞, 同时如果有多个客户端连接那么该函数会返 回这些客户所对应的多个已连接套接字。

3、send(向TCP套接字发送数据)

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
    sockfd:已连接套接字
    buf:即将被发送的数据
    len:数据长度
    flags:发送标志。
        MSG_NOSIGNAL:当对端已关闭时,不产生 SIGPIPE 信号
        MSG_OOB:发送紧急(带外)数据,只针对 TCP 连接
返回值:
    成功:已发送字节数
    失败:-1

当 flags 为 0 时,send 与 write 作用相同。

4、recv(从TCP套接字收取数据)

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数:
    sockfd:已连接套接字
    buf:存储数据缓冲区
    len:缓冲区大小
    flags:接收标志
        MSG_OOB:接收紧急(带外)数据
返回值:
    成功:已接收字节数
    失败:-1

注意:当 flags 为 0 时,recv 与 read 作用相同。

5、connect(连接对端监听套接字)

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:    
    sockfd:待连接套接字
    addr:包含对端地址(IP+PORT)的通用地址结构体的指针
    addrlen:地址结构体大小
返回值:
    成功:0
    失败:-1

二、使用步骤

1、服务端

(1)创建实现通信的工具(socket)

(2)设置IP+端口号

(3)绑定端口号等信息(bind)

(4)设置监听(listen)

(5)等待客户端连接(accept)

(6)互相聊天(send、recv)

(7)关闭聊天(套接字)

2、客户端

(1)创建实现通信的工具(socket)

(2)设置需要连接的服务区的IP地址以及端口号

(3)尝试连接服务器(connect)

(4)相互聊天(send、recv)

(5)关闭聊天(套接字)

三、代码

1、服务器(接收端)

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>  // 包含了地址结构体的定义声明
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>


int main(int argc, char const *argv[])
{
    // 创建一个套接字 (购买一台手机)
    //IPV4、流式套接字
    int sock_fd = socket( AF_INET , SOCK_STREAM , 0 ); // 使用IPV4协议簇, 流式套接字(TCP)
    if (-1 == sock_fd)
    {
        perror("socket rror");
        return -1 ;
    }
    

    // 配置自己的IP和端口号等信息
    // struct sockaddr_in   // IPV4地址结构体
    // {
    //     u_short sin_family;// 地址族
    //     u_short sin_port;// 端口
    //     struct in_addr sin_addr;// IPV4 地址
    //     char sin_zero[8];
    // };
    int addrlen = sizeof(struct sockaddr_in);
    struct sockaddr_in my_addr = {
        .sin_addr.s_addr = htonl(INADDR_ANY) , // 设置服务器的地址, INADDR_ANY 指本机中任何一个地址
        .sin_family = AF_INET , // 使用 IPV4 协议簇
        .sin_port = htons(65000) // 设置服务器的端口号为  65000 
    };

    // 绑定地址信息 (IP + 端口号 + 协议簇)
    int ret_val =  bind(  sock_fd,  (struct sockaddr *)&my_addr, addrlen);
    if (ret_val == -1 )
    {
        perror("bind error");
        return -1 ;
    }else{
        printf(" bind succeed !!\n") ;
    }
    
    // 设置监听 (打开铃声)
    if(listen( sock_fd,  5 )) // 设置sock_fd 为监听套接字, 同时可以接收连接请求数为  5 + 4(默认值) 
    {
        perror("listen error");
        return -1 ;
    }

    // 等待连接
    struct sockaddr_in from_addr; 
    int connect_fd = accept( sock_fd , (struct sockaddr *)&from_addr, &addrlen);
    if (connect_fd == -1 )
    {
        perror("connect error");
        return -1 ;
    }
    else{
        printf("connect succeed !!\n");
    }
    
    // 聊天
    char * msg = calloc(128,1);
    while(1)
    {
        bzero(msg , 128 );
        ret_val =  recv(connect_fd , msg , 128 , 0 );
        if ( ret_val != -1 )
        {
            printf("recv msg : %s " , msg );
        }
        else{
            perror("recv error !!");
        }
    }
    // 关闭套接字
    close(sock_fd);
   
    return 0;
}

2、客户端(发送方)

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>  // 包含了地址结构体的定义声明
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>


int main(int argc, char const *argv[])
{
    // 创建一个套接字 (购买一台手机)
    int sock_fd = socket( AF_INET , SOCK_STREAM , 0 ); // 使用IPV4协议簇, 流式套接字(TCP)
    if (-1 == sock_fd)
    {
        perror("socket rror");
        return -1 ;
    }
    

    // 配置自己的IP和端口号等信息
    // struct sockaddr_in   // IPV4地址结构体
    // {
    //     u_short sin_family;// 地址族
    //     u_short sin_port;// 端口
    //     struct in_addr sin_addr;// IPV4 地址
    //     char sin_zero[8];
    // };
    int addrlen = sizeof(struct sockaddr_in);
    struct sockaddr_in server_addr = {
        .sin_addr.s_addr = inet_addr("192.168.102.2") , // 设置服务器的地址
        .sin_family = AF_INET , // 使用 IPV4 协议簇
        .sin_port = htons(65000) // 设置服务器的端口号为  65000 
    };

  
    // 连接服务器
    if( connect(sock_fd , (const struct sockaddr *)&server_addr, addrlen))
    {
        perror("connect error");
        return -1 ;
    }

    
    // 聊天
    char * msg = calloc(128,1);
    while(1)
    {
        bzero(msg , 128 );
        fgets(msg , 128 , stdin);
        int ret_val =  send(sock_fd , msg , strlen(msg) , 0 );
        if ( ret_val != -1 )
        {
            printf("send msg succeed : %d byte \n" , ret_val );
        }
        else{
            perror("send error !!");
        }
    }

    // 关闭套接字
    close(sock_fd);
    

    return 0;
}

上述程序缺点

若客户端突然掉线,服务器奔溃(需要三次握手)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值