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

news/2024/5/18 15:13:40 标签: 网络编程, 服务器, linux, udp

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发过来的报文信息.
由于最近个人事情比较多,所有很长一段时间都没有去更新了.
欢迎访问博客


http://www.niftyadmin.cn/n/783687.html

相关文章

js 发送短信验证码倒计时

html <input type"button" id"btn" value"免费获取验证码" οnclick"settime(this)" /> js // 短信验证码倒计时var countdown60;function settime(obj) { if (countdown 0) { obj.removeAttribute("disabled&q…

Linux网络编程[DNS解析原理,了解相关DNS解析的函数]

Linux网络编程[浅析DNS原理,了解相关DNS解析的函数] 1.DNS解析的相关原理 2. 域名解析的相关函数 ###端口绑定, DNS解析的相关原理 先通过一张图示来展示一下什么是DNS: 1:每一个域名都是与ip进行绑定的 2:浏览器在访问域名的时候,会自动解析出对应绑定的ip地址 3:整个解…

Linux网络编程[网络中的广播]

Linux网络编程[网络中的广播] 什么是广播网络中广播地址如何去进行广播发送和接收广播实例Demo 什么是广播? 85-95年,这个10年出生的年轻人,对广播都不会陌生的,因为在童年或者青少年时期,某个阶段是由收音机来陪伴的,那收音机利用的是什么原理呢??最简单的就是,需要调频…

Linux网络编程[如何打造守护进程(daemon)]

Linux网络编程[如何打造守护进程(daemon)] 守护进程的含义守护进程的相关编程步骤守护进程的出错处理打造一个我们自己的守护进程 守护进程的含义 守护进程(daemon)是运行在后台的一种特殊的进程.它独立于控制终端并且周期性的执行某种(特殊)任务,或者一直在等待某些事情的发…

C++学习:第一个C++程序

往往学一门新的语言的时候,我们都是从最简单的程序开始的.之前有学过C,但是没有那么系统,所以这一次算是详细,系统的再来一遍吧. 先从Helloworld走起 /** ** Filename: helloWorld.cpp* Description: * Version: 1.0* Created: 2017年05月18日 20…

C++学习:引用和函数的高级用法

C学习:引用和函数的高级用法 引用的概念(引用和引用参数)内联函数默认参数的函数函数重载函数模板 由于本人也是一位新的CCoder,出于对C/C的兴趣爱好,所以学习,这些也都是相关的学习笔记类的.如有写的不好或者不对的地方.求谅解. 引用的概念(引用和引用参数) 为什么要引入引…

C++学习:类,对象,封装

C学习:类,对象,封装 类对象封装 C是一门集面向过程,面向对象以及泛型编程于一体的强大的编程语言,在这里面最重要的要属面向对象了吧???什么是面向对象?面向对象的思想是什么???总结下来就一句话:万物皆对象.在面向对象的世界里,一切都是可以用对象来解释的.这也是面向…

SAP CRM点了附件的超链接后报错的处理方式

SAP CRM系统里&#xff0c;点击了附件的这些超链接后&#xff0c;如果是文本文件&#xff0c;会在浏览器里打开。如果是其他类型的文件&#xff0c;会弹出下载对话框。 然而最近我工作时遇到一个问题&#xff0c;点击超链接后&#xff0c;总是弹出Logon failed的对话框。错误明…