socket网络编程——多线程并发服务器

多线程并发服务器

线程作为程序执行的最小单位,一个进程中可以拥有多条线程,所有线程可以共享进程的内存区域,线程通常在运行时也需要一组寄存器、内存、栈等资源的支撑。


文章目录

  • 多线程并发服务器
  • 说明
  • server端
  • client端
  • pthread_creat()


说明

在使用线程模型开发服务器时需考虑以下问题:
1.调整进程内最大文件描述符上限
2.线程如有共享数据,考虑线程同步
3.服务于客户端线程退出时,退出处理。(退出值,分离态)
4.系统负载,随着链接客户端增加,导致其它线程不能及时得到CPU

:以下server端和client段的代码中的接口函数均采用错误封装处理后的函数(首字母大写),便于调试时定位错误出处,具体错误封装代码可参考以下博文:https://blog.csdn.net/weixin_42734533/article/details/123531214?spm=1001.2014.3001.5502
PS:若不考虑错误函数封装也可,只需将代码中的涉及的接口函数(如:Socket、Listen、Connect…)的首字母改成小写(如:socket、listen、connect…),程序运行时直接调用系统库中的函数,将不会调用错误函数封装中的函数。

server端

#include 
#include 
#include 
#include 
#include 
#include 
#include   //IP转换函数
#include   //toupper函数头文件
#include 
#include 
#include 
#include 
#include 
#include   //线程头文件

#include "wrap.h"  // 

#define MAXLINE 800
#define INET_ADDRSTRLEN 16
#define SERV_PORT 6666


struct s_info{  //定义一个结构体,将客户端的地址与connfd进行捆绑
    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 other size has beens closed\n");
            break;
        }
        printf("Data from client is:%s",buf);
        for(i=0;i<n;i++)
        {
            buf[i]=toupper(buf[i]);
        }
        Write(ts->connfd,buf,MAXLINE); //写回客户端
            memset(buf,0,n);        
    }
    Close(ts->connfd);
    pthread_exit(0);
    
}
    
int main(void)
{
        struct sockaddr_in servaddr, cliaddr;
        socklen_t cliaddr_len;
        int listenfd, connfd;
        char buf[MAXLINE];
                char str[MAXLINE];
        int i, n;
                pthread_t tid;  //定义pthread_t型的变量
            struct s_info ts[100]; //创建结构体数组,设置线程上限
            i=0;

        listenfd = Socket(AF_INET, SOCK_STREAM, 0);

        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(SERV_PORT);
        Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        Listen(listenfd, 20);

            printf("Accepting connections ...\n");
                cliaddr_len=sizeof(cliaddr);
     
                 while(1)
                {
                         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]);  //将ts[i]作为参数传递给子线程do_work()函数
             pthread_detach(tid);     //将线程设置成分离的,线程运行结束后会自动释放所有资源
             i++;  //起下一线程
                   }

        //        return 0;
                pthread_exit(0);
}

client端

#include 
#include 
#include 
#include 
#include 
#include 

#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 6666

int main(int argc, char *argv[])
{
        struct sockaddr_in servaddr;
        char buf[MAXLINE];
        int sockfd, n;

        sockfd = Socket(AF_INET, SOCK_STREAM, 0);

        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
        servaddr.sin_port = htons(SERV_PORT);

        Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

        while (fgets(buf, MAXLINE, stdin) != NULL) {
                Write(sockfd, buf, strlen(buf));
                n = Read(sockfd, buf, MAXLINE);
                if (n == 0) {
                        printf("the other side has been closed.\n");
                        break;
                } else
                        Write(STDOUT_FILENO, buf, n);
        }
        Close(sockfd);
        return 0;
}

pthread_creat()

pthread_creat()函数创建线程
函数原型声明:

#include 
int pthread_creat(
pthread_t *restrict tidp, //新创建的线程ID指向的内存单元。
const pthread_attr_t *restrict attr, //线程属性,默认为NULL
void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开
始运行,该函数的参数就是 void*类型的指针,也就是第四个参数【void *restrict arg】
void *restrict arg //默认为NULL。若上述函数需要参数,将参数放入结构
中并将地址作为arg传入。
)

socket网络编程——多线程并发服务器_第1张图片
pthread_detach()作用:线程分离
创建一个线程在运行结束后,存在一部分资源没有回收(退出状态码),所以需要调用pthread_join来等待线程运行结束,从而回收资源。

但是调用pthread_join(pthread_id)后,该线程没有运行结束,调用者会被阻塞。当主线程再创建子线程时,子线程被阻塞,影响使用。基于此,需要在子线程加入代码pthread_detach(pthread_self)或者主线程中调用pthread_detach(thread_id),这就将子线程的状态设置为detached(脱离的)即子线程运行结束后会自动释放所有资源

上述多线程程序在编译过程中会出现:【undefined reference to ‘pthread_create’】
原因在于:pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a,所以在使用pthread_create()创建线程,以及调用 pthread_atfork()函数建立fork处理程序时,需要链接该库。

解决:
在编译中要加 -lpthread参数

gcc thread.c -o thread -lpthread

thread.c为你些的源文件,不要忘了加上头文件#include
编译运行指令如下:

gcc server.c wrap.c -o server -lpthread

PS:接下来将详细介绍,基于多线程的多客户端与服务器交互:
多线程,多客户端与服务器交互

你可能感兴趣的:(线程,socket,网络,tcp/ip,linux,线程)