简单UDP网络程序 --网络编程套接字

在之前几篇中,都是关于系统编程的笔记整理,今天开始整理网络编程部分

预备知识:

IP地址
IP地址是IP协议中用来表示网络中的不同主机的地址;
IP协议有两个版本,PIv4和IPv6,这里默认用是是IPv4
一台主机可能有多个IP地址(与网卡数量或者虚拟网卡数量有关)
通常用点分十进制的字符串表示
端口号
IP地址+端口号能够标识网络中的唯一一台主机的某一个进程
端口号是一个2个字节16位的整数
一个进程可以绑定多个端口号,一个端口号只能被一个进程绑定
初始TCP、UDP协议

  • 都是传输层协议
  • TCP协议是建立连接的可靠
  • UDP协议是无连接的不可靠
  • TCP协议是面向字节流的
  • UDP协议是面向数据报的

网络字节序
机器有大端小端之分,低地址存放数据低字节位小端序列
那么网络中为了统一字节序列,规定网络字节序为大端序列
发送主机是发送数据按照从低地址到高地址的顺序
接收主机也是按照内存中从低地址到高地址的顺序保存的

那么为了能够保证收发数据的准确性,需要调用以下函数来进行网络字节序和主机字节序之间的转换。

       #include <arpa/inet.h>

       uint32_t htonl(uint32_t hostlong);//主机字节序转向网络字节序

       uint16_t htons(uint16_t hostshort);

       uint32_t ntohl(uint32_t netlong);//网络字节序转向主机字节序

       uint16_t ntohs(uint16_t netshort);

h代表主机,n代表网络,l表示32位长整形,s表示16位短整型
并且即使本台机器是大端,也要调用这些函数进行转换,只不过这些函数不做转换而已,也是为了保证代码的可移植性,在不同的机器上面也能正常运行

用到的socket接口

////创建socket文件描述符(客户端+服务器端,TCP/UDP)
//int socket(int domain,int type,int protocol);
////绑定端口号(服务器 TCP/UDP)
//int bind(int socket,const struct  sockaddr *address,socklen_t address_len);
//接收对端的数据(服务器+客户端,UDP)
//ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
//向对端发送数据(服务器+客户端,UDP)
//ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

service.c

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

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


//****************UDP*************************
//****************服务器**********************

创建socket文件描述符(客户端+服务器端,TCP/UDP)
//int socket(int domain,int type,int protocol);
绑定端口号(服务器 TCP/UDP)
//int bind(int socket,const struct  sockaddr *address,socklen_t address_len);
//接收对端的数据(服务器+客户端,UDP)
//ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
//向对端发送数据(服务器+客户端,UDP)
//ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

//0,绑定端口号
//1,接收客户端的请求(request)
//2,经过计算处理,做出响应
//   因为此处是echo_server 回显服务,所以省去计算处理,只做以下几步处理
//   a)接收到请求后,将其保存下来
//   b)显示到标准输出
//3,将响应发送给客户端


// ./service [IP] [Port]
//  要绑定的IP地址和端口号
int main(int argc ,char * argv[])
{
  //判断命令行参数是否正确
  if(argc!=3)
  {
    printf("Usage:./service [IP] [Port]\n");
    return 0;
  }

  //****************创建socket套接字*********************************
  int fd=socket(AF_INET,SOCK_DGRAM,0);
  if(fd<0)
  {
    perror("socket");
    exit(1);
  }

  //***************将所要绑定的IP地址和Port信息放到sockaddr_in结构体中
  sockaddr_in addr;
  addr.sin_family=AF_INET;//指定为IPv4协议
  //用到了inet_addr()函数,实现两个功能 
  //1.将字符串格式的点分十进制的IP地址转换为数字
  //2.并且该函数会将主机字节序转换为网络字节序
  addr.sin_addr.s_addr=inet_addr(argv[1]);

  //要将端口号转换为数字,并且要进行网络字节序的转换
  addr.sin_port=htons(atoi(argv[2]));
  socklen_t sock_len=sizeof(addr);


  //***************服务器绑定端口号*************************************
  if((bind(fd,(sockaddr *)&addr,sock_len))<0)
  {
    perror("bind");
    exit(2);
  }
  printf("bind success\n");
  //**************服务器进行事件循环************************************
  char buf[1024]={0};
  sockaddr_in client;
  socklen_t client_len=sizeof(client);

  while(1)
  {
    //1.接收来自客户端的请求
    ssize_t read_size=recvfrom(fd,buf,sizeof(buf)-1,0,(sockaddr *)&client,&client_len); //这里的client_len既是输入型参数又是输出型参数

    if(read_size<0)
    {
      //读取错误
      printf("读取失败\n");
      continue;
    }
    if(read_size==0)
    {
      //读到文件结束,表示当前客户端已经关闭socket
      printf("read done\n");
      return 0;
    }
    else
    {
      buf[read_size]='\0';
      //2.将收到的内容显示到标准输出

      printf("client: %s:%d say %s",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf);
      fflush(stdout);
      //3.收到的内容发回给客户端
      sendto(fd,buf,read_size,0,(sockaddr *)&client,client_len);
    }
  }
  close(fd);

}

client.c

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

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

//***************************客户端***************************
//***************************UDP*****************************

//向服务器发送请求
//   发送内容从标准输入读取
//接收服务器的响应
//   将接收的响应显示到标准输出上

// ./client IP  Port
// 向哪个服务器发送请求
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
int main(int argc,char * argv[])
{

  if(argc!=3)
  {
    printf("Usage [IP] [Port]\n");
    exit(1);
  }
  //创建socket
  int fd=socket(AF_INET,SOCK_DGRAM,0);
  if(fd<0)
  {
    perror("socket");
  }
  //将要发送给哪个服务器的信息赋值sockaddr_in

  sockaddr_in service;
  service.sin_family=AF_INET;
  service.sin_addr.s_addr=inet_addr(argv[1]);
  //将端口号转换位网络字节序
  service.sin_port=htons(atoi(argv[2]));
  socklen_t service_len=sizeof(service);

  //从标准输入读取信息
  char buf[1024]={0};

  //**************接收服务器的响应************************
  while(1)
  {
    ssize_t read_size=read(0,buf,sizeof(buf)-1);
    if(read_size<0)
    {
      perror("read");
      return 1;
    }
    if(read_size==0)
    {
      //读取到文件结束
      printf("done\n");
    }
    buf[read_size]='\0';

    //***************将读取的内容发送给服务器****************
    int send_ret=sendto(fd,buf,read_size,0,(sockaddr *)&service, service_len);
    if(send_ret<0)
    {
      perror("send");
      return 1;
    }
    char service_buf[1024]={0};
    read_size=recvfrom(fd,service_buf,sizeof(service_buf)-1,0,NULL,NULL);
    if(read_size<0)
    {
      perror("recvfrom");
      return 1;
    }
    if(read_size==0)
    {
      printf("read done\n");
    }
    else
    {
      buf[read_size]='\0';
      printf("service response :%s",service_buf);
      fflush(stdout);
    }
  }
  close(fd);
}

结果

完。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值