Summary of network programming three

1. Concurrent server model

【1】 Loop server

1>Only one client's request can be processed at a time, and the next client can only be processed after the client exits.

2>Disadvantages: Clients processed by the loop server cannot have time-consuming operations

//*****Model******

sfd = socket();
bind();
listen();
while(1)
{
    newfd = accept();
    while(1)
    {
        recv();
        send();
    }
    close(newfd);
}
close(sfd);

【2】Concurrent server

1>Can handle multiple client requests at the same time

2>The parent process/main thread is specifically responsible for connections, and creates child processes/branch threads for interacting with clients.

~multithreading:

//*****Model******

sfd = socket();
bind();
listen();
while(1)
{
    newfd = accept();

    pthread_create();

    pthread_detach(tid);

}

close(sfd);

void * callBack(void * arg)

{

      recv();
      send();
      close(newfd);

      pthread_exit(NULL);
}

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

#define ERR_MSG(msg) do{\
	fprintf(stderr,"liine %d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.0.79"  //本机IP 
#define PORT 6666          // 1024-49151

void* deal_cli_msg(void *arg);
//需要传递给线程处理函数的参数
struct cli_msg
{
	int newfd;
	struct sockaddr_in cin;
};

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("sfd=%d\n",sfd);
	//允许端口快速重用
	int reuse=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	//填充地址信息结构体
	//真是的地址信息结构体根据地址族制定  AF_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;//必须填 AF_INET
	sin.sin_port = htons(PORT);//端口号, 1024-49151
	sin.sin_addr.s_addr = inet_addr(IP);//本机IP,ifconfig
	//将IP和端口号绑定到套接字上
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind sucess __%d__\n",__LINE__);

	//将套接字设置为被动监听状态,监听是否有客户端连接成功
	if(listen(sfd,128)<0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success __%d__\n",__LINE__);
	struct sockaddr_in cin;        // 存储连接成功的客户端地址信息  
	socklen_t addrlen = sizeof(cin);
	
	pthread_t tid;
	int newfd=-1;
	struct cli_msg info;
	while(1)
	{
		//阻塞函数,从已完成连接的队列头中获取一个客户端信息
		//该文件描述符才是与客户端通信的文件描述符
		newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
		if(newfd<0)
		{
			ERR_MSG("accept");
			return -1;
		}
		printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
		
		info.newfd=newfd;
		info.cin=cin;
		//能运行到当前位置
		if(pthread_create(&tid,NULL,deal_cli_msg,&info)!=0)
		{
			fprintf(stderr,"line:%d pthread_createfailed\n",__LINE__);
			return -1;
		}
		pthread_detach(tid); //分离线程
	}
	close(sfd);
	return 0;
}

void* deal_cli_msg(void *arg)
{
	int newfd=((struct cli_msg*)arg)->newfd;
	struct sockaddr_in cin=((struct cli_msg*)arg)->cin;
	char buf[128]="";
	ssize_t res=0;
	while(1)
	{
		//接收
		res=recv(newfd,buf,sizeof(buf),0);
		if(res<0)
		{
			ERR_MSG("recv");
			break;
		}
		else if(0==res)
		{
			printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
			break;
		}
		printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
		//发送
		strcat(buf,"*_*");
		if(send(newfd,buf,sizeof(buf),0)<0)
		{
			ERR_MSG("send");
			break;
		}
		printf("send sucess\n");
	}
	close(newfd);
	pthread_exit(NULL);
}

2. IO model

【1】Blocking IO

After creating a socket file descriptor, it defaults to blocking IO mode (read, write, recv, send, recvfrom, sendto)

【2】Non-blocking IO

1>Prevent the process from blocking on the IO function, but if you want to obtain valid data, you need to poll

2>When a program uses a non-blocking IO mode socket, it needs to use a loop to constantly determine whether the file descriptor has readable data, which is called polling.

3>The application keeps polling the kernel to monitor whether IO events occur, resulting in high CPU consumption.

1) fcntl function

 【3】Signal drive IO

1>Asynchronous communication method

2>Signal-driven IO means: telling the kernel in advance that when an IO event occurs on a certain file descriptor, the kernel will notify the relevant process: SIGIO

3> For TCP, signal-driven IO is not useful for TCP because signals are generated too frequently and cannot distinguish which file descriptor is sent.

【4】IO multiplexing (key points)

1>If you need to process multiple input and output streams at the same time in a process, use a single process and single thread to process multiple input and output requests at the same time.

2>If multi-process and multi-thread cannot be used, you can choose to use IO multiplexing

3> Since there is no need to create new processes and threads, it reduces system resource overhead and the number of context switches.

4> Allows multiple IO operations to be performed at the same time. Once the kernel finds that a process performs one or more IO events, it will notify the process.

1)select  

 Model:

 Code:

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

#define ERR_MSG(msg) do{\
	fprintf(stderr,"liine %d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.0.211"  //本机IP 
#define PORT 6666          // 1024-49151

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind sucess __%d__\n",__LINE__);

	//允许端口快速使用
	int reuse=-1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	printf("允许端口快速重用成功\n");

	struct sockaddr_in sin;
	sin.sin_family =AF_INET;
	sin.sin_port=htons(PORT);//端口号
	sin.sin_addr.s_addr=inet_addr(IP);//本机号

	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind sucess__%d__\n",__LINE__);

	//将套接字设置为被动监听状态,监听是否有客户端连接成功
	if(listen(sfd,128)<0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success __%d__\n",__LINE__);

	//设置一个读集合
	fd_set  readfds,tmpfds;

	//由于readfds中需要防止要检测的文件描述符,所以不能让他是随机值
	//所以需要将readfds清空
	FD_ZERO(&readfds);
	FD_ZERO(&tmpfds);

	//将需要的文件描述符添加到集合中
	FD_SET(0,&readfds);
	FD_SET(sfd,&readfds);

	int s_res=0;
	char buf[128]="";
	struct sockaddr_in cin;        // 存储连接成功的客户端地址信息  
	socklen_t addrlen = sizeof(cin);

	struct sockaddr_in saveCin[1024-4];//另存客户端地址信息,0,1,2,sfd不可能有对应的客户端
	int newfd =-1;
	ssize_t res=0;
	int maxfd=sfd;//最大文件描述符

	while(1)
	{
		tmpfds=readfds;
		s_res=select(maxfd+1,&tmpfds,NULL,NULL,NULL);
		if(s_res<0)
		{
			ERR_MSG("select");
			return -1;
		}
		else if(0==res)
		{
			printf("time out\n");
			return -1;
		}

		//能运行到当前位置,则代表有文件描述符准备就绪
		//走触发事件的文件描述符对应的处理函数

		//当前集合中有文件描述符准备就绪了
		//当准备就绪,集合会只保留0
		//当sfd准备就绪,集合就会只保留sfd
		//当0和sfd都准备就绪,集合中保留0和sfd

		for(int i=0;i<=maxfd;i++)
		{
			if(!FD_ISSET(i,&tmpfds))//如果不在集合中,则直接往后继续变量
			{
				continue;
			}
			//能运行到当前位置,则说明i代表的文件描述符在tmpfds中
			//要判断i所代表的文件描述符需要走什么对应的函数
			if(0==i)
			{
				printf("触发键盘输入事件>>");
				int sndfd=-1;
				res=scanf("%d %s",&sndfd,buf);
				while(getchar()!=10);
				if(res!=2)
				{
					fprintf(stderr,"请输入正确格式:int string\n");
					continue;
				}
				//能运行到当前位置,则代表输入的格式整数
				if(sndfd<=sfd||sndfd>=1024||!FD_ISSET(sndfd,&readfds))
				{
					fprintf(stderr,"sndfd=%d文件描述符\n",sndfd);
					continue;
				}
				if(send(sndfd,buf,sizeof(buf),0)<0)
				{
					ERR_MSG("send");
					return -1;
				}
			}
			else if(sfd==i)
			{	
				printf("触发客户端连接事件>>");
				fflush(stdout);
				int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
				if(newfd<0)
				{
					ERR_MSG("accept");
					return -1;
				}
				printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
						inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

				saveCin[newfd-4]=cin;

				//将newfd添加到读集合中
				FD_SET(newfd,&readfds);

				//更新maxfd
				maxfd=maxfd>newfd?maxfd:newfd;

			}
			else
			{
				bzero(buf,sizeof(buf));
				//接收
				res=recv(i,buf,sizeof(buf),0);
				if(res<0)
				{
					ERR_MSG("recv");
					return -1;
				}
				else if(0==res)
				{
					printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
							inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

					//关闭文件描述符
					close(i);
					//从集合中剔除该文件描述符
					FD_CLR(i,&readfds);

					//更新maxfd
					//从目前最大的文件描述符中往小的判断
					int j=0;
					for(int j=maxfd;j>=0;j--)
					{
						if(FD_ISSET(j,&readfds))
						{
							maxfd=j;
							break;
						}
					}
					if(j<0)
						maxfd=-1;

					continue;
				}
				printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
						inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

				strcat(buf,"*_*");
				if(send(i,buf,sizeof(buf),0)<0)
				{
					ERR_MSG("send");
					return -1;
				}
				printf("send sucess\n");
			}
		}
	}
	close(sfd);
	return 0;
}

2)poll

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

#define ERR_MSG(msg) do{\
	fprintf(stderr,"liine %d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.0.211"  //本机IP 
#define PORT 6666          // 1024-49151

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind sucess __%d__\n",__LINE__);

	//将套接字设置为被动监听状态,监听是否有客户端连接成功
	if(listen(sfd,128)<0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success __%d__\n",__LINE__);

	//设置一个读集合
	fd_set  readfds,tmpfds;

	//由于readfds中需要防止要检测的文件描述符,所以不能让他是随机值
	//所以需要将readfds清空
	FD_ZERO(&readfds);
	FD_ZERO(&tmpfds);

	//将需要的文件描述符添加到集合中
	FD_SET(0,&readfds);
	FD_SET(sfd,&readfds);

	int s_res=0;
	char buf[128]="";
	struct sockaddr_in cin;        // 存储连接成功的客户端地址信息  
	socklen_t addrlen = sizeof(cin);

	int newfd =-1;
	ssize_t res=-1;
	int maxfd=sfd;

	while(1)
	{
		
		tmpfds=readfds;
		s_res=select(maxfd+1,&tmpfds,NULL,NULL,NULL);
		if(s_res<0)
		{
			ERR_MSG("select");
			return -1;
		}
		if(0==res)
		{
			printf("time out\n");
			return -1;
		}
		
		//能运行到当前位置,则代表有文件描述符准备就绪
		//走触发事件的文件描述符对应的处理函数

		for(int i=0;i<=maxfd;i++)
		{
			if(!FD_ISSET(i,&tmpfds))//如果不在集合中,则直接往后继续变量
			{
				continue;
			}
		//能运行到当前位置,则说明i代表的文件描述符在tmpfds中
		//要判断i所代表的文件描述符需要走什么对应的函数
			if(0==i)
			{
				printf("触发键盘输入事件>>");
				fgets(buf,sizeof(buf),stdin);
				buf[strlen(buf)-1]=0;
				printf("%s\n",buf);
			}
			else if(sfd==1)
			{	
				printf("触发客户端连接事件>>");
				fflush(stdout);
				int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
				if(newfd<0)
				{
					ERR_MSG("accept");
					return -1;
				}
				printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
					inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
	
				//将newfd添加到读集合中
				FD_SET(newfd,&readfds);

				//更新maxfd
				maxfd=maxfd>newfd?maxfd:newfd;

			}
			else
			{
				bzero(buf,sizeof(buf));
			//接收
				res=recv(i,buf,sizeof(buf),0);
				if(res<0)
				{
					ERR_MSG("recv");
					return -1;
				}
				else if(0==res)
				{
					printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
						inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);
				
					//关闭文件描述符
					close(i);
					//从集合中剔除该文件描述符
					FD_CLR(i,&readfds);
				
					//更新maxfd
					//从目前最大的文件描述符中往小的判断
					int j=0;
					for(int j=maxfd;j>=0;j--)
					{
						if(FD_ISSET(j,&readfds))
						{
							maxfd=j;
							break;
						}
					}
					if(j<0)
						maxfd=-1;

					continue;
				}
				printf("[%s:%d] newfd=%d 连接成功 __%d__\n",\
					inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), newfd,__LINE__);

				strcat(buf,"*_*");
				if(send(i,buf,sizeof(buf),0)<0)
				{
					ERR_MSG("send");
					return -1;
				}
				printf("send sucess\n");
			}
		}
	}
	return 0;
}

3. Function library

[1] The concept of library

1>The library is a binary executable file. Compared with binary executable programs, the library cannot be run alone .

        a. All functions stored in the library are functional functions, and only functional functions can be stored.

        b. The main function is not allowed to be stored in the library

        c. The library is written functional code that can be used

2>The library needs to be loaded into memory for use

3>Each operating system has its own library, which is incompatible

Classification of libraries:

Static library, dynamic library

 【2】Static library

1) Static library principle: The functions encapsulated by the static library will be inherited into the executable program when the program is compiled into the link library step.

Advantages: 1>When the program is running, it has nothing to do with the static library, making it easy to transplant.

            2>Static libraries will run faster

Disadvantages: 1>The update and deployment of the program is troublesome

           2> Large, it wastes disk space when storing and wastes memory space when loading and running.

2) Static library production instructions

 step:

1>Divide into files and separate the functional function from the main function, func.c main.c

2>Write a header file for the functional function: func.h

3> Compile main.c and func.c jointly and test whether the file splitting is successful.

4>Use the above instructions to encapsulate func.c into a static library

5>Note: libxxx.a, xxx is the name of the library

3) Use of static libraries

【3】Dynamic library (shared library)

 1) The principle of the dynamic library: The dynamic library postpones the linking step of the library function until the program is running . When the program executes the library function, it will find the dynamic library function.

1>If the dynamic library function does not exist in the memory, the dynamic library function will be loaded into the memory for execution.

2>If the dynamic library function exists in the memory, it will be called directly and the second copy will not be loaded.

3>So there will be only one copy of the dynamic library function in the memory

advantage:

1> Small, save disk space when storing, save memory space when running

2>It is more convenient to update and deploy the program, and there is no need to recompile the executable program.

shortcoming:

1>When the program is running, if the dynamic library is not found, it will cause the program to crash, so using a dynamic library to generate a binary program has low portability.

2>Low operating efficiency

2) Dynamic library production instructions

  step:

1>Divide into files and separate the functional function from the main function, func.c main.c

2>Write a header file for the functional function: func.h

3> Compile main.c and func.c jointly and test whether the file splitting is successful.

4>Use the above instructions to encapsulate func.c into a static library

5>Note: libxxx.so, xxx is the name of the library

3) Use of dynamic libraries

 4 ) Environment variable configuration of dynamic libraries

1> Move the dynamic library to the /lib/ or /usr/lib directory

        a. sudo mv ./libmyfunc.so /usr/lib/

        b. sudo mv libmyfunc.so/lib/

2>Configure environment variables: LD_LIBRARY_PATH

 3>Modify the environment variable Liu Ang’s configuration file

        a. cd / ec/ld.so.conf.d/

        b. sudo touch my.conf to create a file ending with .conf

        c. sudo vim my.conf

        d. Fill in the absolute path of the folder where the dynamic library is located. Note that you can only fill in the absolute path of one dynamic library in one line.

        e. After saving and exiting, execute sudo ldconfig to refresh the environment variables.

Guess you like

Origin blog.csdn.net/weixin_57039874/article/details/130462614
Recommended