UDP套节字编程的实现

服务器端

因为UDP是面向数据报的,所以不能直接使用read(),write()来读写数据。

而是要使用recvfrom(),sendto()来读写数据

1.recvfrom()经socket接收数据

#include<sys/types.h>
#include<sys/socket.h>
//函数原型
ssize_t recvform(int sockfd,void *buf,size_t len, int flags,struct socket *src_addr, socklen_t *addrlen);

ssize_t 相当于 int ,socklen_t 相当于 int 

参数说明:

socket:套接字

buf:缓冲区

len:缓冲区的长度

flags:调用操作方式,是以下一个或多个标志的组合,可通过or操作连接在一起,默认为0

src_addr:指针,指向装有源地址的缓冲区

addrlen:指针,指向from缓冲区长度值

返回值:

如果成功返回读取的字节数,如果失败,返回-1

如果远端关闭了文件描述符,返回0;

 

2.sendto()向指定目的地发送数据

#include<sys/types.h>
#include<sys/socket.h>
//函数原型
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,struct sockaddr *dest_addr, socklen_t addrlen);

参数说明:

socket:套接字

buff:待发送数据的缓冲区

size:缓冲区长度

flags:调用方式标志位,一般为0

dest_addr: 指针,指向目的套接字的地址

addrlen:所指地址的长度

返回值:成功返回发送的字节数,失败返回-1

 

地址转换函数:

这里只介绍基于IPV4的socket编程,我们通常用点分十进制(字符串)表示IP地址,但是在socket编程中,sockaddr_in中的IP地址字段是一个32位的IP

字符串转in_addr的函数:

#include<arpa/inet.h>
//点分十进制字符串转in_addr
int inet_aton(const char *strptr,struct in_addr *addrptr);
in_addr_t inet_addr(const char *strptr);
int inet_pton(int family,const char *strptr,void *addrptr);

in_addr转字符串的函数:

char* inet_ntoa(struct in_addr inaddr);
const char* inet_ntop(int family,const void *addrptr,char *strptr,size_t len);

注:inet_ntoa函数返回了一个char*,很显然函数内部自己开辟了一段内存来保存这个IP的结果,但是这个返回的结果在静态区,并不需要手动释放。

但是在多线程调用inet_ntoa,是否会出现异常?

  • 在APUE中,明确提出inet_ntoa不是线程安全的函数
  • 但是在centos7上测试,并没有出现问题,可能是内部实现加了互斥锁
  • 在多线程环境下,推荐使用inet_ntop,这个函数由调研者提供一个缓冲区保存结果,可以规避线程安全问题

 

server.c

服务器应该做的事:

1.初始化服务器:

1)创建socket文件

2)将socket文件绑定到指定的结构体sockaddr中

2.开始事务循环处理

1)接收来自客户端的请求

2)处理请求,并将处理好的结构返回给客户端

(此处实现简单的回显式服务器-客户端,所以直接将请求打印到显示器)

//server.c   127.0.0.1  9090

 #include<stdio.h>
 #include<stdlib.h>
 #include<sys/socket.h>
 #include<arpa/inet.h>
 #include<netinet/in.h>
 #include<string.h>
 
  int mian(int argc,char* argv*[])
  {
      if(argc!=3)
      {
          printf("Usage:./server[ip][port]\n");
          return 1;
      }
  
      int sockfd = socket(AF_INET,SOCK_DGRAM,0);
      if(sockfd<0)
      {
          peeror("socket error\n");
          return 2;
      }
  
      struct socketaddr_in local_addr;
      local_addr.sin_famliy=AF_INET;
      local_addr.sin_port = htons(atoi(argv[2]));
      local_addr.sin_addr.s_addr = inet_addr(argv[1]);
      int ret = bind(sockfd,(struct sockaddr*)&local_addr,sizeof(local_addr));
      if(ret<0)
      {
          perror("bind\n");
          return 1;
      }
  
      char buf[1024] ={0};
      struct sockaddr_in peer;
      while(1)
        {
          int len  = sizeof(peer);
          ssize_t read_size = recvfrom(sockfd,buf,sizeof(buf)-1,0,(struct sockaddr*)&peer,&len);
          if(read_size<0)
          {
              continue;
          }
          else if(read_size>0)
          {
              buf[read_size]='\0';
              printf("[%s:%d]:%s\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port),buf);
              sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&peer,sizeof(peer));
          }
      }
      return 0;
  }
                        

 

client.c

client要做的事:

1.检查命令行参数

2.创建socket文件,创建一个对端sockaddr结构体,作为发送函数的参数

3.发送请求给服务器

4.接受服务器的响应并打印出来

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值