tcp并发设计

4注意:原始代码,如果先关闭服务器端,再次开启服务器的时候会报"connect: Connection refused
"错误,这是因为先关服务器端,导致系统认为客户端仍然在与服务器端连接造成。

可以使用setsockopt

setsockopt函数用于设置套接字选项,可以用来控制套接字的行为和属性。通过setsockopt函数,可以设置套接字的各种选项,如超时设置、缓冲区大小、复用地址、广播等。这些选项可以影响套接字的连接、通信、数据传输等方面的行为,使程序更加灵活、高效地运行。通过设置不同的选项,可以满足不同的网络编程需求。
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

参数说明:
- `sockfd`为目标套接字的文件描述符。
- `level`为选项所在的协议层,一般为`SOL_SOCKET`表示Socket级别选项。
- `optname`为要设置的选项名。
- `optval`为指向包含新选项值的缓冲区的指针。
- `optlen`为新选项值的长度。

成功时返回0,失败时返回-1并设置errno。

(后续文章会仔细讲解)

    int flag=1,len=sizeof(int);
    if(setsockopt(fd,SOL_SOCKET,SO_REUSEADD,&flag,len)==-1){
        perror("setsockopt");
        exit(0);
    }

进程并发设计

并法思路:

一个新生成的文件描述符对应一个客户端,如果多个客户端就需要多个accept函数生成多个文件描述符

注意:给客户端的端口号代表的是服务器的端口号,为了确定是连接的是哪个服务器

accept(fd, (struct sockaddr *)&clint_addr, &addrlen);accept获取的是客户端的端口

1.创建子进程

子进程接收accept返回的新的newfd

while(1){
	newfd = accept(fd, (struct sockaddr *)&clint_addr, &addrlen);
	if(newfd < 0){
		perror("accept");
		exit(0);
	}
		printf("addr:%s port:%d\n",inet_ntoa(clint_addr.sin_addr),ntohs(clint_addr.sin_port));
	if((pid=fork())<0){
		perror("fork");
		exit(0);
	}else if(pid==0){
		close(fd);//此时子进程不用fd,将子进程中fd关掉
		ClientHandle(newfd);//此时子进程打开了一个newfd,父进程也打开了一个newfd,浪费资源,因此将父进程的newfd关掉
		exit(0);
	}else
		close(newfd);//父进程不用newfd,将父进程中newfd关掉
	}
	close(fd);
	return 0;
}
void ClientHandle(int newfd){
	int ret;
	char buf[BUFSIZ]={};

 2.执行子进程内容

void ClientHandle(int newfd){
	int ret;
	char buf[BUFSIZ]={};
	while(1){
	//	memset(buf, 0, BUFSIZ);
		bzero(buf,BUFSIZ);
		ret = read(newfd, buf, BUFSIZ);
		if(ret < 0)
		{
			perror("read");
			exit(0);
		}
		else if(ret == 0)
			break;
		else
			printf("buf = %s\n", buf);
	}
}

 3.回收子进程

通过信号机制
1.初始化signaction函数
	struct sigaction act;
	act.sa_handler=SignHandle;
	act.sa_flags=SA_RESTART;
	sigemptyset(&act.sa_mask);
	sigaction(SIGCHLD,&act,NULL);

注意:

accept: Interrupted system call
当回收进程时,可能会出现"accept: Interrupted system call"错误,是因为使用信号机制时导致某些系统调用被终止了,此时设置sigaction函数结构体中的sa_flags=SA_RESTART,让意外终止的系统调用继续运行。

(可以通过man signaction命令查看)

2.创建SignHandle函数
void SignHandle(int sig){
	if(sig==SIGCHLD){
		printf("client exit\n");
		wait(NULL);
	}
}

 原始代码

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include<strings.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/wait.h>
#define BACKLOG 5
void ClientHandle(int newfd);
void SignHandle(int sig);
void SignHandle(int sig){
	if(sig==SIGCHLD){
		printf("client exit\n");
		wait(NULL);
	}
}
int main(int argc, char *argv[])
{
	int fd, newfd;
	struct sockaddr_in addr,clint_addr;
	socklen_t addrlen;//尽量不使用指针类型,如果指针指向的地址没有存储给定的数据,很可能成为野指针
	addrlen=sizeof(clint_addr);
	pid_t pid;
	struct sigaction act;
	act.sa_handler=SignHandle;
	act.sa_flags=SA_RESTART;
	sigemptyset(&act.sa_mask);
	sigaction(SIGCHLD,&act,NULL);
	
	if(argc < 3){
		fprintf(stderr, "%s<addr><port>\n", argv[0]);
		exit(0);
	}
	fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd < 0){
		perror("socket");
		exit(0);
	}
	addr.sin_family = AF_INET;
	addr.sin_port = htons( atoi(argv[2]) );
	if ( inet_aton(argv[1], &addr.sin_addr) == 0) {
		fprintf(stderr, "Invalid address\n");
		exit(EXIT_FAILURE);
	}

	int flag=1,len=sizeof(int);
	if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&flag,len)==-1){
		perror("setsockopt");
		exit(0);
	}
	if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){
		perror("bind");
		exit(0);
	}

	if(listen(fd, BACKLOG) == -1){
		perror("listen");
		exit(0);
	}
	//此新的文件描述符用于跟客户通讯
	//并法思路:一个新生成的文件描述符对应一个客户端,如果多个客户端就需要多个accept函数生成多个文件描述符
	while(1){
	newfd = accept(fd, (struct sockaddr *)&clint_addr, &addrlen);
	if(newfd < 0){
		perror("accept");
		exit(0);
	}
		printf("addr:%s port:%d\n",inet_ntoa(clint_addr.sin_addr),ntohs(clint_addr.sin_port));
	if((pid=fork())<0){
		perror("fork");
		exit(0);
	}else if(pid==0){
		close(fd);//此时子进程不用fd,将子进程中fd关掉
		ClientHandle(newfd);//此时子进程打开了一个newfd,父进程也打开了一个newfd,浪费资源,因此将父进程的newfd关掉
		exit(0);
	}else
		close(newfd);//父进程不用newfd,将父进程中newfd关掉
	}
	close(fd);
	return 0;
}
void ClientHandle(int newfd){
	int ret;
	char buf[BUFSIZ]={};
	while(1){
	//	memset(buf, 0, BUFSIZ);
		bzero(buf,BUFSIZ);
		ret = read(newfd, buf, BUFSIZ);
		if(ret < 0)
		{
			perror("read");
			exit(0);
		}
		else if(ret == 0)
			break;
		else
			printf("buf = %s\n", buf);
	}
}

线程并发设计

并发思路:通过accept返回的新的文件描述符,传参给子线程

1.创建子线程

	while(1){
		
		newfd = accept(fd, (struct sockaddr *)&clint_addr, &addrlen);
		if(newfd < 0){
			perror("accept");
			exit(0);
		}
		printf("addr:%s port:%d\n", inet_ntoa(clint_addr.sin_addr), ntohs(clint_addr.sin_port) );
		pthread_create(&tid, NULL, ClinetHandle, &newfd);
		pthread_detach(tid);//设置为分离模式,不需要回收线程
	}

 2.执行子线程函数

void *ClinetHandle(void *arg){
	int ret;
	char buf[BUFSIZ] = {};
	int newfd = *(int *)arg;
	while(1){
		//memset(buf, 0, BUFSIZ);
		bzero(buf, BUFSIZ);
		ret = read(newfd, buf, BUFSIZ);
		if(ret < 0)
		{
			perror("read");
			exit(0);
		}
		else if(ret == 0)
			break;
		else
			printf("buf = %s\n", buf);
	}
	printf("client exited\n");
	close(newfd);
	return NULL;
}

 原始代码

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

#define BACKLOG 5

void *ClinetHandle(void *arg);
int main(int argc, char *argv[])
{
	int fd, newfd;
	struct sockaddr_in addr, clint_addr;
	pthread_t tid;
	socklen_t addrlen = sizeof(clint_addr);
	
	if(argc < 3){
		fprintf(stderr, "%s<addr><port>\n", argv[0]);
		exit(0);
	}


	fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd < 0){
		perror("socket");
		exit(0);
	}
	addr.sin_family = AF_INET;
	addr.sin_port = htons( atoi(argv[2]) );
	if ( inet_aton(argv[1], &addr.sin_addr) == 0) {
		fprintf(stderr, "Invalid address\n");
		exit(EXIT_FAILURE);
	}


	int flag=1,len= sizeof (int); 
	if ( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1) { 
		      perror("setsockopt"); 
			        exit(1); 
	} 

	if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){
		perror("bind");
		exit(0);
	}

	if(listen(fd, BACKLOG) == -1){
		perror("listen");
		exit(0);
	}
	while(1){
		
		newfd = accept(fd, (struct sockaddr *)&clint_addr, &addrlen);
		if(newfd < 0){
			perror("accept");
			exit(0);
		}
		printf("addr:%s port:%d\n", inet_ntoa(clint_addr.sin_addr), ntohs(clint_addr.sin_port) );
		pthread_create(&tid, NULL, ClinetHandle, &newfd);
		pthread_detach(tid);//设置为分离模式,不需要回收线程
	}
	close(fd);
	return 0;
}
void *ClinetHandle(void *arg){
	int ret;
	char buf[BUFSIZ] = {};
	int newfd = *(int *)arg;
	while(1){
		//memset(buf, 0, BUFSIZ);
		bzero(buf, BUFSIZ);
		ret = read(newfd, buf, BUFSIZ);
		if(ret < 0)
		{
			perror("read");
			exit(0);
		}
		else if(ret == 0)
			break;
		else
			printf("buf = %s\n", buf);
	}
	printf("client exited\n");
	close(newfd);
	return NULL;
}

注意:

vi排版:按v,上下键选中需要排版的代码,按=即可

相关函数:

`bzero()` 函数用于将指定长度的内存区域清零,即将所有字节初始化为0。它通常用于清空敏感的数据或准备数据结构。`bzero()` 函数在许多系统中已经被废弃,应该使用更现代的函数`memset()` 来替代。其原型如下:

include<strings.h>
void bzero(void *s, size_t n);

其中,参数 `s` 是指向要清零的内存区域的指针,参数 `n` 是要清零的字节数。
man inet_addr可以查看使用in_addr结构体的函数返回值类型

相关推荐

  1. tcp并发设计

    2024-07-09 16:55:08       16 阅读
  2. TCP并发模型

    2024-07-09 16:55:08       35 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-09 16:55:08       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-09 16:55:08       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-09 16:55:08       57 阅读
  4. Python语言-面向对象

    2024-07-09 16:55:08       68 阅读

热门阅读

  1. 云服务器如何隐藏源IP

    2024-07-09 16:55:08       32 阅读
  2. python 将stl数据转为vtk

    2024-07-09 16:55:08       22 阅读
  3. RISC-V的历史与设计理念

    2024-07-09 16:55:08       26 阅读
  4. mysql面试

    2024-07-09 16:55:08       32 阅读
  5. linux程序安装-编译-rpm-yum

    2024-07-09 16:55:08       23 阅读
  6. Haproxy搭建Web群集

    2024-07-09 16:55:08       30 阅读
  7. DNS隧道

    DNS隧道

    2024-07-09 16:55:08      32 阅读
  8. 利用node连接mongodb实现一个小型后端服务系统demo

    2024-07-09 16:55:08       27 阅读
  9. pdfplumber vs PyMuPDF:PDF文本、图像和表格识别的比较

    2024-07-09 16:55:08       31 阅读