线程版服务器实现(pthread_server)

用到的所有方法所需要的参数可以在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地址

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

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

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

相关文章

软考《信息系统运行管理员》-1.4 常见的信息系统

1.4 常见的信息系统 常见的信息系统综述 财务系统 财务信息系统会计信息系统 办公自动化系统业务处理系统生产管理系统ERP系统客户关系管理系统人力资源系统 会计信息系统 主要任务是保证记账的正确性。 订单处理子系统库存子系统会计应收/应支系统总账子系统 财务信息系…

effective java (1)(考虑使用!)静态工厂方法代替构造方法

只是目前阶段 对本书第一章内容的浅显认知&#xff0c;说实话 这一章 我看了4遍左右&#xff0c;每一遍感觉都不一样 他的创建模式 有时候像设计模式&#xff0c;但作者已经在原文中描述&#xff0c;它并不等价于 设计模式 我们正常 创建一个年级类 是长这样的 我们不写成标准…

机械拆装-基于Unity-总体设计

前言 在工业设计和制造领域&#xff0c;零部件的拆装技术是一个重要的应用场景&#xff0c;比如我们在工程训练课程中经历的摩托车发动机拆装课程&#xff0c;是机械类学生的必修课程。虚拟拆装系统模拟和仿真了模型的拆装过程&#xff0c;虽然SolidWorks等机械设计软件能够解决…

Splashtop 的屏幕录制功能如何提高 IT 合规性

在当今的数字时代&#xff0c;随着远程办公的普及以及监管要求和网络安全威胁的加剧&#xff0c;IT 副总裁、首席信息官&#xff08;CIO&#xff09;等 IT 管理人员面临着一系列独特挑战。 各组织在远程支持运营中要全力维护合规性、提高安全性并坚持问责制&#xff0c;技术解…

瓦罗兰特新赛季更新资讯 瓦罗兰特新赛季免费加速器

瓦罗兰特新赛季来喽&#xff0c;这是一款由拳头开发的免费第一人称射击游戏&#xff0c;游戏凭借其独特的玩法和丰富的英雄选择吸引了大量玩家。 我们可以在游戏中选择自己喜欢的角色出场与敌人进行对战&#xff0c;而且每一个角色都有自己独特的道具以及技能&#xff0c;使用好…

实体零售连锁企业如何通过物流接口实现数智化转型升级?

在电子商务浪潮的持续冲击下&#xff0c;传统的实体零售行业面临着巨大的挑战。为了在线上线下融合的新零售时代保持竞争力&#xff0c;众多实体零售企业积极寻求数字化转型的突破。 某中国零售连锁百强企业近年来致力于打造自有品牌的线上销售体系&#xff0c;自2021年8月起接…

高效管理客户的秘诀:企业如何建立稳固的客户关系

如今的竞争&#xff0c;从商业模式、产品、服务到销售环节&#xff0c;竞争已经不再是单一层面的&#xff0c;而是全方位的&#xff0c;企业需要打造全价值链竞争优势。在这个过程中&#xff0c;客户管理的作用是无可替代的&#xff0c;成为企业成功的关键因素之一。如何高效地…

Excel表格转换Word文档的3个简单方法分享!

在日常办公中&#xff0c;我们经常需要将Excel表格中的数据转换为Word文档以便于编辑、排版或分享。然而&#xff0c;很多人可能并不清楚如何实现这一转换过程&#xff0c;或者只能采取复制粘贴的笨拙方式&#xff0c;导致格式错乱、效率低下。本文将详细介绍两种高效、便捷的E…

企业应该如果安全上网,软件防查盗版,企业防盗版

随着信息化的发展&#xff0c;企业日常办公越来越依赖互联网。终端以及普通PC终端在访问互联网过程中&#xff0c;会遇到各种各样不容忽视的风险&#xff0c;例如员工主动故意的数据泄漏&#xff0c;后台应用程序偷偷向外部发信息&#xff0c;木马间谍软件的外联&#xff0c;以…

精密机器中的交叉导轨负荷与容许负荷的差异!

交叉导轨的设计和制造过程中&#xff0c;负荷及容许负荷是至关重要的参数&#xff0c;只有准确计算出交叉导轨的载荷&#xff0c;才能保证交叉导轨的稳定性和使用寿命。 负荷和容许载荷是两个不同的参数&#xff0c;那这两者的有什么差异呢&#xff1f; 交叉导轨的负荷是指其承…

【Linux 命令行参数解析函数getopt()】原理及直白理解

最近写代码恰好碰见getopt()这个函数&#xff0c;去网上找了很久&#xff0c;说实话&#xff0c;其他人写的有点看不懂&#xff0c;所以将我认为可以便于理解的地方描述一下&#xff1a; int getopt(int argc, char * const argv[], const char *optstring);首先理解这个函数的…

军用光电耦合器产品的市场潜力与应用前景

光电耦合器作为现代军事技术中的关键组件&#xff0c;其在军用领域的市场空间和应用前景备受关注。本文将深入分析光电耦合器产品在军事领域中的市场潜力&#xff0c;探讨其技术特点、应用场景及未来发展趋势。 光电耦合器技术特点与工作原理 光电耦合器是一种能够将电信号与光…

HDC Cloud 2024 | CodeArts加速软件智能化开发,携手HarmonyOS重塑企业应用创新体验

2024年6月21~23日&#xff0c;华为开发者大会HDC 2024在东莞溪流背坡村隆重举行。期间华为云主办了以“CodeArts加速软件智能化开发&#xff0c;携手HarmonyOS重塑企业应用创新体验”为主题的分论坛。论坛汇聚了各行各业的专家学者、技术领袖和开发者&#xff0c;共同探讨Harmo…

解决Transformer根本缺陷,所有大模型都能获得巨大改进

即使最强大的 LLM 也难以通过 token 索引来关注句子等概念&#xff0c;现在有办法了。 最近两天&#xff0c;马斯克和 LeCun 的口水战妥妥成为大家的看点。这两位 AI 圈的名人你来我往&#xff0c;在推特&#xff08;现为 X&#xff09;上相互拆对方台。 LeCun 在宣传自家最新论…

VERYCLOUD睿鸿股份确认参展2024年ChinaJoy BTOB商务洽谈馆,期待与你相聚

作为在全球数字娱乐领域兼具知名度与影响力的年度盛会&#xff0c;2024年第二十一届ChinaJoy将于7月26日至7月29日在上海新国际博览中心盛大召开&#xff0c;本届展会主题为&#xff1a;初心“游”在&#xff0c;精彩无限&#xff01;&#xff08;Stay True, Game On.&#xff…

阿一网络安全培训中心专门为你准备了一份WScan使用教程

下载地址&#xff1a;https://github.com/chushuai/wscan/releases 版本的选择 Windows就选windows_amd64 Linux就选linux_amd64 mac就选darwin_amd64 下载好后&#xff0c;运行一次exe会生成一个config.yaml文件 把该文件中plugins下面的所有插件的enabled设置为True。&…

Java养老护理助浴陪诊小程序APP源码

&#x1f496;护理助浴陪诊小程序&#x1f496; 一、引言&#xff1a;养老新趋势&#x1f331; 在快节奏的现代生活中&#xff0c;养老问题逐渐成为了社会关注的焦点。如何为老年人提供便捷、贴心的服务&#xff0c;让他们晚年生活更加安心、舒适&#xff0c;是我们每个人都需…

CentOS7环境下DataX的安装、使用及问题解决

DataX概述 DataX 是阿里巴巴开源的一个异构数据源离线同步工具&#xff0c;致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定高效的数据同步功能。 为了解决异构数据源同步问题&#xff0c;DataX将复杂的网状的同步链路变…

六、常用API ★ ✔

六、常用API 模块14.基础API第一章.String1.String介绍2.String的实现原理 ★3.String的创建 &#xff08;无参、字符串、字符数组、字节数组&#xff09;4.String 面试题【String s new String("abc")】5.字符串常见问题 第二章.String的方法1.判断方法&#xff08…

Typescript 【实用教程】(2024最新版)含类型声明,类型断言,函数,接口,泛型等

简介 TypeScript 是 JavaScript 的超集&#xff0c;是 JavaScript&#xff08;弱类型语言&#xff09; 的强类型版本。 拥有类型机制文件后缀 .tsTypescript type ES6TypeScript 和 JavaScript 的关系类似 less 和 css 的关系TypeScript对 JavaScript 添加了一些扩展&#x…