Linux之进程间通信(三):套接字之TCP

目录

TCP特点

TCP适用情况

TCP 通信流程

函数接口

socket

bind

listen

recv  

connect

send

TCP流程 

服务器端

流程

代码演示

客户端

流程

代码演示


 


TCP特点

都是传输层协议,全双工通信、 面向连接、可靠

  • 是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误数据无丢失数据无失序数据无重复到达的通信)

TCP适用情况

  •  适合于对传输质量要求较高,以及传输大量数据的通信。
  • 在需要可靠数据传输的场合,通常使用TCP协议
  • MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议

TCP 通信流程

函数接口

socket

        NAME:socket - create an endpoint for communication

        SYNOPSIS://头文件

       #include <sys/types.h>          /* See NOTES */

       #include <sys/socket.h>

       int socket(int domain, int type, int protocol);

           功能: 创建套接字文件

           参数:

                        domain:协议族

                                 AF_UNIX, AF_LOCAL   用于本地通信

                                 AF_INET             IPv4 Internet protocols         

                                 AF_INET6            IPv6 Internet protocols         

                        type:协议类型

                                SOCK_STREAM      TCP

                                SOCK_DGRAM     UDP  

                SOCK_RAW    原始套接字      

                        protocol:

                                一般情况下写0

                                系统默认自动帮助匹配对应协议

                                传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP

                                网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)     

                返回值:

                        成功: 返回一个特殊文件描述符;

                        失败: -1 更新errno

bind

        int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

        功能: 绑定,将socket()返回值和IP/端口号进行绑定;

(以什么样的形式去绑定?就是填充第二个结构体,把端口号和IP填充到这个结构体中)

        参数:

                sockfd:  是socket()函数的返回值;

                const struct sockaddr *addr:

                        struct sockaddr是结构体类型,是一个通用结构体;

                            struct sockaddr {

                                                 sa_family_t sa_family;    // 2个字节typedef unsigned short int  sa_family_t;  //

                                        char        sa_data[14];  // 14字节

                                }

                                整个结构体大小为16个字节


(程序员每次填充的时候填充自己的结构体,将自己的结构体强转成通用的结构体,原因:每个协议都对应一个结构体,如果每个协议都调用一次这个函数,就会调用很多函数接口。为了做到统一,你可以定义自己的结构体。

用IPv4通信时传值需传对应结构体struct sockaddr_in是Internet的结构体,本地通信还会有本地通信所要填充的结构体sockaddr_un,每种协议都有自己需要填充的一个结构体,如果每种协议都有自己的函数接口的话,函数接口太多,没办法记忆,为了做到统一性,填充的填充自己的结构体,传值的时候传struct sockaddr,那么就需要把自己填充的sockaddr_in强制转换成struct sockaddr形式) 


struct sockaddr_in {

                                      unsigned short sin_family;  //协议IPv4,2个字节

                                      unsigned short sin_port;    //端口号 ,2个字节                                                                          struct in_addr    sin_addr;  

                                      struct in_addr {

                                                                      __be32 s_addr;//IP地址                       };

                                        /* Pad to size of `struct sockaddr'. */

                                      unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -

                                                sizeof(unsigned short int) - sizeof(struct in_addr)];

                                                        //8个字节

                                };

                addrlen:

                        结构体的大小;

                                sizeof(serveraddr);

                返回值:0

                        -1  失败,更新errno

listen

        int listen(int sockfd, int backlog);

        功能: 用于监听,将主动套接字变为被动套接字;

        参数:

                sockfd:  socket()的返回值

                backlog:客户端同时连接服务器的最大个数;

           不同平台可同时链接的数不同,一般写6-8个

(队列1:保存正在连接)

                            (队列2,连接上的客户端)

        返回值:

                失败 -1

recv  

        ssize_t recv(int sockfd, void *buf, size_t len, int flags);

        功能: 接收数据

        参数:

                sockfd: acceptfd ;

                buf  存放位置

                len  大小

                flags  一般填0,相当于read()函数

               

                        MSG_DONTWAIT  非阻塞

        返回值:

                < 0  失败出错

                ==0  表示客户端退出

                >0   成功接收的字节个数

          

connect

        int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

        功能:用于连接服务器;

        参数:

                sockfd:socket函数的返回值

                addr:    

                        填充的结构体是服务器端的;

                addrlen:

                        结构体的大小

        返回值

                -1 失败

                正确 0

               

send

        ssize_t send(int sockfd, const void *buf, size_t len, int flags);

        功能:发送数据

        参数:

                sockfd:socket函数的返回值

                buf:发送内容存放的地址

                len:发送内存的长度

                flags :    如果填0,相当于write();

TCP流程 

服务器端

流程

        1) socket(),创建套接字文件,用于连接 sockfd(有一个属性默认是阻塞)

        2) bind(), 绑定,把socket()函数返回的文件描述符和IP、端口号进行绑定;

        3) listen(), (监听)将socket()返回的文件描述符的属性,由主动变为被动;       

        4) accept(), 阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,

                                则accept()函数返回,返回一个用于通信的套接字文件;

        5) recv(), 接收客户端发来的数据;   read()

        6) send(), 发送数据;

        7) close(), 关闭文件描述符;连接、通信

代码演示

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    int sockfd, acceptfd;
    if (argc != 2)
    {
        printf("please input %s <port>\n", argv[0]);
        return -1;
    }
    //1.创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    //填充结构体
    struct sockaddr_in serveraddr, clientaddr;
    serveraddr.sin_family = AF_INET;//IPV4
    serveraddr.sin_port = htons(atoi(argv[1]));//从终端输入端口
    //serveraddr.sin_addr.s_addr = inet_addr(argv[2]);//从终端输入ip
    //serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);//"0.0.0.0"自动获取本机地址
    serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //"0.0.0.0"自动获取本机地址

    socklen_t len = sizeof(clientaddr);

    //2.绑定
    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("bind err");
        return -1;
    }
    //3.监听
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err.");
        return -1;
    }
    //4.阻塞等待客户端链接
    while (1)
    {
        if ((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &len)) < 0)
        {
            perror("accept err.");
            return -1;
        }
        printf("client:ip=%s,port=%d\n", inet_ntoa(clientaddr.sin_addr),
               ntohs(clientaddr.sin_port));
        char buf[128];
        int recvbyte;
        while (1)
        {
            recvbyte = recv(acceptfd, buf, sizeof(buf), 0);
            if (recvbyte < 0)
            {
                perror("recv err.");
                return -1;
            }
            else if (recvbyte == 0)
            {
                printf("client exit.\n");
                break;
            }
            else
            {
                printf("buf=%s\n", buf);
            }
        }
        close(acceptfd);
    }
    close(sockfd);
    return 0;
}

客户端

流程

        1) socket(),创建套接字文件,既用于连接,也用于通信;

                完成一个结构体的填充

        2) connect(); 用于发起连接请求;

        3) send(), 发送数据;

        4) recv(), 接收数据;

        5) close(), 关闭文件描述符;

代码演示

#include <stdio.h>
 #include <sys/types.h>  
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    int sockfd,acceptfd;
    if(argc != 3)
    {
        printf("please input %s <ip> <port>\n",argv[0]);
        return -1;
    }
    //1.创建套接字
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
   //填充结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family=AF_INET;//与服务器端一致选择IPV4
    serveraddr.sin_port=htons(atoi(argv[2]));//与服务器端一致,选择相同端口
    serveraddr.sin_addr.s_addr=inet_addr(argv[1]);//终端输入地址
    //2.请求链接服务器
    if(connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr))<0)
    {
        perror("bind err");
        return -1;
    }
    
    char buf[128];
    int recvbyte;
    while(1)
    { 
        fgets(buf,sizeof(buf),stdin);
        if(buf[strlen(buf)-1]=='\n')
            buf[strlen(buf)-1]='\0';//fgets获取\n也存储,故要把\n转换成\0,表示字符串结束。
        send(sockfd,buf,sizeof(buf),0);
    }
    close(sockfd);
    return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值