Linux实现多进/线程并发服务端Socket通信

多进程并发服务端

程序流程

在这里插入图片描述
服务端实现流程:

  1. 创建套接字,绑定地址结构,设置监听上限等。
  2. 循环调用accept函数接收客户端请求并创建子进程。
  3. 子进程中关闭套接字(不进行其他操作),父进程中完成服务端与客户端数据通信。
  4. 在父进程中注册信号捕捉函数,等待子进程结束并进行回收,避免僵尸进程的出现。
  5. 调用read函数循环读取客户端数据,完成大小写转换,调用write函数写回数据至客户端。

服务端程序

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <signal.h>
#include "wrap.h"
using namespace std;
//定义服务端端口号
#define SERVER_PORT  9527

void catch_child(int signum)
{
    while((waitpid(0,NULL,WNOHANG))>0);
    return ;
}
int main (int argc ,char*argv[])
{
    int lfd=0;//用于监听的套接字
    int cfd=0;//用于通信的套接字

    pid_t pid;
    int ret ;
    char buf[BUFSIZ];

    //创建套接字
    lfd=Socket(AF_INET,SOCK_STREAM,0);
    //创建地址结构
    struct sockaddr_in server_addr,client_addr;
    socklen_t client_addr_len;
    //初始化
    //memset(&server_addr,0,sizeof(server_addr));//将地址结构清零
    bzero(&server_addr,sizeof(server_addr));

    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(SERVER_PORT);
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    //绑定地址结构
    Bind(lfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
    //设置监听
    Listen(lfd,128);
   
    client_addr_len=sizeof(client_addr);
    // 循环接收客户端请求
    while(1)
    {
        cfd=Accept(lfd,(struct sockaddr*)&client_addr,&client_addr_len);
        //创建子进程
        pid=fork();
        if (pid<0)//检查创建进程是否成功
        {
           sys_err("fork error"); 
        }
        else if(pid==0)//子进程
        {
            close(lfd);//关闭监听套接字
            break;
        }
        //父进程
        else{
            struct sigaction act;
            act.sa_handler=catch_child;
            sigemptyset(&act.sa_mask);
            act.sa_flags=0;
            //注册新号捕捉函数
            ret=sigaction(SIGCHLD,&act,NULL);
            if(ret!=0)
            {
                sys_err("sigaction error");
            }
            //关闭套接字
            close(cfd);
            continue;
        }
    }
        if(pid==0)
        {
            //循环读取客户端数据信息
            for(;;)
            {
                ret=Read(cfd,buf,sizeof(buf));
                //输出到屏幕
                write(STDOUT_FILENO,buf,ret);
                if(ret==0)
                {
                    //读完数据后关闭套接字
                    close(cfd);
                    exit(1);
                }
                //完成大小写转换
                for (int i=0;i<ret;i++)
                {
                    buf[i]=toupper(buf[i]);
                }
                //数据写回到buf
                write(cfd,buf,ret);     
            }
        }
    return 0;
}

客户端程序

这里与之前的客户端程序一样

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <ctype.h>
#include <arpa/inet.h>
#include "wrap.h"
using namespace std;

//定义一个端口号(服务端)
#define SERVER_PORT  9527
//#define BUFSIZ _IO_BUFSIZ

// void sys_err(const char *str)
// {
//     perror(str);
//     exit(1);
// }
int main (int argc,char *argv[])
{
    int ret=0; 
    int c_fd=0;//创建用于通信的文件描述符
    char buf[BUFSIZ];
    struct sockaddr_in server_addr;//创建服务端地址结构体
    //初始化成员
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(SERVER_PORT);
    //server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//任意有效ip

    //将点分十进制的ip地址转化为用于网络传输的二进制数值格式
    inet_pton(AF_INET,"127.0.0.1",&(server_addr.sin_addr));
    c_fd=Socket(AF_INET,SOCK_STREAM,0);//创建套接字
    if(c_fd==-1)
    {
        sys_err("socket error");
    }
    //客户端与服务端建立连接
    ret=connect(c_fd,(struct sockaddr*)&server_addr,sizeof(server_addr));
    if(ret==-1)
    {
         sys_err("connect error");
    }
    //循环写入数据
    while(1)
    {
        fgets(buf,sizeof(buf),stdin);
        write(c_fd,buf,strlen(buf));
        ret=read(c_fd,buf,sizeof(buf));
        write(STDOUT_FILENO,buf,ret);
    }
    //关闭套接字
    close(c_fd);
    return 0;
}

运行结果

服务端:
在这里插入图片描述
客户端:在这里插入图片描述
在这里插入图片描述
客户端中止进程(Ctrl+C):
如果子进程不进行回收,会出现僵尸进程
在这里插入图片描述
在这里插入图片描述

多线程并发服务端

程序流程

在这里插入图片描述
服务端实现流程:

  1. 创建套接字,绑定地址结构,设置监听上限等。
  2. 循环调用accept函数接收客户端请求并创建子线程。
  3. 子线程回调函数用于完成客户端与服务端的数据通信,调用read函数循环读取客户端数据,完成大小写转换,调用write函数写回数据至客户端。
  4. 在主线程中完成线程分离(调用pthread_detach函数),自动回收子线程。

服务端程序

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <signal.h>
#include <pthread.h>
#include "wrap.h"
using namespace std;
//定义服务端端口号
#define SERVER_PORT  9527

struct s_info {                     //定义一个结构体, 将地址结构跟cfd捆绑
    struct sockaddr_in client_addr;
    int cfd;
};

//子线程回调函数
void *do_work(void *arg)
{
    int n,i;
    struct s_info *ts = (struct s_info*)arg;
    char buf[BUFSIZ];
    char str[INET_ADDRSTRLEN];      //#define INET_ADDRSTRLEN 16  可用"[+d"查看

    while (1) {
        n = Read(ts->cfd, buf, BUFSIZ);                     //读客户端
        if (n == 0) {
            printf("the client %d closed...\n", ts->cfd);
            break;                                              //跳出循环,关闭cfd
        }
        printf("received from %s at PORT %d\n",
                inet_ntop(AF_INET, &(*ts).client_addr.sin_addr, str, sizeof(str)),
                ntohs((*ts).client_addr.sin_port));             //打印客户端信息(IP/PORT)

        for (i = 0; i < n; i++) 
            buf[i] = toupper(buf[i]);                           //小写-->大写

        Write(STDOUT_FILENO, buf, n);                           //写出至屏幕
        Write(ts->cfd, buf, n);                                 //回写给客户端
    }
    Close(ts->cfd);

    return (void *)0;   //pthread_exit()  结束子线程
}
int main (int argc ,char*argv[])
{
    int lfd=0;//用于监听的套接字
    int cfd=0;//用于通信的套接字

    pid_t pid;
    int ret,i=0 ;
    char buf[BUFSIZ];

    //创建套接字
    lfd=Socket(AF_INET,SOCK_STREAM,0);
    //创建地址结构
    struct sockaddr_in server_addr,client_addr;
    socklen_t client_addr_len;

    pthread_t tid;
    //根据最大线程数创建结构体数组
    struct s_info ts[256];  

    //初始化
    //memset(&server_addr,0,sizeof(server_addr));//将地址结构清零
    bzero(&server_addr,sizeof(server_addr));

    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(SERVER_PORT);
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    //绑定地址结构
    Bind(lfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
    //设置监听
    Listen(lfd,128);
   
    client_addr_len=sizeof(client_addr);
    // 循环接收客户端请求
    while(1)
    {
        cfd=Accept(lfd,(struct sockaddr*)&client_addr,&client_addr_len);

        ts[i].client_addr = client_addr;
        ts[i].cfd = cfd;

        /* 达到线程最大数时,pthread_create出错处理, 增加服务器稳定性 */
        pthread_create(&tid, NULL, do_work, (void*)&ts[i]);
        //子线程分离,防止僵线程产生.
        pthread_detach(tid);                                                    
        i++;
    }
    return 0;
}

客户端程序

同多进程客户端

运行结果

服务端:
在这里插入图片描述
客户端:
在这里插入图片描述
在这里插入图片描述

附录(错误处理函数封装)

//wrap.cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include "wrap.h"
void sys_err(const char *str)
{
    perror(str);
    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
			sys_err("accept error");
	}
	return n;
}

int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    int n;
	if ((n = bind(fd, sa, salen)) < 0)
		sys_err("bind error");
    return n;
}
// int Connect(int fd, const struct sockaddr *sa, socklen_t salen)
// {
//     int n;
//     n = connect(fd, sa, salen);
// 	if (n < 0) {
// 		perr_exit("connect error");
//     }
//     return n;
// }

int Listen(int fd, int backlog)
{
    int n;
	if ((n = listen(fd, backlog)) < 0)
		sys_err("listen error");
    return n;
}
int Socket(int domain, int type, int protocol)
{
	int n;
	if ((n = socket(domain, type, protocol)) < 0)
		sys_err("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;
// }

// /*参三: 应该读取的字节数*/                          //socket 4096  readn(cfd, buf, 4096)   nleft = 4096-1500
// 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;                  //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;   //nleft = 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) {   //"hello\n"
// 			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;
// }

// /*readline --- fgets*/    
// //传出参数 vptr
// 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[] = hello\n
// 			*ptr++ = c;
// 			if (c == '\n')
// 				break;
// 		} else if (rc == 0) {
// 			*ptr = 0;
// 			return n-1;
// 		} else
// 			return -1;
// 	}
// 	*ptr = 0;

// 	return n;
// }
//wrap.h
#ifndef __WRAP_H_
#define __WRAP_H_

void sys_err(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 domain, 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);
#endif
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值