Linux网络编程之循环服务器(其一)

news/2024/9/23 14:35:27/

文章目录


本博客参考《Linux C/C++服务器开发实践》—— 朱文伟 李建英

什么是循环服务器

循环服务器在同一个时刻只能响应一个客户端的请求,处理完一个客户端的工作后,才能处理下一个客户端的工作,就好像分时工作一样。循环服务器指的是对于客户端的请求和连接,服务器在处理完毕一个后,再处理另一个,即串行处理客户机的请求。这个I/O模型对应的是同步阻塞模型。
有关五种网络I/O模型的详细了解,请参考下面的博客:
链接:Linux网络编程之I/O模型(详解)

UDP循环服务器

UDP循环服务器的实现方法:UDP服务器每次从套接字上读取一个客户端的请求并处理,然后将处理结果返回给客户端。伪代码如下:

socket();
bind();
while(1){recvfrom();process();sendto();
}

因为UDP是无连接的,没有一个客户端可以一直占用服务器服务器能满足每一个客户端的请求。

一个简单的UDP循环服务器代码

udpserver.cpp

#include "myhead.h"#define PORT 10024
char rbuf[50], sbuf[100];int main(){int sockfd, size, ret, val;char on = 1;struct sockaddr_in saddr;struct sockaddr_in raddr;//设置地址信息,IP信息size = sizeof(struct sockaddr_in);val = sizeof(struct sockaddr);memset(&saddr, 0, size);saddr.sin_family = AF_INET;saddr.sin_port = htons(PORT);saddr.sin_addr.s_addr = htonl(INADDR_ANY);//创建UDP套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){perror("create sockfd fail\n");return -1;}//设置端口复用setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));//绑定地址信息ret = bind(sockfd, (struct sockaddr*)&saddr, val);if(ret < 0){perror("bind fail\n");return -1;}while(1){puts("waitting data\n");ret = recvfrom(sockfd, rbuf, sizeof(rbuf), 0, (struct sockaddr*)&raddr, (socklen_t*)&val);if(ret < 0){perror("recvfrom failed\n");}printf("recv data:%s\n", rbuf);snprintf(sbuf, sizeof(sbuf), "server has received your data(%s)\n", rbuf);ret = sendto(sockfd, sbuf, sizeof(rbuf), 0, (struct sockaddr*)&raddr,val);memset(rbuf, 0, sizeof(rbuf));}close(sockfd);getchar();return 0;
}

udpclient.cpp

#include <stdio.h>
#include <winsock.h>
#pragma comment(lib, "wsock32")
#define BUF_SIZE 200
#define PORT 10024
#define IP "192.168.159.128"
char wbuf[50], rbuf[100];int main()
{SOCKET s;int len = -1;WSADATA wsadata;struct hostent* phe;struct servent* pse;struct protoent* ppe;struct sockaddr_in saddr, raddr;int fromlen, ret, type;if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) {printf("WSAStarup failed\n");return -1;}memset(&saddr, 0, sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(PORT);saddr.sin_addr.s_addr = inet_addr(IP);if ((ppe = getprotobyname("UDP")) == 0) {printf("get protocol information error \n");WSACleanup();return -1;}s = socket(PF_INET, SOCK_DGRAM, ppe->p_proto);if (s == INVALID_SOCKET) {printf(" create socket error \n");WSACleanup();return -1;}fromlen = sizeof(struct sockaddr);printf("please inter data:");scanf_s("%s", wbuf, sizeof(wbuf));ret = sendto(s, wbuf, sizeof(wbuf), 0, (struct sockaddr*)&saddr, sizeof(struct sockaddr));if (ret < 0) {perror("sendto failed\n");}len = recvfrom(s, rbuf, sizeof(rbuf), 0, (struct sockaddr*)&raddr, &fromlen);if (len < 0) perror("recvfrom fail\n");printf("server reply:%s\n", rbuf);closesocket(s);WSACleanup();return 0;
}

注意,为了更贴近一线企业实战环境,客户端的程序是放到Windows上运行的,所以它的代码也使用了Windows系统提供的接口。该代码可以在Visual C++上运行,如果想深入了解Visual C++,可以参考清华大学出版社的《Viusal C++ 2017从入门到精通》一书。

TCP循环服务器

TCP服务器接受一个客户端的连接,然后处理,完成了这个客户端的所有请求后,断开连接。伪代码如下:

socket();
bind();
listen();
while(1){accept();process();close();
}

注:TCP循环服务器一次只能处理一个客户端的请求(除非使用一客户端一线程)。只有在这个客户端的所有请求都满足后,服务器才可以继续后面的请求。如果一个客户端占住服务器不放,则其他的客户端都不能工作,因此TCP服务器一般很少使用这个模型。
链接: 想了解更多服务器模型,可以参考这里

一个简单的TCP循环服务器代码

tcpserver.cpp

#include "myhead.h"
#define  BUF_SIZE  200
#define PORT 8888int main()
{struct   sockaddr_in fsin;int       clisock,alen, connum = 0, len, s;char     buf[BUF_SIZE] = "hi,client", rbuf[BUF_SIZE];struct  servent  *pse;    /* server information    */struct  protoent *ppe;    /* proto information     */struct sockaddr_in sin;   /* endpoint IP address   */memset(&sin, 0, sizeof(sin));sin.sin_family = AF_INET;sin.sin_addr.s_addr = INADDR_ANY;sin.sin_port = htons(PORT);s = socket(PF_INET, SOCK_STREAM, 0);if (s == -1){printf("creat socket error \n");getchar();return -1;}if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1){printf("socket bind error \n");getchar();return -1;}if (listen(s, 10) == -1){printf("  socket listen error \n");getchar();return -1;}while (1){alen = sizeof(struct sockaddr);puts("waiting client...");clisock = accept(s, (struct sockaddr *)&fsin,(socklen_t*)&alen);if (clisock == -1){printf("accept failed\n");getchar();return -1;}connum++;printf("%d  client  comes\n", connum);len = recv(clisock, rbuf, sizeof(rbuf), 0);if (len < 0) perror("recv failed");sprintf(buf,"Server has received your data(%s).", rbuf);send(clisock, buf, strlen(buf), 0);close(clisock);}return 0;
}

tcpclient.cpp

#include "myhead.h"
#define  BUF_SIZE  200
#define PORT 8888
char wbuf[50], rbuf[100];
int main()
{char   buff[BUF_SIZE];SOCKET  s;int    len;WSADATA  wsadata;struct hostent *phe;      /*host information     */struct servent *pse;      /* server information  */struct protoent *ppe;     /*protocol information */struct sockaddr_in saddr;   /*endpoint IP address  */int   type;if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0){printf("WSAStartup failed\n");WSACleanup();return -1;}memset(&saddr, 0, sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(PORT);saddr.sin_addr.s_addr = inet_addr("192.168.0.153");s = socket(PF_INET, SOCK_STREAM, 0);if (s == INVALID_SOCKET){printf(" creat socket error \n");WSACleanup();return -1;}if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr)) == SOCKET_ERROR){printf("connect socket  error \n");WSACleanup();return -1;}printf("please enter data:");scanf_s("%s", wbuf, sizeof(wbuf));len = send(s, wbuf, sizeof(wbuf), 0);if (len < 0) perror("send failed");len = recv(s, rbuf, sizeof(rbuf), 0);if (len < 0) perror("recv failed");printf("server reply:%s\n", rbuf);closesocket(s);WSACleanup();return 0;
}

都读到这了,免费给个三连支持一下吧,下一期我会继续努力~

附录(myhead.h)

// myhead.h
#ifndef _MYHEAD_H
#define _MYHEAD_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/input.h>  //跟输入子系统模型有关的头文件
#include <dirent.h>
#include <stdbool.h>
#include <strings.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <poll.h>
#include <sys/epoll.h>
#endif

http://www.ppmy.cn/news/1511952.html

相关文章

https握手过程详解

https握手过程详解 上一篇《HTTPS通讯全过程》中https握手过程实际上还有更多的细节&#xff0c;为什么会这样设计呢&#xff1f;是因为一开始将握手过程时&#xff0c;吧步骤说的太详细会导致更难理解惹。所以我就先在上一篇把部分细节忽略&#xff0c;把原来几步的过程先简化…

微服务中的Sidecar模式

微服务中的Sidecar模式 什么是sidecarsidecar如何工作Sidecar 代理服务注册发现Sidecar 代理异构服务发起服务调用异构服务如何被调用 常见应用以MOSN流量接管为例使用 sidecar 模式的优势sidecar和面向切片编程AOP的关系参考 什么是sidecar sidecar是服务网络架构的产物。 S…

C#中客户端直接引用服务端Proto文件

gRPC 客户端是从 .proto 文件生成的具体客户端类型。 具体 gRPC 客户端具有转换为 .proto 文件中 gRPC 服务的方法。 下一步打开【服务引用】 控制面板 选择grpc选项&#xff0c;然后继续 到此配置完成&#xff0c;然后就和服务共用一份protocol文件

startData

某音startData 记得加入学习群&#xff1a; python爬虫&js逆向3 714283180

前端工程化-02.Yapi接口管理平台

YApi Pro-高效、易用、功能强大的可视化接口管理平台 图片中链接已失效&#xff0c;此处为新链接 选择添加接口&#xff0c;输入接口名称和接口路径。 创建成功后自动进入接口页面&#xff0c;点击编辑&#xff0c;可以编辑完善这个接口的信息。两个非常重要的信息&#xff1a…

虚拟地址空间与内存映射

虚拟地址空间 对于32位操作系统而言&#xff0c;每个进程都有4G大小的虚拟地址空间。 地址范围&#xff0c;表示程序的寻址能力。我们所谓的虚拟地址空间本质就是一个地址范围。看到的虚拟地址&#xff0c;都是在这个4G的范围内 对于32位操作系统而言&#xff0c;其虚拟地址空间…

【电路笔记】-无源衰减器总结

无源衰减器总结 文章目录 无源衰减器总结1、概述2、L-型无源衰减器设计3、T-型无源衰减器设计4、桥接 T 型衰减器设计5、π型无源衰减器设计无源衰减器是一个纯电阻网络,可用于控制输出信号的电平。 1、概述 无源衰减器是一种纯电阻网络,用于削弱或“衰减”传输线的信号电平…

YOLOV8分类任务更换主干mobilenetv2网络

目录 1.定义模块 2.导入模块 3.task.py文件更改 4.更改后找文件运行一下 1.定义模块 自定义Movilenet_v2类 class Mobilenet_v2(nn.Module):def __init__(self):super().__init__()model models.mobilenet_v2(pretrainedTrue)self.layernn.Sequential(model.features,)def …