嵌入式Linux网络编程,TCP多并发服务器,TCP多线程并发服务器,TCP多进程并发服务器


在这里插入图片描述

TCP循环服务器模型 TCP多进程并发服务器 TCP多线程服务器
socket(...);
bind(...);
listen(...);
while(1){
  accept(...);
  process(...);
  close(...);
}
socket(...);
bind(...);
listen(...);
while(1){
  accpet(...);
  if(fork(...) == 0){
    process(...);
    close(...);
    exit(...);
  }
  close(...);
}
socket(...);
bind(...);
listen(...);
while(1){
  accpet(...);
  if((pthread_create(...))!==-1) {
    process(...);
    close(...);
    exit(...);
  }
  close(...)
}
TCP服务器一般很少用 TCP并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是由服务器创建一个子进程来处理。 多线程服务器是对多进程的服务器的改进

1,TCP多线程并发服务器

1.1,头文件net.h

#ifndef __NET_H__
#define __NET_H__

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

#define SERV_IP_ADDR "192.168.31.100"
#define SERV_PORT 5005
#define BACKLOG 5
#define QUIT_STR "quite"

#endif

1.2,客户端client.c

>## /* ./client serv_ip serv_port */
#include "net.h"

void usage(char *s)
{
	printf("Usage: %s <serv_ip> <serv_port>\n",s);
	printf("\tserv_ip: server ip address\n");
	printf("\tserv_port: server port(>5000)\n ");
}
int main(int argc, const char *argv[])
{
	int fd;
	short port;
	struct sockaddr_in sin;
	if(argc != 3)
	{
		usage((char *)argv[0]);
		exit(1);
	}
	if((port = atoi(argv[2])) < 5000)
	{
		usage((char *)argv[0]);
		exit(1);
	}
	/* 1 创建socket fd */
	if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		perror("socket");
		exit(-1);
	}
	
	/* 2 连接服务器 */
	/* 2.1 填充struct sockaddr_in结构体变量*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);//转为网络字节序端口号
	if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0)
	{
		perror("inet_pton");
		goto _error1;
	}

	/* 2.2 连接服务器*/
	if(connect(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
	{
		perror("connect");
		goto _error1;
	}
	/* 3 读写*/
	char buf[BUFSIZ];
	while(1)
	{
		bzero(buf,BUFSIZ);
		if(fgets(buf,BUFSIZ-1,stdin) == NULL)
		{
			continue;
		}
		write(fd,buf,strlen(buf));
		if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
		{
			printf("client is existing!\n");
			break;
		}
	}

_error1:
	close(fd);
	return 0;
}

1.3,服务器端server.c

#include "net.h"
#include <pthread.h>

/* 线程传参  */
typedef struct{
	int addr;//客户端IP地址
	int port;//客户端端口号
	int fd;//为请求链接的客户端分配的新的socket fd
}ARG;

/* 子线程处理函数 */
void client_data_handle(void *arg);

int main(int argc, const char *argv[])
{
	pthread_t tid;//子线程ID号
	int fd;
	struct sockaddr_in sin;//如果是IPV6的编程,要使用struct sockddr_in6结构体(详细情况请参考man 7 ipv6),通常更通用的方法可以通过struct sockaddr_storage来编程

	/* 1 创建socket fd */
	if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		perror("socket");
		exit(-1);
	}
	/* 优化 1 允许绑定地址快速重用 */ 
	int b_reuse = 1;
	setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int)); 
	
	/* 2 绑定 */
	/* 2.1 填充struct sockaddr_in 结构体变量*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERV_PORT);
#if 1
	/* 优化 2 让服务器可以绑定在任意的IP上*/
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
	if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0)
	{
		perror("inet_pton");
		goto _error1;
	}
#endif
	/* 2.2 绑定*/
	if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)))
	{
		perror("bind");
		goto _error1;
	}

	/* 3 使用listen()把主动套接字变成被动套接字 */
	if(listen(fd,BACKLOG) < 0)
	{
		perror("listen");
		goto _error1;
	}
	
	int newfd = -1;
	struct sockaddr_in cin;
	socklen_t cin_addr_len = sizeof(cin);
	/* 优化 4 用多进程/多线程处理已经建立好链接的客户端数据*/
	while(1)
	{
		/* 4 阻塞等待客户端链接请求*/
		/* 优化 3 通过函数获取刚建立链接的客户端的IP地址和端口号*/
		if((newfd = accept(fd,(struct sockaddr *)&cin,&cin_addr_len)) < 0)
		{
			perror("connect");
			goto _error1;
		}
		
		ARG arg;
		arg.addr = cin.sin_addr.s_addr;
		arg.port = ntohs(cin.sin_port);
		arg.fd = newfd;
		
		if(pthread_create(&tid,NULL,(void *)client_data_handle,(void *)&arg) != 0)
		{
			perror("pthread_create");
			goto _errno2;
		}
	}

_errno2:
	close(newfd);
_error1:
	close(fd);
	return 0;
}

void client_data_handle(void *arg)
{
	int ret = -1;//read()是个阻塞函数,要做读写错误的工程处理
	char buf[BUFSIZ];//BUFSIZ是系统提供的
	char cin_ipv4_addr[16];
	ARG parg = *(ARG *)arg;
		
	if(inet_ntop(AF_INET,&parg.addr,cin_ipv4_addr,sizeof(cin_ipv4_addr)) < 0)
	{
		perror("inet_ntop");
		exit(-1);
	}

	printf("client(:%s potr(:%d\n",cin_ipv4_addr,parg.port);

	printf("the client pthread fd is %d\n",parg.fd);
	while(1)
	{
		/* 5 读写*/
		bzero(buf,BUFSIZ);
		do
		{
			ret = read(parg.fd,buf,BUFSIZ-1);
		}while(ret < 0 && errno == EINTR);//阻塞读写
		if(ret < 0)
		{
			perror("read");
			break;
		}
		if(ret == 0)//对方已关闭
		{
			break;
		}

		printf("receive data: %s",buf);
	
		
		if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
		{
			printf("client is existing!\n");
			break;
		}
	}
	close(parg.fd);
	printf("pthread fd %d is closed!\n",parg.fd);
}

2,TCP多进程并发服务器

2.1,头文件net.h

#ifndef __NET_H__
#define __NET_H__

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

#define SERV_IP_ADDR "192.168.31.100"
#define SERV_PORT 5005
#define BACKLOG 5
#define QUIT_STR "quite"

#endif

2.2,客户端程序client.c

/* ./client serv_ip serv_port */
#include "net.h"

void usage(char *s)
{
	printf("Usage: %s <serv_ip> <serv_port>\n",s);
	printf("\tserv_ip: server ip address\n");
	printf("\tserv_port: server port(>5000)\n ");
}
int main(int argc, const char *argv[])
{
	int fd;
	short port;
	struct sockaddr_in sin;
	if(argc != 3)
	{
		usage((char *)argv[0]);
		exit(1);
	}
	if((port = atoi(argv[2])) < 5000)
	{
		usage((char *)argv[0]);
		exit(1);
	}
	/* 1 创建socket fd */
	if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		perror("socket");
		exit(-1);
	}
	
	/* 2 连接服务器 */
	/* 2.1 填充struct sockaddr_in结构体变量*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);//转为网络字节序端口号
	if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0)
	{
		perror("inet_pton");
		goto _error1;
	}

	/* 2.2 连接服务器*/
	if(connect(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
	{
		perror("connect");
		goto _error1;
	}
	/* 3 读写*/
	char buf[BUFSIZ];
	while(1)
	{
		bzero(buf,BUFSIZ);
		if(fgets(buf,BUFSIZ-1,stdin) == NULL)
		{
			continue;
		}
		write(fd,buf,strlen(buf));
		if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
		{
			printf("client is existing!\n");
			break;
		}
	}

_error1:
	close(fd);
	return 0;
}

2.3,服务器端程序service.c

#include "net.h"
#include <pthread.h>
#include <signal.h>

/* 线程传参  */
typedef struct{
	int addr;//客户端IP地址
	int port;//客户端端口号
	int fd;//为请求链接的客户端分配的新的socket fd
}ARG;

/* 子线程处理函数 */
void client_data_handle(ARG *arg);
void sig_child_handle(int signo)
{
	if(SIGCHLD == signo)
	{
		waitpid(-1,NULL,WNOHANG);
	}
}

int main(int argc, const char *argv[])
{
	int fd;
	struct sockaddr_in sin;//如果是IPV6的编程,要使用struct sockddr_in6结构体(详细情况请参考man 7 ipv6),通常更通用的方法可以通过struct sockaddr_storage来编程
	pid_t pid;//子进程ID

	signal(SIGCHLD,sig_child_handle);

	/* 1 创建socket fd */
	if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		perror("socket");
		exit(-1);
	}
	/* 优化 1 允许绑定地址快速重用 */ 
	int b_reuse = 1;
	setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int)); 
	
	/* 2 绑定 */
	/* 2.1 填充struct sockaddr_in 结构体变量*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERV_PORT);
#if 1
	/* 优化 2 让服务器可以绑定在任意的IP上*/
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
	if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0)
	{
		perror("inet_pton");
		goto _error1;
	}
#endif
	/* 2.2 绑定*/
	if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)))
	{
		perror("bind");
		goto _error1;
	}

	/* 3 使用listen()把主动套接字变成被动套接字 */
	if(listen(fd,BACKLOG) < 0)
	{
		perror("listen");
		goto _error1;
	}
	
	struct sockaddr_in cin;
	socklen_t cin_addr_len = sizeof(cin);
	/* 优化 4 用多进程/多线程处理已经建立好链接的客户端数据*/
	while(1)
	{
		int newfd = -1;
		/* 4 阻塞等待客户端链接请求*/
		/* 优化 3 通过函数获取刚建立链接的客户端的IP地址和端口号*/
		if((newfd = accept(fd,(struct sockaddr *)&cin,&cin_addr_len)) < 0)
		{
			perror("connect");
			goto _error1;
		}
		if((pid = fork()) < 0)
		{
			perror("fork");
			break;
		}
		else if(pid == 0)
		{
			ARG arg;
			arg.addr = cin.sin_addr.s_addr;
			arg.port = ntohs(cin.sin_port);
			arg.fd = newfd;

			close (fd);
			client_data_handle(&arg);
			return 0;
		}
		else
		{
			close(newfd);
		}
		close(newfd);

	}

_error1:
	close(fd);
	return 0;
}

void client_data_handle(ARG *arg)
{
	int ret = -1;//read()是个阻塞函数,要做读写错误的工程处理
	char buf[BUFSIZ];//BUFSIZ是系统提供的
	char cin_ipv4_addr[16];
	ARG parg = *arg;
		
	if(inet_ntop(AF_INET,&parg.addr,cin_ipv4_addr,sizeof(cin_ipv4_addr)) < 0)
	{
		perror("inet_ntop");
		exit(-1);
	}

	printf("client(:%s potr(:%d\n",cin_ipv4_addr,parg.port);

	printf("the client process fd is %d\n",parg.fd);
	while(1)
	{
		/* 5 读写*/
		bzero(buf,BUFSIZ);
		do
		{
			ret = read(parg.fd,buf,BUFSIZ-1);
		}while(ret < 0 && errno == EINTR);//阻塞读写
		if(ret < 0)
		{
			perror("read");
			break;
		}
		if(ret == 0)//对方已关闭
		{
			break;
		}

		printf("receive data: %s",buf);
		
		if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
		{
			printf("client is existing!\n");
			break;
		}
	}
	close(parg.fd);
	printf("process fd %d is closed!\n",parg.fd);

	return ;
}

猜你喜欢

转载自blog.csdn.net/m0_37542524/article/details/84259867