c语言中如何实现网络通信

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yinbucheng/article/details/51735065

主要函数:

------------------------------------------
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

展开阅读全文

没有更多推荐了,返回首页