linux网络编程 | c | 多线程并发服务器实现

多线程并发服务器

基于该视频完成

12-多线程并发服务器分析_哔哩哔哩_bilibili

通过的是非阻塞忙轮询的方式实现的

和阻塞等待的区别就是,阻塞是真的阻塞了,而这个方式是一直在问有没有请求有没有请求

linux | c | 多进程并发服务器实现-CSDN博客

可以先看看这篇博客,思路和功能都一样

文章目录

1.核心思路&功能

实现一个服务器可以连接多个客户端,每当accept函数等待到客户端进行连接时 就创建一个子进程;

核心思路:让accept循环阻塞等待客户端,每当有客户端连接时就fork子进程,让子进程去和客户端进行通信,父进程用于监听并使用信号捕捉回收子进程;(子进程关闭用于监听的套接字lfd,父进程关闭用于通信的cfd)

**功能:**客户端输入小写字符串,服务器转成大写返回给客户端

2.代码实现

warp.h

c 复制代码
#ifndef __WRAP_H_
#define __WRAP_H_
#include<sys/epoll.h>
//#include<event2/event.h>
#include<sys/select.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<string.h>
#include<dirent.h>
#include<sys/stat.h>
#include<wait.h>
#include<sys/mman.h>
#include<signal.h>
#include<pthread.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<strings.h>
#include<netinet/ip.h>
#define SRV_PORT 1234


void perr_exit(const char *s);
int Accept(int fd,struct sockaddr *sa,socklen_t * salenptr);
int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
int Connect(int fd, const struct sockaddr *sa, socklen_t addrlen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
size_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd,const void *ptr,size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
ssize_t my_read(int fd, char  *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);



#endif

warp.c

c 复制代码
#include"warp.h"


void perr_exit(const char *s)
{
	perror(s);
	exit(1);
}

int Accept(int fd,struct sockaddr *sa,socklen_t * salenptr)
{
	int n;
again:
	if((n=accept(fd,sa,salenptr))<0)
	{
		if((errno==ECONNABORTED)||(errno==EINTR))
			goto again;
		else
			perr_exit("accept error");
	}
	return n;
}

int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
	int n;
	if((n=bind(fd,sa,salen))<0)
		perr_exit("bind error");

	return n;
}

int Connect(int fd, const struct sockaddr *sa, socklen_t addrlen)
{
	int n;
	n=connect(fd,sa,addrlen);
	if(n<0)
	{
		perr_exit("connect error");
	}
	return n;
}

int Listen(int fd, int backlog)
{
	int n;
	if((n=listen(fd,backlog))<0)
		perr_exit("listen error");
	return n;
}

int Socket(int family, int type, int protocol)
{
	int n;
	if((n=socket(family,type,protocol))<0)
		perr_exit("socket error");
	return n;
}

size_t Read(int fd, void *ptr, size_t nbytes)
{
	ssize_t n;
again:
	if((n=read(fd,ptr,nbytes))==-1)
	{
		if(errno==EINTR)
			goto again;
		else
			return -1;
	}
	return n;
}

ssize_t Write(int fd,const void *ptr,size_t nbytes)
{
	ssize_t n;
again:
	if((n=write(fd,ptr,nbytes))==-1)
	{
		if(errno==EINTR)
			goto again;
		else
			return -1;
	}

	return 0;
}

int Close(int fd)
{
	int n;
	if((n=close(fd))==-1)
		perr_exit("close error");

	return n;
}

ssize_t Readn(int fd, void *vptr, size_t n)
{
	size_t nleft;
	ssize_t nread;
	char *ptr;

	ptr=vptr;
	nleft=n;

	while(nleft>0)
	{
		if((nread=read(fd,ptr,nleft))<0)
		{
			if(errno==EINTR)
				nread=0;
			else
				return -1;
		}
		else if(nread==0)
			break;
	
	}
}
ssize_t Writen(int fd, const void *vptr, size_t n)
{
	size_t nleft;
	ssize_t nwritten;
	char *ptr;

	ptr=(char *)vptr;
	nleft=n;

	while(nleft>0)
	{
		if((nwritten=write(fd,ptr,nleft))<=0)
		{
			if(nwritten<0&&errno==EINTR)
				nwritten=0;
			else
				return -1;
		}
		nleft-=nwritten;
		ptr+=nwritten;
	}
	return n;
}

ssize_t my_read(int fd, char  *ptr)
{
	static int read_cnt;
	static char *read_ptr;
	static char read_buf[100];

	if(read_cnt<=0)
	{
again:
		if((read_cnt=read(fd,read_buf,sizeof(read_buf)))<0)
		{
			if(errno==EINTR)
				goto again;
			return -1;
		}else if(read_cnt==0)
			return 0;
		read_ptr=read_buf;
	}
	read_cnt--;
	*ptr=*read_ptr++;
	return 1;
}

ssize_t Readline(int fd, void *vptr, size_t maxlen)
{
	ssize_t n,rc;
	char c,*ptr;
	ptr=vptr;

	for(n=1;n<maxlen;n++)
	{
		if((rc=my_read(fd,&c))==1)
		{
			*ptr++=c;
			if(c=='\n')
				break;
		}else if(rc==0)
		{
			*ptr=0;
			return n-1;
		}
		else	
			return -1;
	}
	*ptr=0;
	return n;
}

multi_thread_concurrency_sever.c

c 复制代码
#include"warp.h" 

#define MAXLINE	8192
#define SERV_PORT 1234

struct s_info{
	struct sockaddr_in cliaddr;
	int connfd;
};

//子线程回调函数
void *do_work(void *arg)
{
	int n,i;
	struct s_info *ts=(struct s_info*)arg;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN];

	while(1)
	{
		n=Read(ts->connfd,buf,MAXLINE);
		if(n==0)
		{
			printf("the client %d closed..\n",ts->connfd);
			break;
		}
		printf("received from %s at port %d\n",
				inet_ntop(AF_INET,&(*ts).cliaddr.sin_addr,str,sizeof(str)),
				ntohs((*ts).cliaddr.sin_port));
		for(i=0;i<n;i++)
			buf[i]=toupper(buf[i]);
		Write(STDOUT_FILENO,buf,n);
		Write(ts->connfd,buf,n);
	}
	Close(ts->connfd);
	return (void *)0;
}

int main(int argc,char * argv[])
{	
	struct sockaddr_in servaddr,cliaddr;
	socklen_t cliaddr_len;
	int listenfd,connfd;
	pthread_t tid;
	
	struct s_info ts[256];
	int i=0;
	
	listenfd=Socket(AF_INET,SOCK_STREAM,0);
	
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(SRV_PORT);
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

	Bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));

	Listen(listenfd,128);

	printf("Accepting client connect ..\n");

	while(1){
		cliaddr_len=sizeof(cliaddr);
		connfd=Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);
		ts[i].cliaddr=cliaddr;
		ts[i].connfd=connfd;
		
		pthread_create(&tid,NULL,do_work,(void *)&ts[i]);
		pthread_detach(tid);//子线程分离,防止僵尸线程产生
		i++;			
	}
	return 0;
}
gcc 复制代码
gcc warp.c multi_thread_concurrency_sever.c -o multi_process_thread_sever -lpthread

运行图

两个客户端访问服务器端

3.代码解释

1.指定的固定端口号为1234

也可以用argv接受ip和port参数

2.srv_addr.sin_addr.s_addr=htonl(INADDR_ANY);

  • sin_addrstruct sockaddr_in结构体中的一个嵌套结构体,其成员s_addr用于存放 IP 地址信息(以 32 位整数形式表示)。htonl函数和htons类似,不过它是将主机字节序的 32 位整数(通常用于 IP 地址)转换为网络字节序。INADDR_ANY是一个特殊的常量,它表示服务器端套接字可以绑定到本机的任意可用 IP 地址上,通过这行代码,将转换为网络字节序后的INADDR_ANY值赋给了srv_addr结构体的sin_addr.s_addr成员,使得服务器能够监听来自本机所有网络接口上对应端口的连接请求。

3.需要用到的调用都需要错误处理,封装到warp.c后进行使用

4.子线程做的事情:(和子进程做的事情是一样的)

子线程的回调函数

void *tfn(void *arg){

close(lfd)关闭监听套接字

read()

逻辑处理:小写转大写

write()

}

5.主线程做的事情:创建套接字和连接

6.也可以创建一个回收子线程的线程(只回收线程,不干别的)

pthread_join(tid,void **)

也可以用detach,这样就不用管子线程回收的问题了

7.线程分离(pthread_detach)的基本概念

  • 当使用pthread_detach函数对一个线程进行分离操作后,该线程就变成了一个 "分离线程"。在这种状态下,线程结束时其资源会由系统自动回收,而不需要其他线程(如主线程)显式地调用pthread_join函数来回收资源。
相关推荐
Cici_ovo1 分钟前
wlan和vlan
网络·智能路由器
我科绝伦(Huanhuan Zhou)5 分钟前
Linux 系统服务开机自启动指导手册
java·linux·服务器
hunter2062062 小时前
ubuntu终端当一段时间内没有程序运行时,自动关闭终端。
linux·chrome·ubuntu
代码讲故事3 小时前
从Windows通过XRDP远程访问和控制银河麒麟ukey v10服务器,以及多次连接后黑屏的问题
linux·运维·服务器·windows·远程连接·远程桌面·xrdp
hardWork_yulu5 小时前
Android RTMP直播练习实践
网络·安卓
qq_243050795 小时前
irpas:互联网路由协议攻击套件!全参数详细教程!Kali Linux入门教程!黑客渗透测试!
linux·网络·web安全·网络安全·黑客·渗透测试·系统安全
IT北辰6 小时前
Linux下 date时间应该与系统的 RTC(硬件时钟)同步
linux·运维·实时音视频
Jason Yan6 小时前
【经验分享】ARM Linux-RT内核实时系统性能评估工具
linux·arm开发·经验分享
步、步、为营7 小时前
.net无运行时发布原理
linux·服务器·.net
大丈夫立于天地间7 小时前
机遇、挑战与融合创新之路
网络