Network programming learning diary ---- signal driven

Posted by everogrin on Thu, 10 Feb 2022 08:49:48 +0100

1, IO model - signal driven

Using the signal mechanism in system programming, first let the program install the SIGIO signal processing function (when the socket has data, the system will automatically send SIGIO signal). We only need to capture this signal in the program to know whether there is data. If there is data, we can read it out.

 

Model

signal(SIGIO,fun);

sockfd ---- as long as soockfd has data generation

Then a sigio signal will be generated automatically

2. Characteristics and steps of signal driving:

Features: only applicable to UDP protocol, not TCP protocol

Steps:

1) Since we don't know when the data will arrive, we must capture the SIGIO signal before the data arrives

2) Set the owner of the socket to tell the system the PID number of the process corresponding to the socket

3) If you use the signal driven model, you need to add the trigger mode attribute of the signal to the socket.

 

3. How to set the owner of socket ---- fcntl ------ man 2 fcntl

       #include <unistd.h>
       #include <fcntl.h>

       int fcntl(int fd, int cmd, ... /* arg */ );

Parameters

fd: file descriptor

      cmd:   F_SETOWN (int) -- function: set owner

arg: pid number of the process -- "getpid();

Return value:

Success: 0

Failed - 1

For example:

  fcntl(sockfd,F_SETOWN ,getpid());

 

4. How to add signal trigger mode attribute to socket

       #include <unistd.h>
       #include <fcntl.h>

       int fcntl(int fd, int cmd, ... /* arg */ );

Parameters

fd: file descriptor

         cmd: F_SETFL (int) -- sets the properties of the socket

arg: attribute to be set ----- O_ASYNC

Return value:

Success: 0

Failed - 1

 

For example:

   int state;

    state= fcntl(sockfd,F_GETFL);

    state  |= O_ASYNC

    fcntl (sockfd,F_SETFL,state);

 

The server

#include "head.h"

int sockfd;

void myfun(int sig)
{
	char buf[100];
	struct sockaddr_in cliaddr;
	socklen_t len = sizeof(cliaddr);
	bzero(buf, sizeof(buf));
	bzero(&cliaddr, len);

	recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&cliaddr, &len);
	printf("from %s : %s", inet_ntoa(cliaddr.sin_addr), buf);
}

int main(int argc, char *argv[])
{
	//1. 
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);

	//2. 
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr, len);

	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[1]));
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	bind(sockfd, (struct sockaddr *)&srvaddr, len);

	//3.
	signal(SIGIO, myfun);

	//4. 
	fcntl(sockfd, F_SETOWN, getpid());

	//5. 
	int state;
	state = fcntl(sockfd, F_GETFL);
	state |= O_ASYNC;
	fcntl(sockfd, F_SETFL, state);

	//6.
	while (1)
		pause();
}

client

#include "head.h"

int main(int argc,char *argv[])   //  ./Jack 192.168.19.3 50001
{
	//1. Create UDP protocol socket.
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);

	//2. Write directly.
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr,len);

	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[2]));
	inet_pton(AF_INET,argv[1],&srvaddr.sin_addr);

	char buf[100];
	while(1)
	{
		bzero(buf,sizeof(buf));
		fgets(buf,sizeof(buf),stdin);
		sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&srvaddr,len);

		if(strncmp(buf,"quit",4) == 0)
		{
			break;
		}
	}

	//3. Destroy the mailbox
	close(sockfd);

	return 0;
}

 

2, Supermarket acceptance?

1. Why is there timeout control reception?

Generally, we are used to blocking IO. If there is data, it will be read. If there is no data, it will be blocked and waiting all the time.

Therefore, there may be a situation where people have been waiting and can't wait for the result. Five oh one puts forward timeout control.

2. How to realize "supermarket control"?

Method 1: use multiplexing

   select(xx,xx,xx,xx,null); ---- wait indefinitely for data in the collection

--- if there is data, the selext function will return

                                                  

select(xx,xx,xx,xx,5) ; ----- the command will block and wait within 5s. If there is no data, the select function will return 0 after 5s

--------- if data arrives within 5s, the select function will return success and the number of file descriptors

      #include <sys/select.h>
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

How to set the time

struct timeval {
               long    tv_sec; / * seconds * / second
               long    tv_usec; / * microseconds * / subtle
};

struct timeval v;
v.tv_sev =5;
v.tv_usec = 0;

Before each select, in addition to adding all the file descriptors to the collection, you also need to reset the time.

select(maxfd+1,&set,NULL,NULL,&v);

client

#include "head.h"

void *fun(void *arg)
{
	int i;
	for(i=0;i<100;i++)
	{
		printf("%d",i);
		sleep(1);
	}
}

int main(int argc,char *argv[])   //  ./Rose 50002
								  //  . / Rose port number
{
	//0. Create a thread to calculate time.
	pthread_t tid;
	pthread_create(&tid,NULL,fun,NULL);

	//1. Create a TCP socket.
	int sockfd;
	sockfd = socket(AF_INET,SOCK_STREAM,0);   //Sockfd - > socket not connected

	//2. Bind IP address, port number and protocol to TCP socket.
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr,len);

	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[1])); 
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);  

	bind(sockfd,(struct sockaddr *)&srvaddr,len);

	//3. Set the ringing tone.
	//Sockfd - > socket not connected
	listen(sockfd,5);
	//Sockfd - > Listen socket
	
	//4. Sit blocked and wait for the other party's connection. 
	struct sockaddr_in cliaddr;
	bzero(&cliaddr,sizeof(cliaddr));

	int connfd;
	connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
	if(connfd > 0)
	{
		printf("new connection: %s\n",inet_ntoa(cliaddr.sin_addr));
	}

	//5. Add connfd to the collection.
	fd_set set;
	struct timeval v;
	int ret;
	char buf[100];

	while(1)
	{
		FD_ZERO(&set);
		FD_SET(connfd,&set);

		v.tv_sec = 5;
		v.tv_usec = 0;

		ret = select(connfd+1,&set,NULL,NULL,&v);
		if(ret == -1)
			printf("select function failed!\n");

		if(ret == 0)
			printf("Timeout!\n");

		if(ret > 0)  //Data arrives
		{
			bzero(buf,sizeof(buf));
			recv(connfd,buf,sizeof(buf),0);
			printf("from client:%s",buf);
			if(strncmp(buf,"quit",4) == 0)
			{
				break;
			}
		}
	}
	
	//6. Hang up.
	close(connfd);
	close(sockfd);
	 
	return 0;
}

Method 2: set the attribute of the socket itself to timeout reception.

1) What is the mechanism? If you do not set the properties of the socket, you will wait indefinitely when reading the data of the socket. -- > Always blocking if you set the timeout received attribute to the socket, there will be a time limit when reading the data of the socket.

2) Specific steps:

Blocking conditions:

connfd = accept(sockfd); --> Blocking waiting connections.

recv(connfd); --> Block until data arrives in the socket.

Set socket properties:

connfd = accept(sockfd); --> Blocking waiting connections.

Set the timeout receiving attribute for connfd -- > if it is set, the data on connfd will be blocked within the specified time, and it will not be blocked after the time.

recv(connfd); --> Within the specified time - > blocking waiting -- > outside the specified time - > the function returns - 1, that is, when this recv() returns - 1, it represents a timeout.

3) How to set timeout receive attribute to socket? -- > setsockopt() --> man 2 setsockopt

Detailed usage: setsockopt txt

#include <sys/types.h>        
#include <sys/socket.h>

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

Parameters:

sockfd: socket

level: priority

SOL_SOCKET: socket

IPPROTO_IP: IP priority

IPPRO_TCP: TCP priority

optname: Option name
optval: value    (int   -> Enable is 1, not enable is 0)
          (struct timeval -> Need to fill in a time)
optlen: Value type size

Return value:

Success: 0

Failed: - 1

For example:

I want to set the 10s timeout receiving attribute for sockfd. How is the code written?

struct timeval v;

v.tv_sec = 10;

v.tv_usec = 0;

setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&v,sizeof(v));

Next: recv(sockfd)

Blocking waiting within 10s

The recv() function will return - 1 after 10s

Exercise 2: write a TCP server and use method 2 to realize timeout control. If the client has no data within 8s, print a sentence "timeout". If there is data within 8s, print the words said by the client.

client

#include "head.h"

int main(int argc,char *argv[])   //  ./Jack 192.168.19.5 50001
								  //  . / Jack server's IP address and port number
{
	//1. Create TCP protocol socket.
	int sockfd;
	sockfd = socket(AF_INET,SOCK_STREAM,0);

	//2. Call.
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr,len);

	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[2]));
	inet_pton(AF_INET,argv[1],&srvaddr.sin_addr);

	int ret = connect(sockfd,(struct sockaddr *)&srvaddr,len);
	if(ret == -1)
	{
		printf("connect error!\n");
	}

	//3. Continuously send data to the server.
	char buf[100] = {0};
	while(1)
	{
		bzero(buf,sizeof(buf));
		fgets(buf,sizeof(buf),stdin);
		send(sockfd,buf,strlen(buf),0);

		if(strncmp(buf,"quit",4) == 0)
		{
			break;
		}
	}

	//4. Hang up.
	close(sockfd);

	return 0;
}

The server

#include "head.h"

void *fun(void *arg)
{
	int i;
	for (i = 0; i < 100; i++)
	{
		printf("%d\n", i);
		sleep(1);
	}
}

int main(int argc, char *argv[]) //  ./Rose 50002
								 //  . / Rose port number
{
	//0. Create a thread to calculate time.
	pthread_t tid;
	pthread_create(&tid, NULL, fun, NULL);

	//1. Create a TCP socket.
	int sockfd;
	sockfd = socket(AF_INET, SOCK_STREAM, 0); //Sockfd - > socket not connected

	//2. Bind IP address, port number and protocol to TCP socket.
	struct sockaddr_in srvaddr;
	socklen_t len = sizeof(srvaddr);
	bzero(&srvaddr, len);

	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[1]));
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	bind(sockfd, (struct sockaddr *)&srvaddr, len);

	//3. Set the ringing tone.
	//Sockfd - > socket not connected
	listen(sockfd, 5);
	//Sockfd - > Listen socket

	//4. Sit blocked and wait for the other party's connection.
	struct sockaddr_in cliaddr;
	bzero(&cliaddr, sizeof(cliaddr));

	int connfd;
	connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);
	if (connfd > 0)
	{
		printf("new connection: %s\n", inet_ntoa(cliaddr.sin_addr));
	}

	struct timeval v;
	ssize_t ret;
	char buf[100];
	v.tv_sec = 8;
	v.tv_usec = 0;
	setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &v, sizeof(v));
	while (1)
	{
		bzero(buf, sizeof(buf));
		ret = recv(connfd, buf, sizeof(buf), 0);
		printf("from client:%s", buf);
		if (strncmp(buf, "quit", 4) == 0)
		{
			break;
		}
		if (ret == -1)
		{
			printf("Timeout\n");
		}
	}

	//6. Hang up.
	close(connfd);
	close(sockfd);

	return 0;
}