Linux学习之网络编程3(高并发服务器)

写在前面

Linux网络编程我是看视频学的,Linux网络编程,看完这个视频大概网络编程的基础差不多就掌握了。这个系列是我看这个Linux网络编程视频写的笔记总结。


高并发服务器

问题:

根据上一个笔记,我们可以写出一个简单的服务端和客户端通信,但是我们发现一个问题——服务器只能连接一个客户端。然而在实际生活中,我们发现一个服务器连接的客户端远远不止一个,所以我们就要做一个高并发服务器。

解决方法:

回看之前的代码,之所以只能一对一通信,是因为服务器只有一次执行accept的机会,一旦建立连接成功,就会去进行通信处理业务,而其他想要建立连接的服务器就没办法建立连接。因此我们想到在Linux系统编程中学的进程和线程,我们可以让父进程(主线程)去监听,一定有客户端请求建立连接,我们就创建子进程(其他线程)去和客户端建立连接进行通信,父进程(主线程)继续监听。


多进程并发服务器

思路(步骤):

  1. 前期准备工作:
    • 先用socket()生成一个套接字lfd用来监听
    • bind()对第一步生成的套接字绑定地址结构(绑的是服务器的地址结构)
    • listen()函数设置lfd的监听上限,最大是128.
  2. 进入循环,accept与客户端建立连接,得到用于通信的套接字的文件描述符cfd
  3. fork()创建子进程
  4. 对于父进程,由于父进程只是监听,不需要与客户端进行通信,所以我们就关闭cfd,注册信号捕捉函数,用来回收子进程,然后一直循环监听。
  5. 对于子进程,由于子进程只是进行通信,不需要监听,所以我们就关闭lfd,然后就与客户端进行通信,处理业务。

源代码:

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>	
#include<signal.h>

#define PORT 6666

void sys_err(char* str)
{
	perror(str);
	exit(-1);
}

void wait_child(int signum)		//信号捕捉,回收子进程
{
	while((waitpid(0,NULL,WNOHANG))>0);
//	if(waitpid(0,NULL,0)!=-1)
//		printf("disconnect a client successfully\n");
	return;
}

int main()
{
	struct sockaddr_in addr_s,addr_c;
	socklen_t addr_c_len=sizeof addr_c;
	int lfd,cfd,res,n;
	pid_t pid;
	struct sigaction act;
	char buf[BUFSIZ],client_IP[1024];
	
	lfd=socket(AF_INET,SOCK_STREAM,0);
	if(lfd<0)
		sys_err("socket error");
		
	addr_s.sin_family=AF_INET;
	addr_s.sin_port=htons(PORT);
	addr_s.sin_addr.s_addr=htonl(INADDR_ANY);
	res=bind(lfd,(struct sockaddr*)&addr_s,sizeof addr_s);
	if(res==-1)
		sys_err("bind error");
		
	res=listen(lfd,128);
	if(res==-1)
		sys_err("listen error");
		
	while(1)
	{
		cfd=accept(lfd,(struct socket*)&addr_c,&addr_c_len);
		
		pid=fork();			//创建子进程
		
		if(pid==0)			//子进程
		{
			close(lfd);		//打印客户端的IP和端口号,可以省略
			printf("connect successfully,client IP:%s,port:%d\n",inet_ntop(AF_INET,&addr_c.sin_addr.s_addr,&client_IP,sizeof client_IP),ntohs(addr_c.sin_port));
			break;
		}
		
		else if(pid>0)
		{
			close(cfd);
			act.sa_handler=wait_child;
			sigemptyset(&act.sa_mask);
			act.sa_flags=0;
			sigaction(SIGCHLD,&act,NULL);
			continue;
		}
	}
	
	if(pid==0)		//父进程
	{
		while(1)
		{
			n=read(cfd,buf,sizeof buf);
			for(int i=0;i<n;i++)
				buf[i]=toupper(buf[i]);
			write(cfd,buf,n);
			write(STDOUT_FILENO,buf,n);
		}
	}
	
	close(cfd);
	close(lfd);
	
	return 0;
}

效果

1


多线程并发服务器

思路(步骤):

  1. 前期准备工作:
    • 先用socket()生成一个套接字lfd用来监听
    • bind()对第一步生成的套接字绑定地址结构(绑的是服务器的地址结构)
    • listen()函数设置lfd的监听上限,最大是128.
  2. 进入循环,accept与客户端建立连接,得到用于通信的套接字的文件描述符cfd
  3. 创建子线程
  4. 对于子线程,由于子线程只是进行通信,不需要监听,所以我们就关闭lfd,然后就与客户端进行通信,处理业务。
  5. 对于父进程,由于父线程只是监听,不需要与客户端进行通信,所以我们就关闭cfd,然后设置线程pthread_detach()分离或者使用pthread_join()回收子线程。

源代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>

#define PORT 6666

void sys_err(char* str)
{
	perror(str);
	exit(-1);
}

void* fun(void* arg)
{
	int cfd=(int) arg,n;
	char buf[BUFSIZ];
	while(1)
	{
		n=read(cfd,buf,sizeof buf);
		if(n==0)
		{
			printf("one client closed......\n");
			break;
		}
		for(int i=0;i<n;i++)
			buf[i]=toupper(buf[i]);
		write(cfd,buf,n);
		write(STDOUT_FILENO,buf,n);
	}
	close(cfd);
	return NULL;
}
	
	

int main()
{
	struct sockaddr_in addr_s,addr_c;
	socklen_t addr_c_len=sizeof addr_c;
	char c_IP[1024];
	int lfd,cfd,res;
	pthread_t tid;

	addr_s.sin_family=AF_INET;
	addr_s.sin_port=htons(PORT);
	addr_s.sin_addr.s_addr=htonl(INADDR_ANY);

	lfd=socket(AF_INET,SOCK_STREAM,0);
	if(lfd<0)
		sys_err("socket errro");

	res=bind(lfd,(struct sockaddr*)&addr_s,sizeof addr_s);
	if(res<0)
		sys_err("bind error");

	res=listen(lfd,128);	
	if(res<0)
		sys_err("listen error");
	
	printf("accepting connect........\n");
	while(1)
	{
		cfd=accept(lfd,(struct sockaddr*)& addr_c,&addr_c_len);
		if(cfd==-1)
			sys_err("accept error");
		printf("connect successfully,client ip:%s,port:%d\n",inet_ntop(AF_INET,&addr_c.sin_addr.s_addr,c_IP,sizeof c_IP),ntohs(addr_c.sin_port));

		res=pthread_create(&tid,NULL,fun,(void*)cfd);
		if(res!=0)
			fprintf(stderr,"pthread create error:%s",strerror(res));

		pthread_detach(tid);	
		if(res!=0)
			fprintf(stderr,"pthread create error:%s",strerror(res));
	}
	close(lfd);
	return 0;
}

效果

1


写在最后

个人亲身经验:我们学习的一系列Linux命令,一定要自己亲手去敲。不要只是看别人敲代码,不要只是停留在眼睛看,脑袋以为自己懂了,等你实际上手去敲会发现许许多多的这样那样的问题。毕竟“实践出真知”。


如果你觉得我写的题解还不错的,请各位王子公主移步到我的其他题解看看

  1. 数据结构与算法部分(还在更新中):
  • C++ STL总结 - 基于算法竞赛(强力推荐
  • 动态规划——01背包问题
  • 动态规划——完全背包问题
  • 动态规划——多重背包问题
  • 动态规划——分组背包问题
  • 动态规划——最长上升子序列(LIS)
  • 二叉树的中序遍历(三种方法)
  • 最长回文子串
  • 最短路算法——Dijkstra(C++实现)
  • 最短路算法———Bellman_Ford算法(C++实现)
  • 最短路算法———SPFA算法(C++实现)
  • 最小生成树算法———prim算法(C++实现)
  • 最小生成树算法———Kruskal算法(C++实现)
  • 染色法判断二分图(C++实现)
  1. Linux部分(还在更新中):
  • Linux学习之初识Linux
  • Linux学习之命令行基础操作
  • Linux学习之基础命令(适合小白)
  • Linux学习之权限管理和用户管理
  • Linux学习之制作静态库和动态库
  • Linux学习之makefile
  • Linux学习之系统编程1(关于读写系统函数)
  • Linux学习之系统编程2(关于进程及其相关的函数)
  • Linux学习之系统编程3(进程及wait函数)
  • Linux学习之系统编程4(进程间通信)
  • Linux学习之系统编程5(信号)
  • Linux学习之系统编程6(线程)
  • Linux学习之系统编程7(线程同步/互斥锁/信号量/条件变量)
  • Linux学习之网络编程(纯理论)
  • Linux学习之网络编程2(socket,简单C/S模型)

✨🎉总结

“种一颗树最好的是十年前,其次就是现在”
所以,
“让我们一起努力吧,去奔赴更高更远的山海”
在这里插入图片描述
如果有错误❌,欢迎指正哟😋

🎉如果觉得收获满满,可以动动小手,点点赞👍,支持一下哟🎉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/319992.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

研0或研一|如何快速入门深度学习?

一、经验建议 1️⃣课程篇 直接上手B站【小土堆PyTorch深度学习快速入门教程】&#xff0c;共计9h50min左右&#xff0c;预计一周就可以学完&#xff0c;比较偏向理论和实践相结合跟李沐学AI B站【动手学深度学习 PyTorch版】刘二大人B站【PyTorch深度学习实践】&#xff0c;…

springboot集成jsp

首先pom中引入依赖包 <!--引入servlet--> <dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId> </dependency> <!--引入jstl标签库--> <dependency><groupId>javax.servle…

echarts 3D地图

vueecharts 3D地图,可自定义地图背景底图。鼠标放上显示弹窗&#xff0c;弹窗自动切换。 <template><div id"gbznt" class"gbznt" ref"gbznt"><img class"mapBg" src"../../../img/propertyTransaction/echart-bg…

二进制与十六进制,二进制与八进制之间的相互转换技巧

目录 1.二进制转换为八进制 2.八进制转换为二进制 3.二进制转换为十六进制 4.十六进制转换为二进制 1.二进制转换为八进制 转换为8进制 第一步&#xff1a;以小数点为分界线&#xff0c;整数部分自右向左&#xff0c;小数部分自左向右每3位取成1位&#xff1a; 整数部分…

码蹄集新手村:绝对值,输入各种类型数,平面、空间三角形面积(坐标)

1.绝对值 虽然用if else判断是否小于0&#xff0c;小于0取相反数也能解决&#xff0c;但可能会出现错误&#xff0c;尽量用fabs()函数 2. 输入各种类型数所需的常见英文字母 字符 %c double %lf float %f 3.平面三角形面积&#xff08;坐标&#xff09; 记住三角形面积公…

人物故事:从留美复旦流浪博士,分享一位杰出C++开发者的故事

近日留美复旦流浪博士的事迹冲上热搜。通过多方渠道消息来源&#xff0c;据说该流浪博士在1985年&#xff0c;15岁还在读高一的他&#xff0c;就已经被复旦大学少年班物理学专业录取。在复旦大学就读期间曾获得李政道奖学金。1988年&#xff0c;19岁时从复旦大学通过第九届CUSP…

【并发】共享模型之管程

共享模型之管程 共享问题 package 并发;public class Test1 {static int a0;public static void main(String[] args) throws InterruptedException {Thread t1new Thread(new Runnable() {Overridepublic void run() {for(int i0;i<5000;i){a;}}});Thread t2new Thread(n…

第 4 章 链表

文章目录 4.1 链表(Linked List)介绍4.2 单链表的应用实例4.3 单链表面试题(新浪、百度、腾讯)4.4 双向链表应用实例4.4.1 双向链表的操作分析和实现4.4.2 课堂作业和思路提示 4.5 单向环形链表应用场景4.6 单向环形链表介绍4.7 Josephu 问题4.8 Josephu 问题的代码实现 4.1 链…

Docker五部曲之四:Docker Compose

文章目录 前言Compose应用程序模型Compose规范顶层属性servicenetworkvolumesconfigssecrets 环境变量.env文件environment属性主机shell中的环境变量 Profiles&#xff08;剖面&#xff09;启动剖面自动启动剖面和依赖项解析 多compose.yml文件共享与扩展构建规范构建属性 部署…

单例模式的八种写法、单例和并发的关系

文章目录 1.单例模式的作用2.单例模式的适用场景3.饿汉式静态常量&#xff08;可用&#xff09;静态代码块&#xff08;可用&#xff09; 4.懒汉式线程不安全&#xff08;不可用&#xff09;同步方法&#xff08;线程安全&#xff0c;但不推荐用&#xff09;同步代码块&#xf…

【我想开发一个小程序,大概需要多少钱?】

小程序开发为什么报价差距很大&#xff1f;主要是因为小程序的实现方法和功能模型不同。 小程序的实现方法&#xff1a; 实现方法主要分为SAAS小程序、定制小程序和第三方平台小程序。不同的实现方法价格都是不一样的&#xff0c;大概的区间如下&#xff1a; SAAS小程序和第三…

Github 2024-01-14 Go开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-01-14统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Go项目10TypeScript项目1 frp: 一个开源的快速反向代理 创建周期&#xff1a;2946 天开发语言&#xff1a;G…

第 2 章 数据结构和算法概述

文章目录 2.1 数据结构和算法的关系2.2 看几个实际编程中遇到的问题2.2.1 问题一-字符串替换问题2.2.2 一个五子棋程序2.2.3 约瑟夫(Josephu)问题(丢手帕问题)2.2.4 其它常见算法问题: 2.3 线性结构和非线性结构2.3.1 线性结构2.3.2 非线性结构 2.1 数据结构和算法的关系 数据 …

扫雷游戏棋盘的打印,判断输赢,深度分析

少年们&#xff0c;大家好&#xff0c;我是博主那一脸阳光&#xff0c;我来分享扫雷的打印和判断输赢&#xff0c;代码如何编写&#xff0c;如何使用&#xff0c;深度理解扫雷的游戏。 数据结构的分析和理论 我上次介绍棋盘的初始化&#xff0c;但是如果不打印出来&#xff0…

AI 大模型面试指南(含答案)大放送!

▼最近直播超级多&#xff0c;预约保你有收获 今晚直播&#xff1a;《大模型构建企业知识库实践》 AI 大模型技术经过2023年的狂飙&#xff0c;2024年必将迎来应用的落地&#xff0c;对 IT 同学来讲&#xff0c;这里蕴含着大量的技术机会&#xff0c;越来越多的企业开始招聘 AI…

2624. 蜗牛排序

说在前面 &#x1f388;不知道大家对于算法的学习是一个怎样的心态呢&#xff1f;为了面试还是因为兴趣&#xff1f;不管是出于什么原因&#xff0c;算法学习需要持续保持。 题目描述 请你编写一段代码为所有数组实现 snail(rowsCount&#xff0c;colsCount) 方法&#xff0c;…

数据库备份脚本嘎嘎香,被秀到了!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

mybatisplus(service CRUD 接口)

一、我们在控制器层都是调用Service层&#xff0c;不会直接调用仓储层。现在我给大家介绍一下怎么快速实现Service 的CRUD 定义接口&#xff1a;IProductService 继承IService<实体> package com.saas.plusdemo;import com.baomidou.mybatisplus.extension.service.ISe…

Sentinel限流、熔断

1、限流 单个服务节点限流 sentinel 提供了两种不同的隔离机制&#xff1a;信号量隔离和线程池隔离&#xff0c;它们的主要区别如下&#xff1a; 信号量隔离&#xff08;Semaphore Isolation&#xff09;&#xff1a; 原理&#xff1a;信号量隔离基于计数器&#xff08;或称令…

iOS swift UISlider改变进度条的高度和圆形滑块的大小

文章目录 1.改变进度条的高度&#xff08;亲测有效&#xff09;2.改变圆形滑块的大小&#xff08;亲测有效&#xff09; 1.改变进度条的高度&#xff08;亲测有效&#xff09; import UIKitclass CustomSlider: UISlider {// 设置轨道高度var trackHeight: CGFloat 10// 重写…
最新文章