Linux下的socket简单通信

   日期:2021-03-12     浏览:98    评论:0    
核心提示:编写一个客户端和服务端,要求能够将客户端发送的信息显示出来。编译完在终端运行,端口号和IP地址可以在命令行中指定。

目录:

  • 1. 案例概述
  • 2. 代码(已做详细注释)
    • 2.1 客户端代码
    • 2.2 服务端代码
  • 3. 运行测试:
    • 3.1 编译:
    • 3.2 运行:
    • 3.3 运行结果:
  • 4. 踩坑经历:
    • 4.1 问题:
    • 4.2 解决办法:将IP地址换成127.0.0.1

1. 案例概述

采用主从模式,客户端向服务端发送自己的主机名称。该案例比较简单,目的是练习掌握熟悉套接字编程框架。
编写一个客户端和服务端,要求能够将客户端发送的信息显示出来。编译完在终端运行,端口号和IP地址可以在命令行中指定。

  1. 通信规程(应用层协议)
    客户端将信息发送到服务端,仅发送一次,服务端无反馈。

  2. 服务端(udp01_s.c)
    按主从模式的编程框架构建好后,主要侧重点是其数据传输部分,即通信规程的实现部分。
    该服务端只负责接收数据,不向客户端反馈任何数据。

  3. 客户端(udp01_c.c)
    与服务端类似,侧重点也是通信规程的实现部分。
    客户端只发送数据,不需要接收数据。

2. 代码(已做详细注释)

2.1 客户端代码

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


//client客户端
int main(int argc, char **argv)//argc 是指传入参数的个数,argv[] 是一个指针数组,**argv 是一个指针数组指针
{ 
    const int SIZE_BUF = 1024;
    struct sockaddr_in addr1, addr2;//结构体,用来处理网络通信的地址
    socklen_t addrLen;//addrlen:dest结构体的长度,一般和sizeof()表示
    int sock = -1;
    char *buf = NULL;

    if(argc != 3)
    { 
        printf("Usage: %s port serverip\n", argv[0]);
        return 0;
    }

    //fill server address填写服务器地址
    bzero(&addr1, sizeof(addr1));//初始化结构体 addr1
    
    addr1.sin_family = AF_INET;//设置地址家族
    addr1.sin_port = htons(atoi(argv[1]));//设置端口
    //mysock.sin_addr.s_addr = inet_addr("192.168.1.0"); //设置地址
    //inet_aton 地址转换,将x.x.x.x形式的IP地址串转换为in_addr类型的结构体 成功返回1,失败返回0
    if(0 == inet_aton(argv[2], &addr1.sin_addr))//如果转换失败
    { 
        printf("server-ip is invalid.\n");
        return 1;
    }

    sock = socket(AF_INET, SOCK_DGRAM, 0);//创建套接字(获得fd 套接字描述符)
    if(sock == -1)//如果失败
    { 
        //for linux, perror("xxx") show error description "yyy" in format xxx: yyy
        perror("socket() fail ");
        return 2;
    }

    

    buf = (char*)malloc(SIZE_BUF);


    addrLen = sizeof(addr2);
    //in block mode, wait till error occur, or data arrival
    //recvfrom(sock, buf, SIZE_BUF, 0, (struct sockaddr*)&addr2, &addrLen);

    sprintf(buf,"pid:%d", getpid());//将获取的进程识别码转存到buf中,并输出到屏幕上
    
    
    //如果发送数据失败
    if(-1 == sendto(sock, buf, strlen(buf), 0, (struct sockaddr*)&addr1, sizeof(addr1)))
    { 
        perror("sendto() fail ");
    }
    else
    { 
        printf("sendto() ok\n");
    }

    close(sock);//关闭套接字
    free(buf);

    return 0;
}


2.2 服务端代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>


//server
int main(int argc, char **argv)
{ 
    const int SIZE_BUF = 1024;
    char *buf = (char*)malloc(SIZE_BUF);
    struct sockaddr_in addr1, addr2;
    int sockfd = -1;

    if(argc != 3)
    { 
        printf("Usage: %s port serverip\n", argv[0]);
        return 0;
    }

    bzero(&addr1, sizeof(addr1));                       //初始化结构体 addr1
    addr1.sin_family = AF_INET;                         //设置地址家族
    addr1.sin_port = htons(atoi(argv[1]));              //设置端口
    addr1.sin_addr.s_addr = inet_addr(argv[2]);  //设置地址
    int inet_aton1 = inet_aton(argv[2], &addr1.sin_addr);        //地址转换
    if(inet_aton1 == 0)        //如果转换失败
    { 
        printf("server-ip is invalid.\n");
        return 1;
    }
    //服务器工作过程:
    // 1)创建数据报套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd == -1)//如果失败
    { 
        //for linux, perror("xxx") show error description "yyy" in format xxx: yyy
        perror("socket() fail ");
        return 2;
    }
    // 2)绑定地址和端口信息,端口需要通知所有客户端,
    int bind1 = bind(sockfd, (struct sockaddr*)&addr1, sizeof(addr1));
    if(bind1 == -1)
    { 
        perror("bind() fail ");
        return 3;
    }
    // 3)等待接收客户端数据

    // 4)收到数据后,按照通信规程(也可称为业务逻辑)处理,一般要反馈
    int len = sizeof(addr2);
    int recvfrom1 = recvfrom(sockfd,buf,SIZE_BUF,0,(struct sockaddr*)&addr2,&len);
    if(recvfrom1 == -1)
    { 
        perror("recvfrom() fail ");
    }
    else
    { 
        printf("recvfrom() ok\n");
    }
    // 5)在必要的情况下,关闭套接字
    close(sockfd);
    return 0;


}

3. 运行测试:

注意:先运行服务端代码,再运行客户端代码!!!

3.1 编译:

//服务端
gcc server2.c -o server2

//客户端
gcc client.c -o client

3.2 运行:

//服务端
./server2 1234 127.0.0.1

//客户端
./client 1234 127.0.0.1

3.3 运行结果:


4. 踩坑经历:

4.1 问题:

第一次在终端输入的地址是192.168.0.1,程序始终无法运行,客户端可以发送数据包,服务端一直显示bind()失败。

后来才明白:默认情况下,登陆路由器管理界面的地址是192.168.0.1或192.168.1.1。即192.168.0.1已经和路由器的LAN端口进行绑定,所以会一直bind()失败。

4.2 解决办法:将IP地址换成127.0.0.1

127.0.0.1是回送地址,指本地机,一般用来测试使用。回送地址(127.x.x.x)是本机回送地址(Loopback Address),即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,协议软件立即返回,不进行任何网络传输。

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
更多>相关资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服