Linux网络编程[UDP客户端服务器的编程模型]

Linux网络编程[UDP客户端服务器的编程模型]

  1. 编程模型概述
  2. 相关函数
  3. 实例demo

编程模型概述

从一个图示开始:
这里写图片描述
从上述图示中我们都可以看到,UDP的传输相对来说比TCP传输的时候要简单很多,因为其不需要建立稳定连接,只是相互端口之间的发送,对于一些传输效率需求比较高的应用场景来说,是比较适合的,但是其在稳定性,可靠性上就有一定差异了

相关函数

发送数据:

#include<sys/socket.h>
ssize_t send(int sockfd,const void* buf,size_t nbytes,int flag);(调用connect过后可以直接调用send方法)
返回:成功返回发送字节数,出错返回-1

ssize_t sendto(int sockfd,const void * buf,size_t nbytes,int flag,const struct sockaddr * destaddr,socklen_t destlen);
返回:成功返回发送字节数,出错返回-1

ssize_t sendmsg(int sockfd,const struct msghdr *msg,int flag);
返回:成功返回发送字节数,出错返回-1

struct msghdr{
    void *msg_name; /**/
    socklen_t msg_namelen;/**/
    struct iovec * msg_iov;/**/
    int msgiovlen; /**/
    void *msg_control;/**/
    socklen_t msg_controllen;/**/
    int msg_flag;/**/
}

接收数据

 #include<sys/socket.h>
 ssize_t recv(int sockfd,void *buf,size_t nbytes,int flag);

 ssize_t recvfrom(int sockfd,void * restrict buf,size_t len,int flag,struct sockaddr* restrict addr,socklen_t *restrict addrlen);
 一般flag为0;

 ssize_t recvmsg(int sockfd,struct msghdr *msg,int flag);
 返回:返回消息的字节数,无消息返回0,出错返回-1

不多说上代码

/*
 * ===========================================================================
 *
 *       Filename:  udp_server.c
 *    Description:
 *    基于udp协议的一个server端的程序(udp协议没有差错校验)面向无连接的不可靠的协议 
 *        Version:  1.0
 *        Created:  2017年05月13日 09时27分26秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netdb.h>
#include<string.h>
#include<fcntl.h>
#include<signal.h>
#include<memory.h>
#include<time.h>

int sockfd;
void sig_handler(int signo){
  if(signo == SIGINT){
    perror("sig interrupt and pid cancel");
    exit(1);
    close(sockfd);
  }
}

void out_client_info(struct sockaddr_in *clientaddr){
  char ip[16];
  int port;
  memset(ip,0,sizeof(ip));
  inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));
  port = ntohs(clientaddr->sin_port);

  printf("client:%s(%d)\n",ip,port);
}


void do_service(){
  struct sockaddr_in clientaddr;
  socklen_t len = sizeof(clientaddr);
  char buff[1024];
  memset(&clientaddr,0,len);
  ssize_t size = recvfrom(sockfd,buff,sizeof(buff),0,(struct sockaddr *)&clientaddr,&len);
  if(size < 0 ){
        printf("recv error");
  }else{
    out_client_info(&clientaddr);
    printf("client send info:%s\n",buff);
    time_t t = time(0);
    char *ptr = ctime(&t);
    ssize_t time_size = strlen(ptr) * sizeof(char);
    if(sendto(sockfd,ptr,time_size,0,(struct sockaddr*)&clientaddr,len) < 0){
      perror("send to error");
    }
 }
}


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

  //确定绑定的端口,从命令行传入参数,第二个参数是绑定端口的参数
  if(argc < 2){
    printf("usage :%s port\n",argv[0]);
    exit(EXIT_FAILURE);
  }
 if(signal(SIGINT,sig_handler) == SIG_ERR){
   perror("signal sigint error");
   exit(EXIT_FAILURE);
 } 
 /* *
  *创建socket
  *
  * */
  sockfd = socket(AF_INET,SOCK_DGRAM,0);
  if(sockfd < 0 ){
    perror("socket create error");
    exit(EXIT_FAILURE);
  }

  int res; 
  int opt = 1;

  /* *
   *设置端口停止后,后续端口能够立马再次使用
   * */
  if((res = setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt))) <0){
    perror("setsockopt error");
    exit(1);
  }

 /* *
  *调用bind函数对socket和地址进行绑定
  sockaddr--->socket通用地址
  sockaddr_in--->socket专用地址
  * */ 
  struct sockaddr_in addr_in;
  memset(&addr_in,0,sizeof(addr_in));
  addr_in.sin_family = AF_INET;  //IPV4
  addr_in.sin_port = htons(atoi(argv[1])); //端口必须要是网络字节序
  addr_in.sin_addr.s_addr = INADDR_ANY;  //获得主机上面所有ip地址的请求
  if(bind(sockfd,(struct sockaddr *)&addr_in,sizeof(addr_in)) < 0){  //对端口进行绑定
     perror("bind error");
     exit(EXIT_FAILURE);
  }
  /* *
   *
   * */
  while(1){
    do_service();
  }
  return 0;
}
/*
 * ===========================================================================
 *
 *       Filename:  udp_client.c
 *    Description:  基于udp协议的一个client端
 *        Version:  1.0
 *        Created:  2017年05月13日 10时09分41秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:   (), 
 *        Company:  
 *
 * ===========================================================================
 */

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netdb.h>
#include<memory.h>



int client_sockfd;

int main(int argc,char * argv[]){
  if(argc < 3){
    printf("usage :%s ip port\n",argv[0]);
    exit(EXIT_FAILURE);
  }

  client_sockfd = socket(AF_INET,SOCK_DGRAM,0);
  if(client_sockfd < 0 ){
    perror("create client socket error");
    exit(EXIT_FAILURE);
  }

  /**
   *一般在客户端是不需要去进行绑定操作的,调用recv和sendto进行双向通信
   */
  struct sockaddr_in serveraddr;
  memset(&serveraddr,0,sizeof(serveraddr));
  serveraddr.sin_family = AF_INET; //IPV4
  serveraddr.sin_port = htons(atoi(argv[2]));//端口
  inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr); //ip

 /* *
  *注意:如果在udp中调用connect的话,不会进行握手动作,只是在内核中去记住了地址信息,这个时候可以直接使用send去发送信息,而不需要再去进行调用sendto函数

  调用connect的好处就是客户端接收到的数据报文仅仅是服务器发过来的,如果不去调用的话,则很有可能是其他服务器发过来的
  *
  * */ 
  int connect_result;
  connect_result = connect(client_sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
  if(connect < 0){
    perror("connect error");
    exit(EXIT_FAILURE);
  }


  char buff[1024] = "helloworld";
  ssize_t client_size_t;
  client_size_t = sendto(client_sockfd,buff,sizeof(buff),0,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
  if(client_size_t < 0){
    perror("sendto error");
    exit(1);
  } else {
     memset(buff,0,sizeof(buff));
     size_t size;
     size = recv(client_sockfd,buff,sizeof(buff),0);
     if(size <0){
        perror("recv error");
        exit(EXIT_FAILURE);
     }else{
        printf("%s",buff);
     }
  }
 close(client_sockfd); 

  return 0;
}

代码都是经过调试的,udp_server端负责去接收client发过来的报文信息.
由于最近个人事情比较多,所有很长一段时间都没有去更新了.
欢迎访问博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值