欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

c语言中如何实现网络通信(流程实例)

程序员文章站 2022-04-12 21:13:36
主要函数: ------------------------------------------ TCP实现服务器与客户端的通信流程 //服务器端---服务器是一个被动的角色...

主要函数:

------------------------------------------

TCP实现服务器与客户端的通信流程

//服务器端---服务器是一个被动的角色

1.socket          //买一个手机

2.bind            //SIM卡 绑定一个手机号(ip+port)

3.listen          //待机(等待电话打入)

4.accept          //接听电话

5.read/write      //通话

6.close           //挂机

//客户端---客户端是一个主动发起请求的一端

1.socket           //买一个手机

2.bind(可选的)     //SIM卡(绑定号码)

3.connect          //拨打电话

4.read/write       //通话

5.close            //挂机

//1.socket  ---- 插口 

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

功能:  创建通信的一端 (socket)

参数:

    @domain    //"域" --范围

               AF_INET  //IPV4 协议的通信

    @type      SOCK_STREAM //TCP (流式套接字)

    @protocol  0           //LINUX下 流式套接字 ==>TCP

              //协议 

返回值:

   成功 对应的socket文件描述符

   失败 返回-1 

注意:

文件描述符:

   实际上就是 创建好的 socket的一个标识符

//2.bind 

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

功能:

    给指定的 socket 绑定地址信息

参数:

   @sockfd    //表示操作的socket

   @addr      //填充的地址信息(ip + port)

   @addrlen   //地址信息结构体的大小

返回值:

   成功 0

   失败 -1

   //通用的地址结构

   struct sockaddr {

       sa_family_t sa_family; //AF_INET //IPV4的协议

       char       sa_data[14];//(ip+port)

   }

//网络通信的地址结构(internet)

struct sockaddr_in {

    sa_family_t    sin_family; /* address family: AF_INET */

    in_port_t      sin_port;   /* port in network byte order */

    struct in_addr sin_addr;   /* internet address */

};

/* Internet address. */

struct in_addr {

    uint32_t       s_addr;      /* address in network byte order */

};

//1.定义一个 地址结构体变量

struct sockaddr_in addr;

bzero(&addr,sizeof(addr)); //清0的函数

//2.之后进行信息填充

addr.sin_family = AF_INET;

addr.sin_port = htons(8888);

addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 

                     //127.0.0.1 是回环测试的地址

//3.进行绑定

if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)

{

    perror("bind fail");

    return 0;

}

//3.listen  --- 设置监听 ---作用:让操作系统监控是否有客户端发起连接

int listen(int sockfd, int backlog);

功能:

   设置监听 

参数:

   @sockfd    //监听套接字

   @backlog   //监听队列的大小

返回值

   成功 0

   失败 -1

   listenfd 

--------------监听队列------------------

fd1 fd2 fd3 fd4

 |

-|--------------------------------------

 |

 \---->建立好连接的套接字 

       accept函数获取已连接的套接字 返回对应

       的标识符 

         |--->后面的读写操作 都是通过这个标识符

              进行的

-----------------------------------------------

accept(); //accept 从监听队列中获得已连接的的socket,返回一个标示符来表示已连接的socket

          //后续通过已连接的socket进行通信

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

功能: 获取连接

参数:

   @sockfd    //监听套接字的fd(标识符)

   @addr     //来电显示(保存对端的地址信息)(ip+port)

   @addrlen  //表示 addr 参数对应类型的大小,值结果参数 --- 就是在用的时候,必须先赋一个初值,最后函数调用完成

             //通过该参数,返回一个结果值

返回值:

   成功 已连接的socket的标识符

   失败 -1

//connect ---发起连接

   int connect(

       int sockfd,  //表示 进行通信的 socket的标识符

       const struct sockaddr *addr, //对端的地址信息(ip+port)

       socklen_t addrlen); //表示的是 addr 参数类型的长度

       

      参数:

        @sockfd   //通过socket函数获得的fd

        @addr     //服务器端的地址

        @addrlen  //参数addr类型的大小 

//数据流向  fd --> buf (count 表示一次读取多少个字节)

ssize_t read(int fd, void *buf, size_t count);

//数据流向  buf--> fd (count 表示一次写多少个字节)

ssize_t    write(int  fd,    const  void  *buf, size_t count);

参数:

   @fd 就是要操作的 socket对应的 标示符

   @buf 保存数据的一块内存首地址

   @count 一次操作的字节数

confd 

char buf[] = "hello QCXY\n";

write(confd,buf,strlen(buf)); //写 数据到socket中

//读数据出来

char rbuf[1024] = {0}; //表示申请了一块1024个字节大小

                       //的内存空间

read(confd,rbuf,sizeof(rbuf)); //读取数据

//练习:

实现 客户端 向服务器发送数据

服务器回发数据的功能

client       -----        server

 scanf();  ---(1)---->        read 之后printf

  read     <--(2)----         write

  printf

  

  循环做,结束条件

  当客户端输入 "quit"字符串时 客户端结束

  怎么判断客户端读到的是"quit"

  

 c语言

 "quit" == buf; (X) //不能这么写

//字符串的比较函数

 strcmp("quit",buf);

 strncmp("quit",buf,4);

客户端的程序:

#include <stdio.h>

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

#include <sys/socket.h>

#include <strings.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <string.h>

//./client 127.0.0.1 8888 

int main(int argc, const char *argv[])

{

    int fd;

    int ret = 0;

    char buf[1024] = {0};

    char rbuf[1024] = {0};

    //处理命令行参数

    //1.socket(手机)

    //2.bind(电话卡)

    //3.connect (拨打电话)

    

    //处理命令行参数

    if(argc != 3)

    {

        printf("Usage: %s <ip> <port>\n",argv[0]);

        return -1;

    }

    //1.socket(手机)

    fd = socket(AF_INET,SOCK_STREAM,0);

    if(fd < 0) //出错处理

    {

        perror("socket fail");

        return -1;

    }

    printf("fd = %d\n",fd);

    //2.bind(电话卡)---绑定的是客户端自己的地址信息

   

     //客户端地址信息

    //1.定义一个 地址结构体变量

    struct sockaddr_in cli_addr;

    bzero(&cli_addr,sizeof(cli_addr)); //清0的函数

    //2.之后进行信息填充

    cli_addr.sin_family = AF_INET;

    cli_addr.sin_port = htons(7777);

    cli_addr.sin_addr.s_addr = inet_addr(argv[1]);

    if(bind(fd,(struct sockaddr*)&cli_addr,sizeof(cli_addr)) < 0)

    {

        perror("bind fail");

        return -1;

    }

    //服务器端的地址信息

    //1.定义一个 地址结构体变量

    struct sockaddr_in addr;

    bzero(&addr,sizeof(addr)); //清0的函数

    //2.之后进行信息填充

    addr.sin_family = AF_INET;

    addr.sin_port = htons(atoi(argv[2]));

    addr.sin_addr.s_addr = inet_addr(argv[1]); 

    //3.connect (拨打电话)

    if(connect(fd,(struct sockaddr*)&addr,sizeof(addr))<0)

    {

        perror("connect fail");

        return -1;

    }

    printf("connect success\n");

   //通信过程

    while(1)

    {

     //客户端从键盘获得数据 

     //数据流向stdin --> buf

     fgets(buf,sizeof(buf),stdin); //stdin表示是从键盘获得数据

     //发送给服务器

     write(fd,buf,strlen(buf));

     //接受服务器回发的消息

     ret = read(fd,rbuf,sizeof(rbuf));

     //如果回发的消息是

     //quit 

     //则结束

     rbuf[ret] = '\0';

     printf("rbuf = %s\n",rbuf);

     

     if(strncmp("quit",buf,4) == 0)

     {

         close(fd);

         break;

     }

    }

    return 0;

}

服务端

#include <stdio.h>

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

#include <sys/socket.h>

#include <strings.h>

#include <netinet/in.h>

#include <arpa/inet.h>

//./server 127.0.0.1 8888

int main(int argc, const char *argv[])

{

    int fd = 0;

    int connfd = 0;

    int ret = 0; 

    char buf[1024] = {0};

    //处理命令行参数

    if(argc != 3)

    {

        printf("Usage: %s <ip> <port>\n",argv[0]);

        return -1;

    }

    //1.socket 创建套接字

    fd = socket(AF_INET,SOCK_STREAM,0);

    if(fd < 0) //出错处理

    {

        perror("socket fail");

        return -1;

    }

    printf("fd = %d\n",fd);

    //2.绑定

    //1.准备地址信息

    //2.绑定

    //

    //1.定义一个 地址结构体变量

    struct sockaddr_in addr;

    bzero(&addr,sizeof(addr)); //清0的函数

    //2.之后进行信息填充

    addr.sin_family = AF_INET;

    addr.sin_port = htons(atoi(argv[2]));

    addr.sin_addr.s_addr = inet_addr(argv[1]); 

    //127.0.0.1 是回环测试的地址

    //3.进行绑定

    if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0)

    {

        perror("bind fail");

        return 0;

    }

    printf("bind success\n");

    //4.设置监听

    if(listen(fd,5) < 0)

    {

        perror("listen fail");

        return -1;

    }

    struct sockaddr_in peer_addr;

    socklen_t addrlen = sizeof(peer_addr); 

    //5.获取连接 -- 接听电话

    while(1) //可以不断接受客户端的请求

    {

        //connfd =  accept(fd,NULL,NULL);

        connfd =  accept(fd,(struct sockaddr*)&peer_addr,&addrlen);

        if(connfd < 0)

        {

            perror("accept fail");

            return -1;

        }

        printf("connfd = %d\n",connfd);

        printf("-----------------------\n");

        printf("ip = %s\n",inet_ntoa(peer_addr.sin_addr));

        printf("port = %d\n",ntohs(peer_addr.sin_port));

        printf("-----------------------\n");

      //通信过程

      //效果实现数据回发

        while(1)

        {

            //read 与 write 的返回值 大于0 时表示的是

            //一次成功操作到的字节数

            ret = read(connfd,buf,sizeof(buf));

            //hello

            buf[ret] = '\0'; //添加'\0'--转换成字符串

            printf("buf = %s\n",buf);//字符串打印 需要

                                     //结束标志 '\0'

            if(ret == 0 || strncmp(buf,"quit",4) == 0)

            {

                close(connfd);

                break;

            }

            write(connfd,buf,ret);

        }

    

    } //telnet <ip> <port>

    return 0;

}

补充:

可以用如下函数替代read,write

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

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

     @sockfd //进行操作的socket的文件描述符

     @buf    //保存数据的首地址

     @len    //一次操作的字节数

     @flags   //标志0--默认的操作方式(阻塞)

返回值:

      成功  成功操作的字节数

     失败   -1&errno