Linux网络编程笔记:socket编程(二)

一、概述

前面一篇笔记主要写了socket的基础知识,包括什么是socket,socket在网络模型中的位置,socket编程常用的函数,最后编写了一个简单的单客户端访问服务器通信的程序。

在实际的应用场景中,单客户端访问的机制显然不能满足需求的,要能够实现多个服务器能够同时访问的技术。可以利用多进程和多线程的方式实现多个客户端同时访问的机制。以下主要通过程序的形式,展现是如何实现的。

二、多进程实现

客户端程序不需要更改,跟前一篇内容相同。在创建多进程进行搭建服务器时,需要注意的是,子进程的回收。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/stat.h>
#include<string.h>
#include <sys/socket.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<sys/wait.h>
#include<signal.h>

#define MAX_LISTEN_NUM      10


void catch_child(int signum){   		//捕捉函数
	while(waitpid(0,NULL,WNOHANG) > 0);
	return ;
}

int main(void){
	int lfd,cfd;
	int pid;
	int ret,rd_ret;
	char buf[1024],client_IP[1024];

	struct sockaddr_in server_addr,client_addr;
	socklen_t client_addr_len;
	
	lfd = socket(AF_INET, SOCK_STREAM, 0);   //创建socket
	if(lfd == -1){
		printf("socket failed\n");
		exit(1);
	}


	server_addr.sin_family = AF_INET;    //设置服务器IP+PORT
	server_addr.sin_port = htons(5678);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	ret = bind(lfd,  (struct sockaddr *)&server_addr, sizeof(server_addr));    //绑定
	if(ret != 0){
		printf("bind failed\n");
		exit(1);
	}

	ret = listen(lfd, MAX_LISTEN_NUM);   //设置监听数
	if(ret != 0){
		printf("listen failed\n");
		exit(1);
	}

	client_addr_len = sizeof(client_addr);	
	while(1){
		cfd = accept(lfd,(struct sockaddr *)&client_addr, &client_addr_len);  	//等待连接


		pid = fork();

		if(pid < 0){
		printf("fork failed\n");
		exit(1);
		}

		if(pid == 0){   	 //子进程 退出循环
			close(lfd);
			break;
		}
		else if (pid > 0){   //父进程   继续循环,去监听下一个连接者,并回收子进程
			struct sigaction act;
			act.sa_handler = catch_child;
			sigemptyset(&act.sa_mask);
			act.sa_flags = 0;
	
			ret = sigaction(SIGCHLD,&act,NULL);
			if(ret != 0){
				close(cfd);
			}
			continue;
		}
	  }

	if(pid == 0){  
 	 
		while(1){
			rd_ret = read(cfd,buf,sizeof(buf));
			if(rd_ret == 0){     //已经读到结尾(对端已经关闭)
				close(lfd);
				exit(1);
			}
			
			write(STDOUT_FILENO, buf,rd_ret);
		}
		
	}


	return 0;
}

客户端1写数据:

socket高并发解决方案 java socket并发编程_多线程

客户端2写数据:

socket高并发解决方案 java socket并发编程_多线程_02


服务器读数据

socket高并发解决方案 java socket并发编程_网络_03

三、多线程实现

在多线程里,线程回收不像进程那样复杂,只要将创建的线程进行分离即可。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/stat.h>
#include<string.h>
#include <sys/socket.h>
#include<arpa/inet.h>
#include<ctype.h>
#include<sys/wait.h>
#include<signal.h>


#define MAX_LISTEN_NUM      10


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

void *do_work(void *arg)
{
    int n,i;
    struct s_info *ts = (struct s_info*)arg;
    char buf[1024];
    char str[INET_ADDRSTRLEN];      //#define INET_ADDRSTRLEN 16  

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

        write(STDOUT_FILENO, buf, n);                           //写出至屏幕
       
    }
    close(ts->connfd);
    return (void *)0;
}

int main(void)
{
	struct sockaddr_in servaddr, cliaddr;
	socklen_t cliaddr_len;
	int listenfd, connfd;
	int ret;
	pthread_t tid;
	struct s_info ts[256];      //根据最大线程数创建结构体数组.
	int i = 0;

	listenfd = socket(AF_INET, SOCK_STREAM, 0);   //创建socket
	if(listenfd == -1){
		printf("socket failed\n");
		exit(1);
	}


	servaddr.sin_family = AF_INET;    //设置服务器IP+PORT
	servaddr.sin_port = htons(5678);
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	ret = bind(listenfd,  (struct sockaddr *)&servaddr, sizeof(servaddr));    //绑定
	if(ret != 0){
		printf("bind failed\n");
		exit(1);
	}

	ret = listen(listenfd, MAX_LISTEN_NUM);   //设置监听数
	if(ret != 0){
		printf("listen failed\n");
		exit(1);
	}

	printf("Accepting client connect ...\n");

	while (1) {
	cliaddr_len = sizeof(cliaddr);
	connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);   //阻塞监听客户端链接请求
	ts[i].cliaddr = cliaddr;
	ts[i].connfd = connfd;

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

	return 0;
}

客户端1写数据:

socket高并发解决方案 java socket并发编程_网络_04


客户端2写数据:

socket高并发解决方案 java socket并发编程_socket_05


服务器读数据:

socket高并发解决方案 java socket并发编程_多线程_06