tag 标签: 网络编程

相关博文
  • 热度 25
    2014-6-6 18:19
    3764 次阅读|
    2 个评论
    最近的项目都围绕着TCP socket在进行着,VC下的socket,Linux下的socket,感觉很简单,但是却发现其实下面的几个同事对于socket这个东西还只是“会”,但并不一定懂。   然后和大家在会上讨论了才发现,其实大家都不会socket编程。上网一搜“socket编程”出现的大多就两个代码程序,一个服务器端用来接收N个字节,一个客户端用来连接服务器端并且发送N个字节。想必大多数人都是这么来学习和使用socket的吧。至此,开始记录下个人对socket编程在应用时的一些 稍微深入些的知识。   当然,套接字有很多种,我们常用的是socket套接字,用于网络通信实现,最常用的也就是TCP/UDP的socket编程了。下面以Linux环境的TCP通信为例,说一说我所理解的socket编程。 首先觉得还是同样以那两个比较经典的程序来入门理解下socket编程。 服务器端: …… #define portnumber 3333 int main(int argc, char *argv ;     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)     {         fprintf(stderr,"Socket error:%s\n\a",strerror(errno));         exit(1);     }     bzero(server_addr,sizeof(struct sockaddr_in));     server_addr.sin_family=AF_INET;     server_addr.sin_addr.s_addr=htonl(INADDR_ANY);      server_addr.sin_port=htons(portnumber);     if(bind(sockfd,(struct sockaddr *)(server_addr),sizeof(struct sockaddr))==-1)     {         fprintf(stderr,"Bind error:%s\n\a",strerror(errno));         exit(1);     }     if(listen(sockfd,5)==-1)     {         fprintf(stderr,"Listen error:%s\n\a",strerror(errno));         exit(1);     }     while(1)     {         sin_size=sizeof(struct sockaddr_in);         if((new_fd=accept(sockfd,(struct sockaddr *)(client_addr),sin_size))==-1)         {             fprintf(stderr,"Accept error:%s\n\a",strerror(errno));             exit(1);         }         fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));         if((nbytes=read(new_fd,buffer,1024))==-1)         {             fprintf(stderr,"Read Error:%s\n",strerror(errno));             exit(1);         }                  buffer ='\0';         printf("Server received %s\n",buffer);                 close(new_fd);     }     close(sockfd);     exit(0); } 从上段代码分析看来,其实服务器端实现的流程大概是:建立socket-填充socket需要的网络参数配置-绑定socket套接字-监听来自客户端的连接申请-接收客户端的连接申请,建立socket连接-读写数据。   下面我们在依次分析分析程序中用到的那些和socket有关的代码。   sockaddr_in结构体,它的作用是封装一些建立socket需要用到的参数,包括协议族、IP地址、端口号等,这些都和socket的建立息息相关。 再来看socket()这个函数,首先给出函数原型:   int socket(int domain, int type, int protocol);   第一个参数domain,协议族。本例中使用的是协议族是AF_INET是指ARPA因特网协议族,当然有了AF_INET6以后则表示IPV4协议族,而AF_INET6是指IPV6了。除此之外,还有很多在教材中不容易出现的协议族:AF_UNIX(有人说叫UNIX协议族,但其实确切的说,它指代的是文件系统协议族,从这里可以看出,原来socket不一定都是用来网络通信的),AF_ISO(顾名思义,ISO标准协议族),不同的Linux内核中支持不同的协议族,大家可以在头文件socket.h里面找到。 第二个参数type,类型。这里的类型指的是套接字类型,共分以下几种:流套接字SOCK_STREAM、数据报套接字SOCK_DGRAM、原始套接字SOCK_RAW、包套接字SOCK_PACKET等等。需要说明的是,这里的类型需和第一个参数的协议族对应起来,也就是说有些套接字是只能适用于某个或某些协议族的,好比以上四个套接字类型是适用在AF_INET这个协议族的。分别介绍下这四个套接字类型,很多人直接把SOCK_STREAM理解成TCP,SOCK_DGRAM理解成UDP,那是没问题的。而SOCK_RAW是一个IP层的套接字类型,我们在实现PING功能时就要用到这个套接字类型。SOCK_PACKET是一个链路层的套接字,相对底层一些,用的不多,但是如果你想实现一个用于ARP攻击的程序,它会帮助到你。 第三个参数protocol,协议。也就是具体用到的传输协议。例程中用的值是0,代表的是使用默认协议。那就需要解释一下系统怎么知道默认的协议是什么?答案来自于前两个参数,协议受限于协议族和类型。也就是说,大部分时候前两个参数定下来以后第三个参数可设置性就不那么强了,比如本例程中的,AF_INET和SOCK_STREAM设定后,就代表使用的是TCP协议了,就不能再把第三个参数又设成IPPOTO_UDP来使用UDP协议了。 返回值,如果执行成功则返回一个套接字描述符,例程中将它赋给了sockfd。如果失败的话则返回-1或INVALID_SOCKET。那这个描述符有什么用呢?首先个人理解的描述符就好比是C++下面的类指针。通俗的说,描述符代表了一个内存标识,我们通过这个描述符可以访问到一些相关的变量和函数。因为是内存中的标识,而且描述符是存放在进程的一个特殊列表中,所以它只能用于本进程,就算定义成全局变量也是一样的。   在执行socket函数后,建立了一个套接字并且得到了它的描述符sockfd,之后则是需要具体填充一个sockaddr_in,将协议族、地址、端口号都填充进去。这个sockaddr_in结构体有什么用呢?在于bind函数。   我们之前得到的sockfd描述符只是向操作系统、进程申请得到了部分空间,但并没有和实际的网络服务挂上钩。bind函数则是要将之前的套接字和实际的IP、端口、TCP网络层建立直接的联系。也就是说,在bind之前如果去访问sockfd描述符标识的部分参数或者调用部分函数,都没有任何的实际意义,并不会产生直接的网络动作。而bind之后,则会直接产生真实的网络动作了。   而之后的listen函数则是与与sockfd描述符相关联的函数,目的在于将该服务器socket挂出去接收客户端的连接申请。 对于服务器端,重点则是accept函数。可以看到,该函数通过之前的sockfd描述符得到了另一个新的new_fd描述符。而且在后续的read函数或者write函数所使用的描述符都是这个new_fd。这个如何理解?   饿了,回家吃饭先。明日继续,揭秘些关于socket有趣而真实的小知识。  
相关资源
  • 所需E币: 0
    时间: 2023-12-25 11:06
    大小: 3.48KB
    LinuxSocket网络编程框架主要由3大模块组成:BSDSocketAPIsSocketAbstractionLayerVFSLayerTCP/IP协议在设计和实现上并没有客户端和服务器的概念,在通信过程中所有机器都是对等的。但由于资源(视频、新闻、软件等)都被数据提供者所垄断,所以几乎所有的网络应用程序都很自然地用了客户端/服务器模型,即所有客户端都通过访问服务器来获取所需的资源。BS和CS服务器架构(1)CS架构介绍(clientserver,客户端服务器架构)(2)BS架构介绍(broswerserver,浏览器服务器架构)TCP协议(1)建立连接需要三次握手(2)建立连接的条件:服务器listen时客户端主动发起connect(3)关闭连接需要四次握手(4)服务器或者客户端都可以主动发起关闭packagecom.example.emos.wx.controller.form;importio.swagger.annotations.ApiModel;importlombok.Data;importjavax.validation.constraints.NotBlank;importjavax.validation.constraints.Pattern;@Data@ApiModelpublicclassRegisterForm{  @NotBlank(message="注册码不能为空")  @Pattern(regexp="^[0-9]{6}$",message="注册码必须是6位数字")  privateStringregisterCode;  @NotBlank(message="微信临时授权不能为空")  privateStringcode;  @NotBlank(message="昵称不能为空")  privateStringnickname;  @NotBlank(message="头像不能为空")  privateStringphoto;}在UserController.java中创建login()方法。@PostMapping("/login")@ApiOperation("登陆系统")publicRlogin(@Valid@RequestBodyLoginFormform){intid=userService.login(form.getCode());  Stringtoken=jwtUtil.createToken(id);  Set<String>permsSet=userService.searchUserPermissions(id);  saveCacheToken(token,id);  returnR.ok("登陆成功").put("token",token).put("permission",permsSet);}在CheckinServiceImpl类中,实现抽象方法……publicclassCheckinServiceImplimplementsCheckinService{……publicvoidcreateFaceModel(intuserId,Stringpath){    HttpRequestrequest=HttpUtil.createPost(createFaceModelUrl);    request.form("photo",FileUtil.file(path));    HttpResponseresponse=request.execute();    Stringbody=response.body();    if("无法识别出人脸".equals(body)||"照片中存在多张人脸".equals(body)){      thrownewEmosException(body);    }else{      TbFaceModelentity=newTbFaceModel();      entity.setUserId(userId);      entity.setFaceModel(body);      faceModelDao.insert(entity);    }  }}在CheckinServiceImpl.java类中,实现三个抽象方法。publicclassCheckinServiceImplimplementsCheckinService{……@Override  publicHashMapsearchTodayCheckin(intuserId){    HashMapmap=checkinDao.searchTodayCheckin(userId);    returnmap;  }  @Override  publiclongsearchCheckinDays(intuserId){    longdays=checkinDao.searchCheckinDays(userId);    returndays;  }  @Override  publicArrayList<HashMap>searchWeekCheckin(HashMapparam){    ArrayList<HashMap>checkinList=checkinDao.searchWeekCheckin(param);    ArrayList<String>holidaysList=holidaysDao.searchHolidaysInRange(param);    ArrayList<String>workdayList=workdayDao.searchWorkdayInRange(param);    DateTimestartDate=DateUtil.parseDate(param.get("startDate").toString());    DateTimeendDate=DateUtil.parseDate(param.get("endDate").toString());    DateRangerange=DateUtil.range(startDate,endDate,DateField.DAY_OF_MONTH);    ArrayListlist=newArrayList();    range.forEach(one->{      Stringdate=one.toString("yyyy-MM-dd");      //查看今天是不是假期或者工作日      Stringtype="工作日";      if(one.isWeekend()){        type="节假日";      }      if(holidaysList!=null&&holidaysList.contains(date)){        type="节假日";      }elseif(workdayList!=null&&workdayList.contains(date)){        type="工作日";      }      Stringstatus="";      if(type.equals("工作日")&&DateUtil.compare(one,DateUtil.date())<=0){        status="缺勤";booleanflag=false;        for(HashMap<String,String>map:checkinList){          if(map.containsValue(date)){            status=map.get("status");flag=true;            break;          }        }DateTimeendTime=DateUtil.parse(DateUtil.today()+""+constants.attendanceEndTime);Stringtoday=DateUtil.today();if(date.equals(today)&&DateUtil.date().isBefore(endTime)&&flag==false){          status="";        }      }      HashMapmap=newHashMap();      map.put("date",date);      map.put("status",status);      map.put("type",type);      map.put("day",one.dayOfWeekEnum().toChinese("周"));      list.add(map);    });    returnlist;  }}在EmosWxApiApplicationTests.java类中提供了contextLoads()测试用例方法,我们把生成大量系统消息记录的代码写在其中,程序运行的时候这些消息记录就会写入到MongoDB里面。@SpringBootTestclassEmosWxApiApplicationTests{  @Autowired  privateMessageServicemessageService;  @Test  voidcontextLoads(){    for(inti=1;i<=100;i++){      MessageEntitymessage=newMessageEntity();      message.setUuid(IdUtil.simpleUUID());      message.setSenderId(0);      message.setSenderName("系统消息");      message.setMsg("这是第"+i+"条测试消息");      message.setSendTime(newDate());      Stringid=messageService.insertMessage(message);      MessageRefEntityref=newMessageRefEntity();      ref.setMessageId(id);      ref.setReceiverId(11);//注意:这是接收人ID      ref.setLastFlag(true);      ref.setReadFlag(false);      messageService.insertRef(ref);    }  }}在该页面的模型层里面声明静态数据。list数组保存的是后端Java返回的成员数据,内容上按照部门进行分组。members数组保存的是页面上选择的成员id。#include<stdio.h>#include<sys/socket.h>#include<sys/types.h>#include<stdlib.h>#include<arpa/inet.h>#include<unistd.h>#include<string.h> #defineBACKLOG5 intmain(intargc,char*argv[]){  intfd;  structsockaddr_inaddr;  charbuf[BUFSIZ]={};   if(argc<3){    fprintf(stderr,"%s<addr><port>\n",argv[0]);    exit(0);  }   /*创建套接字*/  fd=socket(AF_INET,SOCK_STREAM,0);  if(fd<0){    perror("socket");    exit(0);  }   addr.sin_family=AF_INET;  addr.sin_port=htons(atoi(argv[2]));  if(inet_aton(argv[1],&addr.sin_addr)==0){    fprintf(stderr,"Invalidaddress\n");    exit(EXIT_FAILURE);  }   /*向服务端发起连接请求*/  if(connect(fd,(structsockaddr*)&addr,sizeof(addr))==-1){    perror("connect");    exit(0);  }  while(1){    printf("Input->");    fgets(buf,BUFSIZ,stdin);    write(fd,buf,strlen(buf));  }  close(fd);  return0;}
  • 所需E币: 0
    时间: 2023-12-25 10:31
    大小: 2.57KB
    上传者: 开心就很好了
    今天我将给大家讲解基于C++的Linux高性能事件驱动网络编程框架的设计方法及技巧,我在文中采取渐进迭代的方式,配合C++11新特性的使用,以及网络编程理论的深度讲解,并手把手带着大家落地实现,助力在网络编程领域有更大的技术提升!Linux系统的性能是指操作系统完成任务的有效性、稳定性和响应速度。Linux系统管理员可能经常会遇到系统不稳定、响应速度慢等问题,例如在Linux上搭建了一个web服务,经常出现网页无法打开、打开速度慢等现象,而遇到这些问题,就有人会抱怨Linux系统不好,其实这些都是表面现象。Linux提供三个「点分十进制字符串表示的IPv4地址和用网络字节序整数表示的IPv4地址之间转换」的接口 publicGraceJSONResultdoLogin(HttpServletRequestrequest,                  HttpServletResponseresponse,                  RegisterLoginBOregisterLoginBO,                  BindingResultresult){  //判断BindingResult是否保存错误的验证信息,如果有,则直接return  if(result.hasErrors()){    Map<String,String>errorMap=getErrors(result);    returnGraceJSONResult.errorMap(errorMap);  }  //获得前端传来的基本信息  StringsmsCode=registerLoginBO.getSmsCode();  Stringmobile=registerLoginBO.getMobile();  //0.校验验证码是否匹配  StringredisSMSCode=redis.get(MOBILE_SMSCODE+mobile);  if(StringUtils.isBlank(redisSMSCode)||!redisSMSCode.equalsIgnoreCase(smsCode)){    returnGraceJSONResult.errorCustom(ResponseStatusEnum.SMS_CODE_ERROR);  }  returnGraceJSONResult.ok();}用户信息其实并不会经常发生变动,所以这块内容完全可以放入缓存,这么一来可以大大减少对数据库的压力。privateAppUsergetUser(StringuserId){  //1.查询redis中是否包含用户信息,如果包含则查询redis返回,如果不包含则查询数据库  StringuserJson=redis.get(REDIS_USER_INFO+":"+userId);  AppUseruser=null;  if(StringUtils.isNotBlank(userJson)){    user=JsonUtils.jsonToPojo(userJson,AppUser.class);  }else{    user=userService.getUser(userId);    //2.由于用户信息不怎么会变动,对于千万级别的网站,这类信息数据不会去查询数据库,完全可以把用户信息存入redis    //哪怕修改信息,也不会立马体现,这也是弱一致性,在这里有过期时间,比如1天以后,用户信息会更新到页面显示,或者缩短到1小时,都可以    //基本信息在新闻媒体类网站是属于数据一致性优先级比较低的,用户眼里看的主要以文章为主,至于文章是谁发的,一般来说不会过多关注    redis.set(REDIS_USER_INFO+":"+userId,JsonUtils.objectToJson(user),1);  }  returnuser;}虽然在表设计的时候把文章阅读数字段进行了设计,但是在大数据量下,文章阅读的累计并发是很高的,在这里我们也是采用redis的计数功能来进行实现。@OverridepublicGraceJSONResultlist(StringarticleId,Integerpage,IntegerpageSize){  if(page==null){    page=COMMON_START_PAGE;  }  if(pageSize==null){    pageSize=COMMON_PAGE_SIZE;  }  PagedGridResultgridResult=         commentPortalService.queryArticleComments(articleId,                           page,                           pageSize);  returnGraceJSONResult.ok(gridResult);}生成html的步骤分为以下几步:定义freemarker生成的html位置配置freemarker基本环境获得ftl模板获得动态数据融合ftl和动态数据,并输出到html@Value("${freemarker.html.target}")privateStringhtmlTarget;@GetMapping("/createHTML")@ResponseBodypublicStringcreateHTML(Modelmodel)throwsException{  //0.配置freemarker基本环境  Configurationcfg=newConfiguration(Configuration.getVersion());  //声明freemarker模板所需要加载的目录的位置  Stringclasspath=this.getClass().getResource("/").getPath();  cfg.setDirectoryForTemplateLoading(newFile(classpath+"templates"));//    System.out.println(htmlTarget);//    System.out.println(classpath+"templates");  //1.获得现有的模板ftl文件  Templatetemplate=cfg.getTemplate("stu.ftl","utf-8");  //2.获得动态数据  Stringstranger=;  model.addAttribute("there",stranger);  model=makeModel(model);  //3.融合动态数据和ftl,生成html  FiletempDic=newFile(htmlTarget);  if(!tempDic.exists()){    tempDic.mkdirs();  }  Writerout=newFileWriter(htmlTarget+File.separator+"10010"+".html");  template.process(model,out);  out.close();  return"ok";}
  • 所需E币: 0
    时间: 2023-12-6 15:17
    大小: 3.8KB
    网络编程概述管道(父子进程)、消息队列(内核经营消息队列)、共享内存(创建一个空间)、信号(通过pid号通信)、信号量(对临界资源,共享内存做P、V控制)。特点:依赖于Linux内核AB两个通信基于内核。缺陷:无法多机通信(不适用与两台不同的电脑)TCP和UDP对比:TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信TCP首部开销20字节;UDP的首部开销小,只有8个字节TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道传统的进程间通信借助内核提供的IPC机制进行,但是只能限于本机通信。若要跨机通信,就必须使用网络通信,这就需要用到内核提供给用户的socketAPI函数库。2.1网络字节序大端字节序:也叫高端字节序(网络字节序),是高端地址存放低位数据,低端地址存放高位数据小端字节序:也叫低端字节序,是低地址存放低位数据,高地址存放高位数据。在application.yml文件中,填入SaToken的配置信息,如下:sa-token: #HTTP请求头中哪个属性用来上传令牌 token-name:token #过期时间(秒),设置为30天 timeout:2592000 #临时有效期,设置为3天 activity-timeout:259200 #不允许相同账号同时在线,新登陆的账号会挤掉原来登陆的账号 allow-concurrent-login:false #在多人登陆相同账号的时候,是否使用相同的Token is-share:false token-style:uuid #是否读取Cookie中的令牌 isReadCookie:false #同端互斥 isConcurrent:false #SaToken缓存令牌用其他的逻辑库,避免业务数据和令牌数据共用相同的Redis逻辑库 alone-redis:  database:1  host:localhost  port:6379  password:abc123456  timeout:10s  lettuce:   pool:    #连接池最大连接数    max-active:200    #连接池最大阻塞等待时间(使用负值表示没有限制)    max-wait:10s    #连接池中的最大空闲连接    max-idle:16    #连接池中的最小空闲连接    min-idle:8Java语言允许我们自己封装异常类,我们可以自定义各种异常类,比如每种业务一个异常类,或者每个模块一个异常类。我这里不想做的那么复杂,不如我们创建一个通用的异常类,用来封装与业务有关的异常信息。在com.example.his.api.exception包中,创建HisException.java类。packagecom.example.his.api.exception;importlombok.Data;@DatapublicclassHisExceptionextendsRuntimeException{  privateStringmsg;  privateintcode=500;  publicHisException(Exceptione){    super(e);    this.msg="执行异常";  }  publicHisException(Stringmsg){    super(msg);    this.msg=msg;  }  publicHisException(Stringmsg,Throwablee){    super(msg,e);    this.msg=msg;  }  publicHisException(Stringmsg,intcode){    super(msg);    this.msg=msg;    this.code=code;  }  publicHisException(Stringmsg,intcode,Throwablee){    super(msg,e);    this.msg=msg;    this.code=code;  }}SpringBoot提供了全局处理异常的技术,只要我们给某个Java类用上@RestControllerAdvice注解,这个类就能捕获SpringBoot项目中所有的异常,然后统一处理(精简异常信息)再返回给前端项目。在com.example.his.api.config包中,创建ExceptionAdvice.java类。packagecom.example.his.api.config;importcn.dev33.satoken.exception.NotLoginException;importcn.felord.payment.PayException;importcn.hutool.json.JSONObject;importcom.example.his.api.exception.HisException;importlombok.extern.slf4j.Slf4j;importorg.springframework.validation.BindException;importorg.springframework.http.HttpStatus;importorg.springframework.http.converter.HttpMessageNotReadableException;importorg.springframework.web.HttpRequestMethodNotSupportedException;importorg.springframework.web.bind.MethodArgumentNotValidException;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.ResponseBody;importorg.springframework.web.bind.annotation.ResponseStatus;importorg.springframework.web.bind.annotation.RestControllerAdvice;importorg.springframework.web.multipart.support.MissingServletRequestPartException;@Slf4j@RestControllerAdvicepublicclassExceptionAdvice{  /*   *捕获异常,并且返回500状态码   */  @ResponseBody  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)  @ExceptionHandler(Exception.class)  publicStringexceptionHandler(Exceptione){    JSONObjectjson=newJSONObject();    if(einstanceofHttpMessageNotReadableException){      HttpMessageNotReadableExceptionexception=(HttpMessageNotReadableException)e;      log.error("error",exception);      json.set("error","请求未提交数据或者数据有误");    }     elseif(einstanceofMissingServletRequestPartException){      MissingServletRequestPartExceptionexception=(MissingServletRequestPartException)e;      log.error("error",exception);      json.set("error","请求提交数据错误");    }     elseif(einstanceofHttpRequestMethodNotSupportedException){      HttpRequestMethodNotSupportedExceptionexception=(HttpRequestMethodNotSupportedException)e;      log.error("error",exception);      json.set("error","HTTP请求方法类型错误");    }     //Web方法参数数据类型转换异常,比如String[]数组类型的参数,你上传的数据却是String类型    elseif(einstanceofBindException){      BindExceptionexception=(BindException)e;      StringdefaultMessage=exception.getFieldError().getDefaultMessage();      log.error(defaultMessage,exception);      json.set("error",defaultMessage);    }    //没有通过后端验证产生的异常    elseif(einstanceofMethodArgumentNotValidException){      MethodArgumentNotValidExceptionexception=(MethodArgumentNotValidException)e;      json.set("error",exception.getBindingResult().getFieldError().getDefaultMessage());    }    //处理业务异常    elseif(einstanceofHisException){      log.error("执行异常",e);      HisExceptionexception=(HisException)e;      json.set("error",exception.getMsg());    }     //微信支付异常    elseif(einstanceofPayException){      PayExceptionexception=(PayException)e;      log.error("微信支付异常",exception);      json.set("error","微信支付异常");    }    //处理其余的异常    else{      log.error("执行异常",e);      json.set("error","执行异常");    }    returnjson.toString();  }  /*   *捕获异常,并且返回401状态码   */  @ResponseBody  @ResponseStatus(HttpStatus.UNAUTHORIZED)  @ExceptionHandler(NotLoginException.class)  publicStringunLoginHandler(Exceptione){    JSONObjectjson=newJSONObject();    json.set("error",e.getMessage());    returnjson.toString();  }}因为Controller类用上@RestController注解之后,Web方法返回的对象会被自动转换成JSON对象,所以我们只需要声明一个封装类,让所有Web方法返回这个封装类的对象即可。除了公共属性之外,不同的Web方法要返回的业务数据也不尽相同,所以选择动态的结构才是最佳的方案,恰好HashMap允许我们随便添加数据,那就选择HashMap作为父类吧。在com.example.his.api.common包中,创建R.java类。packagecom.example.his.api.common;importorg.apache.http.HttpStatus;importjava.util.HashMap;importjava.util.Map;publicclassRextendsHashMap<String,Object>{  publicR(){    //默认创建的R对象中包含了公共的属性    put("code",HttpStatus.SC_OK);    put("msg","success");  }  /*   *覆盖继承的put函数,添加Key-Value数据   */  publicRput(Stringkey,Objectvalue){    super.put(key,value);    //把自己返回,用于链式调用    returnthis;  }  publicstaticRok(){    returnnewR();  }  publicstaticRok(Stringmsg){    Rr=newR();    r.put("msg",msg);    returnr;  }  publicstaticRok(Map<String,Object>map){    Rr=newR();    r.putAll(map);    returnr;  }  publicstaticRerror(intcode,Stringmsg){    Rr=newR();    r.put("code",code);    r.put("msg",msg);    returnr;  }  publicstaticRerror(Stringmsg){    returnerror(HttpStatus.SC_INTERNAL_SERVER_ERROR,msg);  }  publicstaticRerror(){    returnerror(HttpStatus.SC_INTERNAL_SERVER_ERROR,"未知异常,请联系管理员");  }}
  • 所需E币: 1
    时间: 2023-7-11 17:32
    大小: 1.17MB
    上传者: 张红川
    QT中网络编程2.pdf
  • 所需E币: 1
    时间: 2023-7-11 17:32
    大小: 260.64KB
    上传者: 张红川
    QT网络编程.pdf
  • 所需E币: 2
    时间: 2023-4-20 11:35
    大小: 75.17MB
    TCP/IP网络编程-(图灵程序设计丛书)-[韩]尹圣雨
  • 所需E币: 3
    时间: 2023-3-14 13:45
    大小: 89.43MB
    上传者: Argent
    UNIX网络编程卷1:套接字联网API(第3版)
  • 所需E币: 1
    时间: 2022-9-29 20:43
    大小: 15.49MB
    上传者: 西风瘦马
    Linux操作系统与网络编程.pdf
  • 所需E币: 5
    时间: 2022-4-27 19:02
    大小: 103.88MB
    上传者: 文和
    VisualC++网络编程开发与实战
  • 所需E币: 0
    时间: 2022-3-18 16:31
    大小: 15.06KB
    上传者: samewell
    三个输入一个输出的人工神经网络编程.docx
  • 所需E币: 1
    时间: 2022-3-2 13:26
    大小: 36.18MB
    上传者: 西风瘦马
    3204455_Linux网络编程.pdf
  • 所需E币: 0
    时间: 2022-1-25 16:33
    大小: 46.12KB
    上传者: samewell
    《Python网络编程(Linux)》程序实例功能的说明.docx
  • 所需E币: 1
    时间: 2022-1-10 17:47
    大小: 381.93KB
    上传者: Argent
    PHP网络编程从入门到精通
  • 所需E币: 2
    时间: 2021-6-11 10:26
    大小: 187.06KB
    上传者: 若虚
    C++网络编程,Socket详细介绍
  • 所需E币: 5
    时间: 2021-4-19 18:03
    大小: 134.86KB
    上传者: czdian2005
    第16天(网络编程)-每日必修实验.pdf
  • 所需E币: 5
    时间: 2021-3-10 21:30
    大小: 118.83MB
    上传者: htwdb
    linux网络编程资深程序员讲解应用案例
  • 所需E币: 0
    时间: 2020-11-24 14:03
    大小: 404.26KB
    上传者: sense1999
    HarmonyOS网络编程实验指南
  • 所需E币: 5
    时间: 2020-11-8 21:45
    大小: 82.87MB
    上传者: kaidi2003
    UNIX网络编程卷1:套接字联网API(第3版)
  • 所需E币: 0
    时间: 2020-9-20 23:04
    大小: 85.39MB
    上传者: bwj312
    UNIX网络编程卷1:套接字联网API(第3版)
  • 所需E币: 0
    时间: 2020-9-8 00:52
    大小: 381KB
    上传者: samewell
    2011-基本网络编程modi-4.ppt