LinuxC多线程pthread并发服务器(考虑线程锁,线程复用,端口复用)

最近从windows转到linux学习了一段时间了。其实tcp服务器本质基本都一样。

这次上传一个简单的linux下多线程并发服务器。该服务器可以接收128个客户端同时连接,并且加入了互斥锁,排除了多个客户端同时间连接时出现的问题。并且当前一个客户端关闭连接时,后面连接的客户端可以继续使用该线程。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define maxthread 256
typedef struct SockInfo
{
	int sockfd;
	struct sockaddr_in addr;
	pthread_t id;
}SockInfo; //定义结构体,来存储多客户端的线程编号

pthread_mutex_t mutex;	// 定义互斥锁,全局变量
/************************************************************************
函数名称:	void *client_process(void *arg)
函数功能:	线程函数,处理客户信息
函数参数:	已连接套接字
函数返回:	无
************************************************************************/
void *client_process(void *arg)
{
int recv_len = 0;
char recv_buf[1024] = "";	// 接收缓冲区
int connfd = *(int *)arg; // 传过来的已连接套接字
// 解锁,pthread_mutex_lock()唤醒,不阻塞
pthread_mutex_unlock(&mutex);
// 接收数据
while((recv_len = recv(connfd, recv_buf, sizeof(recv_buf), 0)) > 0)
{
//printf("recv_buf: %s\n", recv_buf); // 打印数据
printf(" 来自fd描述符:%d,内容为: %s\n",connfd, recv_buf); // 打印数据
send(connfd, recv_buf, recv_len, 0); // 给客户端回数据
memset(&recv_buf, 0, recv_len);
}
printf("fd:%d 关闭!\n",connfd);
close(connfd);	//关闭已连接套接字
return NULL;
}
//===============================================================
// 语法格式:	void main(void)
// 实现功能:	主函数,建立一个TCP并发服务器
// 入口参数:	无
// 出口参数:	无
//===============================================================
int main(int argc, char *argv[])
{
int sockfd = 0;	// 套接字
int connfd = 0;
int err_log = 0;
struct sockaddr_in my_addr;	// 服务器地址结构体
unsigned short port =8888 ; // 监听端口
pthread_t thread_id;
pthread_mutex_init(&mutex, NULL); // 初始化互斥锁,互斥锁默认是打开的
printf("TCP Server Started at port %d!\n", port);
sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
if(sockfd < 0)
{
perror("socket error");
exit(-1);
}
bzero(&my_addr, sizeof(my_addr));	// 初始化服务器地址
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
printf("Binding server to port %d\n", port);

int flag=1;//设置端口复用,这样如果是服务器主动断开连接后可以马上使用,略过time_wait阶段的等待
setsockopt(sockfd,SOL_SOCKET,SO_REUSEPORT,&flag,sizeof(flag));

// 绑定
err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
if(err_log != 0)
{
perror("bind");
close(sockfd);
exit(-1);
}
// 监听,套接字变被动
err_log = listen(sockfd,4);
if( err_log != 0)
{
perror("listen");
close(sockfd);
exit(-1);
}
printf("Waiting client...\n");
int i=0;//线程的标志
SockInfo info[maxthread];
for(int i=0;i<maxthread;++i)
{
 info[i].sockfd=-1;
}
socklen_t cli_len=sizeof(struct sockaddr_in);

while(1)
{
char cli_ip[INET_ADDRSTRLEN] = "";	// 用于保存客户端IP地址
for(i=0;i<maxthread;++i)
{
if(info[i].sockfd==-1)//遍历线程,找到最小的空闲描述符,i为其结构体标志
{
break;
}
}
if(i==maxthread)break;//如果线程已经满了,不接受连接退出循环
// 上锁,在没有解锁之前,pthread_mutex_lock()会阻塞
pthread_mutex_lock(&mutex);
//获得一个已经建立的连接
info[i].sockfd = accept(sockfd, (struct sockaddr*)&info[i].addr, &cli_len);
// 打印客户端的 ip 和端口
inet_ntop(AF_INET, &info[i].addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
printf("--------client ip=%s,port=%d\n", cli_ip,ntohs(info[i].addr.sin_port));
if(info[i].sockfd > 0)
{
//给回调函数传的参数,&connfd,地址传递
pthread_create(&info[i].id, NULL, client_process, (void *)&info[i]); //创建线程
pthread_detach(info[i].id); // 线程分离,结束时自动回收资源
}
}
close(sockfd);
return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值