线程版服务器实现(pthread_server)

news2024/11/16 19:47:19

用到的所有方法所需要的参数可以在wrap.c文件中查询,wrap中找不到的直接通过man手册查询

1.首先介绍一下我自己写的包裹文件,里面有各种在可能要用到的方法

wrap.c:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.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 salen)
{
    int n;

	if ((n = connect(fd, sa, salen)) < 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;
}

ssize_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 n;
}

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;              //usigned int 剩余未读取的字节数
	ssize_t nread;              //int 实际读到的字节数
	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;

		nleft -= nread;
		ptr += nread;
	}
	return n - nleft;
}
/*:固定的字节数数据*/
ssize_t Writen(int fd, const void *vptr, size_t n)
{
	size_t nleft;
	ssize_t nwritten;
	const char *ptr;

	ptr = 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;
}

static 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;
}

int tcp4bind(short port,const char *IP)
{
    struct sockaddr_in serv_addr;
    int lfd = Socket(AF_INET,SOCK_STREAM,0);
    bzero(&serv_addr,sizeof(serv_addr));
    if(IP == NULL){
        //如果这样使用 0.0.0.0,任意ip将可以连接
        serv_addr.sin_addr.s_addr = INADDR_ANY;
    }else{
        if(inet_pton(AF_INET,IP,&serv_addr.sin_addr.s_addr) <= 0){
            perror(IP);//转换失败
            exit(1);
        }
    }
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port   = htons(port);
   // int opt = 1;
	//setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    Bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
    return lfd;
}

wrap.h:

#ifndef __WRAP_H_
#define __WRAP_H_
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>

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 salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_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);
int tcp4bind(short port,const char *IP);
#endif

2.将这两个文件放到线程服务器源文件的同一目录中,后面需要用到

3.第一步:创建套接字并且绑定

int main(int argc,char *argv[])

由上可见,argc是用户输入终端的参数个数,argv[]表示用户输入终端的信息,argv[0]是./a.out,argv[1]是端口号,如果argc小于2,表示没有传入端口号,退出即可,如下所示:

if(argc < 2)
{
	printf("argc < 2???   \n ./a.out 8000 \n");
	return 0;
}

将传入的字符型端口转为short型,如下所示:

short port=atoi(argv[1]);

然后调用wrap.c中的创建套接字并绑定方法“tcp4bind”,参数NULL表示绑定本地所有的ip地址,调用完后返回一个监听套接字,如下所示:

int lfd = tcp4bind(port,NULL);//创建套接字 绑定

4.第二步:开始监听,一样用wrap的方法,如下所示:

Listen(lfd,128);

5.第三步:开始提取

建立一个结构体保存提取的客户端信息,如下所示:

struct sockaddr_in cliaddr;

调用wrap中的提取方法,将提取到的信息放到上面的结构体(只要看到ipv4的结构体,都要转成通配地址,用(struct sockaddr *)强转)中,len是cliaddr的大小,如下所示:

socklen_t len = sizeof(cliaddr);
int cfd = Accept(lfd,(struct sockaddr *)&cliaddr,&len);

6.主线程负责监听,子线程负责服务客户端

所以,提取成功后要创建一个子线程,如下所示:

pthread_t pthid;
pthread_create(&pthid,&attr,client_fun,info);

info是一个结构体,表示要传两个参数给子线程,结构体定义和初始化如下:

typedef struct c_info
{
	int cfd;
	struct sockaddr_in cliaddr;
}CINFO;

CINFO *info;

info = malloc(sizeof(CINFO));

info->cfd = cfd;
info->cliaddr= cliaddr;

client_fun是线程回调函数,表示子线程针对客户端要干的事情,如下所示:

void* client_fun(void *arg)
{
	CINFO *info = (CINFO *)arg;
	char ip[16]="";
	printf("new client ip=%s port=%d\n",inet_ntop(AF_INET,&(info->cliaddr.sin_addr.s_addr),ip,16),ntohs(info->cliaddr.sin_port));
	while(1)
	{
		char buf[1024]="";
		int count=0;
		count = read(info->cfd,buf,sizeof(buf));
		if(count < 0)
		{
			perror("");
			break;	
		}
		else if(count == 0)
		{
			printf("client close\n");
			break;
		}
		else
		{
			printf("%s\n", buf);
			write(info->cfd,buf,count);	
		}
	}
	close(info->cfd);
	free(info);
}

7.子线程工作完成后,如何回收?

设置一下线程分离属性就可以了,如下所示:

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);

在这里使用(&attr),如下所示:

pthread_create(&pthid,&attr,client_fun,info);

这样的话,子线程退出,系统会自动回收子线程!

8.“1_pthread_server.c”的完整代码如下所示:

#include <stdio.h>
#include <pthread.h>
#include "wrap.h"

typedef struct c_info
{
	int cfd;
	struct sockaddr_in cliaddr;
}CINFO;

void* client_fun(void *arg);

int main(int argc, char *argv[])
{
	if(argc < 2)
	{
		printf("argc < 2???   \n");
		return 0;
	}
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	short port = atoi(argv[1]);
	int lfd = tcp4bind(port,NULL);//创建套接字 绑定 
	Listen(lfd,128);
	struct sockaddr_in cliaddr;
	socklen_t len = sizeof(cliaddr);
	CINFO *info;
	while(1)
	{
		int cfd = Accept(lfd,(struct sockaddr *)&cliaddr,&len);
		char ip[16]="";
		pthread_t pthid;
		info = malloc(sizeof(CINFO));
		info->cfd = cfd;
		info->cliaddr= cliaddr;
		pthread_create(&pthid,&attr,client_fun,info);
	}
	return 0;
}

void* client_fun(void *arg)
{
	CINFO *info = (CINFO *)arg;
	char ip[16]="";
	printf("new client ip=%s port=%d\n",inet_ntop(AF_INET,&(info->cliaddr.sin_addr.s_addr),ip,16),ntohs(info->cliaddr.sin_port));
	while(1)
	{
		char buf[1024]="";
		int count=0;
		count = read(info->cfd,buf,sizeof(buf));
		if(count < 0)
		{
			perror("");
			break;	
		}
		else if(count == 0)
		{
			printf("client close\n");
			break;
		}
		else
		{
			printf("%s\n", buf);
			write(info->cfd,buf,count);	
		}
	}
	close(info->cfd);
	free(info);
}

退出vim编辑器,gcc编译一下:

gcc 1_pthread_server.c wrap.c -lpthread

开始运行,8008表示你给线程服务器设置的端口号:

./a.out 8008

另开一个终端,输入以下命令连接你的线程服务器:

nc xxx.xxx.xxx.xxx 8008

xxx.xxx.xxx.xxx表示你的线程服务器的ip地址

至此即可完成客户端与线程服务器的通信

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【IDEA】单项目多启动

IDEA 允许多次运行项目 一般来说一个项目仅允许启动一次&#xff0c;启动后它的启动按钮就会变成重启按钮&#xff0c;但是一些情况下我们可能需要启动多个客户端&#xff0c;可以用于模拟多个网络请求的发送等&#xff0c;多启动的开启方式如下&#xff1a; 2023.x 之后的版本…

常见网络攻击威胁分享

今天我来分享一下比较常见的网络攻击形式&#xff0c; ARP 欺骗攻击、CC 攻击和 DDoS 流量攻击是较为常见且危害巨大的攻击方式。 一、ARP欺骗攻击 ARP&#xff08;AddressResolutionProtocol&#xff0c;地址解析协议&#xff09;是用于将IP地址转换为MAC地址的协议。ARP欺骗…

web-原生Ajax

概念: Asynchronous JavaScript And XML&#xff0c;异步的JavaScript和XML。 作用: 数据交换:通过Ajax可以给服务器发送请求&#xff0c;并获取服务器响应的数据。 异步交互:可以在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新部分网页的技术&#xff0c;如…

秋招突击——6/20——复习{(单调队列优化)——最大子序列和,背包问题——宠物小精灵收服问题}——新作{两两交换链表中的节点}

文章目录 引言复习单调队列优化——最大子序列和思路分析实现代码参考实现 背包问题——宠物小精灵的收服问题个人实现参考实现 新作两两交换链表中的节点个人实现参考实现 删除有序数组中的重复项个人实现知识补全迭代器的访问和控制vector删除特定的元素erasevector底层删除元…

私有化地图离线部署方案之查询定位服务

私有化地图离线部署整体解决方案&#xff0c;除硬件之外&#xff0c;一般主要由基础地图服务、查询定位服务、路径规划服务和高程检索服务构成。 其中&#xff0c;查询定位服务是指地理编码与逆地理编码服务。 在《私有化地图离线部署方案之基础地图服务》一文中&#xff0c;…

Python 挖坑式填充Excel模板内容(包括页眉/SheetName/logo)

纵览 Python处理Excel的方式--解压缩方式1、导包2、对模板文件进行解压缩3、对解压缩后文件层级进行介绍4、准备需要载入的数据5、模板挖坑6、运行替换代码7、压缩文件8、生成文件9、完成代码10、可能遇到的问题 结语 Python处理Excel的方式–解压缩方式 在处理Excel中过程中&…

Origin做聚类分析并利用聚类插件绘制热力图

1.聚类分析 1.1 K均值聚类 step1、首先进行归一化&#xff0c;具体步骤如图1-1所示&#xff1a; 图1-1 操作后得到归一化值如图1-2所示&#xff1a; 图1-2 step2、执行K均值聚类分析&#xff0c;如图1-3所示&#xff0c;选中聚类列&#xff0c;接着点击“统计”—“多变量分析…

Linux—LVM与磁盘配额

目录 一、LVM 1、LVM概念 2、LVM逻辑卷核心组件 3、LVM管理命令 二、LVM操作主要命令步骤 1、添加硬盘 2、新建分区&#xff0c;并修改分区类型 3、新建物理卷&#xff08;PV&#xff09; 4、新建卷组&#xff08;VG&#xff09; 5、新建逻辑卷&#xff08;LV&#xff0…

pgsql的套接字文件不存在

问题&#xff1a;psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory 解决方式&#xff1a; 检查 postgresql.conf 文件中的 unix_socket_directories 设置&#xff0c;确保它包含 /tmp 或者你期望的目录。 重…

网站安装HTTPS证书的重要性以及如何申请

在互联网时代&#xff0c;数据安全成为了企业和个人关注的焦点。HTTPS证书作为网站安全的重要保障&#xff0c;其重要性不言而喻。下面将探讨HTTPS证书的重要性&#xff0c;并介绍如何申请HTTPS证书&#xff0c;构建一个更安全、更信任的网络环境。 一、HTTPS证书的重要性 1.…

改网络ip地址有什么用

在数字化时代&#xff0c;网络IP地址是每个网络设备和终端在互联网上的唯一标识符。然而&#xff0c;有时出于安全、隐私或网络管理的需要&#xff0c;我们可能需要更改网络IP地址。例如很多小伙伴会选择使用虎观代理IP更改电脑或手机设备上的网络IP地址&#xff0c;那么&#…

中霖教育怎么样?中霖教育好吗?

中霖教育怎么样?中霖教育好吗? 中霖教育包括师资力量、课程设置、教学方法等都是经过不断完善来制定的&#xff0c;我们拥有专业且经验丰富的师资队伍&#xff0c;在教学过程中更注重个性化教学方式&#xff0c;针对每个学员的需求和学习情况制定专属的学习计划。 无论是在…

【源码】最新源支付系统源码 V7版全开源 免授权 附搭建教程

最新源支付系统源码_V7版全开源_免授权_附详细搭建教程_站长亲测 YPay是专为个人站长打造的聚合免签系统&#xff0c;拥有卓越的性能和丰富的功能。它采用全新轻量化的界面UI&#xff0c;让您能更方便快捷地解决知识付费和运营赞助的难题。同时&#xff0c;它基于高性能的thin…

数据库三大范式是什么?你是按什么原则去建数据库表的?

引言&#xff1a;数据库设计是任何信息系统中至关重要的一环&#xff0c;它直接影响着数据管理的效率、系统的性能以及信息的完整性和安全性。在当今数字化和信息化程度不断提升的背景下&#xff0c;正确和高效的数据库设计更显得至关重要。本文旨在探讨数据库设计中的核心理论…

【编译原理】绪论

1.计算机程序语言以及编译 编译是对高级语言的翻译 源程序是句子的集合&#xff0c;树可以较好的反应句子的结构 编译程序是一种翻译程序 2.编号器在语言处理系统中的位置 可重定位&#xff1a;在内存中存放的起始位置不是固定的 加载器&#xff1a;修改可重定位地址&#x…

养殖自动化管理系统:开启智慧养殖新篇章

在现代农业的快速演进中&#xff0c;养殖业正经历一场前所未有的技术革命。养殖自动化管理系统&#xff0c;作为这场变革的前沿科技&#xff0c;正逐步成为推动行业高效、环保、可持续发展的关键力量。本文将深入探讨自动化养殖系统如何通过精准管理、智能监控、数据驱动决策&a…

云原生架构:未来应用程序设计和部署的革新

目录 前言1. 云原生架构的概述1.1 什么是云原生架构1.2 云原生架构的核心理念 2. 云原生架构的核心特征2.1 容器化应用2.2 微服务架构2.3 自动化管理 3. 云原生架构的优势3.1 弹性和可伸缩性3.2 高可用性和容错性3.3 快速交付和持续部署 4. 实施云原生架构的关键技术4.1 容器编…

51单片机最火型号大比拼:性能、应用与选型指南

51单片机作为经典的微控制器架构&#xff0c;凭借其易于学习、价格低廉、应用广泛等优势&#xff0c;一直活跃在嵌入式开发领域。面对市场上琳琅满目的51单片机型号&#xff0c;初学者和开发者常常感到眼花缭乱。本文将对几款最火的51单片机型号进行深度剖析&#xff0c;从性能…

Flink——最流批的大数据框架(流批一体)

Apache Flink基础教程 资料来源&#xff1a;Apache Flink Tutorial (tutorialspoint.com) Apache Flink是Apache Hadoop的开源本地分析数据库。它由Cloudera、MapR、Oracle和Amazon等供应商提供。本教程中提供的示例是使用Cloudera Apache Flink开发的。 本教程是为那些想要学…

基于Jmeter的分布式压测环境搭建及简单压测实践

写在前面 平时在使用Jmeter做压力测试的过程中&#xff0c;由于单机的并发能力有限&#xff0c;所以常常无法满足压力测试的需求。因此&#xff0c;Jmeter还提供了分布式的解决方案。本文是一次利用Jmeter分布式对业务系统登录接口做的压力测试的实践记录。按照惯例&#xff0…