高并发服务器编程之多线程并发服务器

同类文章:
基于Linux的SOCKET编程之TCP半双工Client-Server聊天程序
基于Linux的Socket编程之TCP全双工Server-Client聊天程序
高并发服务器编程之多进程并发服务器

一、多线程服务器分析:

多进程并发与多线程并发实现过程差不多,只是多线程的同步、资源回收与多进程还是有很多区别的。多进程不需要记录子进程的信息,而多线程需要记录。
或许需要将子线程设置为分离态(pthread_detach()的效率,不如在创建线程之前就设置好attr属性高)。当然我们也可以在主线程中创建一个线程专门用来回收用于通信的线程,这点和多进程基本相同,而我们采用创建线程之前就设置好attr属性为分离状态的方法回收结束的子线程。

在多进程中说过C/S心跳机制,多线程也可以使用,但是需要注意:本地127.0.0.1测试时,不会经过网卡,在经过过滤器时便直接接到目的端口。所以说单机无法测试C/S心跳机制。

二、多线程服务器测试代码:

multithread.h:

/*multithread.h*/
#ifndef _MULTI_THREAD_H_
#define _MULTI_THREAD_H_

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define BUF_SIZE 1024

#endif

1、server服务器端:

(1)、server.c:

/*server.c*/
#include<multithread.h>
#include<server.h>

void sys_err(const char * ptr_err)
{
    perror(ptr_err);
    exit(EXIT_FAILURE);
}

void socket_server_create(const char * ipaddr, const char * port)
{
    struct sockaddr_in seraddr;
    int listenfd, ret;

    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        sys_err("socket create");

    seraddr.sin_family = AF_INET;
    seraddr.sin_addr.s_addr = inet_addr(ipaddr);
    seraddr.sin_port = htons(atoi(port));

    if( (ret = bind(listenfd, (struct sockaddr*)&seraddr, sizeof(seraddr))) < 0)
        sys_err("bind server address");

    if((ret = listen(listenfd, BACKLOG_THREAD)) < 0)
        sys_err("listen");

    accept_conn(listenfd);/*处理客户端链接的接收工作*/
    close(listenfd);
}

void accept_conn(const int listenfd)
{
    int connfd, i = 0;
    pthread_t tid;
    socklen_t addrlen;
    pthread_attr_t attr;//线程状态信息
    struct thread_info thread[MAX_THREAD_NUM];//存储线程信息,用于函数指针传参
    struct sockaddr_in cliaddr;//客户端信息

    pthread_attr_init(&attr);/*初始化线程属性*/
    /*设置为分离状态,也可以在线程处理函数中,用pthread_detach()进行分离状态设置,但是效率稍差*/
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    while(1){
        addrlen = sizeof(cliaddr);
again:  
        if((connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &addrlen)) < 0){
            if(errno == EINTR || errno == ECONNABORTED) 
                goto again;
            else
                sys_err("accept");
        }
        /*保存连接到的客户端信息到结构体中以便传参*/
        thread[i].connfd = connfd;
        thread[i].cliaddr = cliaddr;
        /*创建线程并且传递接收到的cilent信息到线程处理函数*/
        pthread_create(&tid, &attr, deal_conn, (void *)&thread[i]);
        pthread_attr_destroy(&attr);//线程资源使用完后释放资源

        i++;
        if(i == MAX_THREAD_NUM){//超过线程总数限制,服务器主线程结束
            printf("Too many connect!\n");
            pthread_exit((void *)(-1));
        }
    }
}
void * deal_conn(void *arg)
{
    struct thread_info * conn = (struct thread_info *)(arg);//无类型指针转换为自定义客户端信息类型指针
    int ret;
    char recvbuf[BUF_SIZE] = {};//接受缓冲区
    char sendbuf[BUF_SIZE] = {};//回射缓冲区
    char ipaddr[IPADDR_SIZE] = {};
    int port = ntohs((conn->cliaddr).sin_port);//客户端端口
    strcpy(ipaddr, inet_ntoa((conn->cliaddr).sin_addr));//客户端IP地址

    int fd = open("sersock.log", O_RDWR);
    lseek(fd, 0, SEEK_END);
    sprintf(recvbuf, "%s:%d\n", ipaddr, port);
    if( (ret = write(fd, recvbuf, strlen(recvbuf))) <0 )//将客户端登录状态写入日志文件
        sys_err("write sersock.log");
    close(fd);

    printf("%s:%d is connect success!\n", ipaddr, port);
    while(1){
        bzero(recvbuf, strlen(recvbuf));
        bzero(sendbuf, strlen(sendbuf));

        if((ret = read(conn->connfd, recvbuf, sizeof(recvbuf))) == 0){/*读取客户端发送信息*/
            printf("The client %s:%d is over!\n", ipaddr, port);
            break;
        }
        sprintf(sendbuf, "Server recv messge:%s", recvbuf);//拼接一部分回射提示信息
        if( (ret = write(conn->connfd, sendbuf, strlen(sendbuf))) < 0)/*回射*/
            sys_err("write");
    }
    close(conn->connfd);
    pthread_exit(NULL);/*不需要返回值*/
}

(2)、server.h:

/*server.h*/
#ifndef _SERVER_H_
#define _SERVER_H_

#include<multithread.h>
#include<pthread.h>
#include<fcntl.h>
#include<errno.h>

#define BACKLOG_THREAD 32
#define MAX_THREAD_NUM 256
#define IPADDR_SIZE 15

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

void socket_server_create(const char *, const char *);
void accept_conn(const int);
void * deal_conn(void *);

#endif

(3)、server_main.c:

/*server_main.c*/
#include<multithread.h>
#include<server.h>

int main(int argc, char **argv)
{
    if(argc < 3 ){
        printf("Too few parameter!\n");
        exit(EXIT_FAILURE);
    }

    socket_server_create(argv[1], argv[2]);
    return 0;
}

(4)、Makefile文件:

CPPFLAGS= -I ../ -I ./
CFLAGS= -g -Wall
#指定线程函数链接库
LDFLAGS= -lpthread
CC=gcc

src = $(wildcard *.c)
obj = $(patsubst %.c,%.o,$(src))
target = server

$(target):$(obj)
    $(CC) $^ $(LDFLAGS) -o $@

%.o:%.c
    $(CC) -c $< $(CFLAGS) $(CPPFLAGS) -o $@

.PHONY:clean
clean:
    -rm -f server
    -rm -f *.o

2、client客户端:

(1)、client.c:

/*client.c*/
#include<multithread.h>
#include<client.h>

void sys_err(const char * ptr_err)
{
    perror(ptr_err);
    exit(EXIT_FAILURE);
}
void socket_client_create(const char * ipaddr, const char * port)
{
    struct sockaddr_in serveraddr;

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(ipaddr);
    serveraddr.sin_port = htons(atoi(port));

    int ret = connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    if(ret < 0)
        sys_err("connect");

    deal_connect(sockfd);

    close(sockfd);
}

void deal_connect(const int sockfd)
{
    char sendbuf[BUF_SIZE] = {};
    char recvbuf[BUF_SIZE] = {};
    int ret;
    while( fgets(sendbuf, sizeof(sendbuf), stdin) ){
        if((ret = write(sockfd, sendbuf, strlen(sendbuf))) < 0)/*发送信息*/
            sys_err("write sockfd");
        if((ret = read(sockfd, recvbuf, sizeof(recvbuf))) < 0)/*接收回射信息*/
            sys_err("read");
        if((ret = write(STDOUT_FILENO, recvbuf, ret)) < 0)/*写到stdout*/
            sys_err("write stdout");
        bzero(sendbuf, strlen(sendbuf));
        bzero(recvbuf, strlen(recvbuf));
    }
}

(2)、client.h:

/*client.h*/
#ifndef _CLIENT_H_
#define _CLIENT_H_

#include<multithread.h>

void socket_client_create(const char *, const char *);
void deal_connect(const int);

#endif

(3)、client_main.c:

/*client_main.c*/
#include<multithread.h>
#include<client.h>

int main(int argc, char ** argv)
{
    if(argc < 3){
        printf("Too few parameter!\n");
        exit(EXIT_FAILURE);
    }
    socket_client_create(argv[1], argv[2]);
    return 0;
}

(4)、Makefile文件:

CPPFLAGS= -I ../ -I ./
CFLAGS= -g -Wall
LDFLAGS= 
CC=gcc

src = $(wildcard *.c)
obj = $(patsubst %.c,%.o,$(src))
target = client

$(target):$(obj)
    $(CC) $^ $(LDFLAGS) -o $@

%.o:%.c
    $(CC) -c $< $(CFLAGS) $(CPPFLAGS) -o $@

.PHONY:clean
clean:
    -rm -f client
    -rm -f *.o

缺点:未处理线程的同步问题(对日志文件的加锁)。

三、测试结果截图分析:

1、测试客户端登录与新信息接收功能,以及查看日志文件的记录信息:
这里写图片描述

2、查看线程、进程间关系,以及进程的tcp连接状态:
这里写图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值