四、网络编程select002

3-7

1、网络基础

2、TCP3次握手-并发

3、TCP状态转换(select-poll)

target:
1:能够描述TCP偷偷内心过程中的主要状态
2、独立使用select实现IO多路转接:(调用select函数,委托内核去帮我们检测有没有客户端连接、select跨平台)
3、理解使用poll实现IO多路转接操作流程(poll只能在linux下使用)
复习:多进程并发服务器

void recyle (int ,num){

	while( waitpid( -1, NULL, wnohang ) > 0 );
	}

int main (){
	// 监听
	int lfd = sock();
	// 绑定
	bind ();
	// 设置监听
	listen ();
	// 信号回收子进程
	struct sigaction act;
	act.sa_handler = 0;
	sigemptyset (& act.sa_mask);
	sinemptyset ( &act.sa_mask );
	sigaction (SIGCHLD, &act, NULL);

	// 父进程
	while( 1){
		int cfd = accept();
		// 创建子进程
		pid_t pid = fork();
		if(pid == 0){  // 子进程
			close(lfd);
			// 通信
			while(1){
				int len = read();
				if( len == -1{
					exit(1);
				}
				else if(len ==0){
					close(cfd);
					break;
				}
				else{
					write();
				}
			}
			// 退出子进程
			return 0;
		}
		else{  // 父进程
			close(cfd);  // 这里回收子进程,不能while循环wait等待,
//因为这样的话,外层循环就退不出去了,所以使用信号中断回收,回收完事之后
//父进程再退到accept函数那里,但是,accept不阻塞等待了,返回-1并设置EINTR==erron,
//所以前边父进程如果是这个EINTR,就再次掉用accept函数
		}
	}
}
				
		
	

多线程并发服务器,
回调函数,需要拿到客服端的文件描述符,客服端的ip,和端口号,所以封装成一个结构体,
每个线程创建之后,都要有一个独立的存储空间


typedef struct sockInfo{
	pthread_t id;
	int fd;
	struct sockaddr_in addr;
} SockInfo;


int main(){
	int lfd = sock(); // 监听
	bind(); // 绑定
	listen(); // 设置监听
	SockInfo sock[256];
	while(1){  // 父进程
		sock[i].fd = accept(lfd,&&sock[i].addr, &len);
		// 创建子线程
		pthread_create(&sock[i].id, NULL, worker, &sock[i]);
		pthread_deatch( sock[i].id);
	}
}

3.3 recv/send


// 接收
ssize_t read( int fd, void * buf, size_t count);
ssize_t recv( int sockfd, void * buf,size_t len, int flags);

// 发送
ssize_t write( int fd, const void * buf,  size_t count);
ssize_t send( int sockfd, const void * buf, size_t len, int flags);


wait等待

第3次和第4次挥手,左端接受第3次挥手完成,发送第4次挥手之后会等待2msl,确保右端接收到了第4次挥手,如果没收到,右边还会接着做出第3次挥手(请求断开连接)

shutdown半关闭

1\专业搞半关闭的
2、close关闭,如果有多个文件描述符,指向同一块内核缓冲区,使用close关闭一个文件描述符,另外的文件描述符依然可以正常读写,而使用shutdown函数,关闭一个文件描述符,其他的文件描述符,会受到同样的限制,
3、dup2函数,后指向前!和反转链表方式一样的记忆策略

8、查看网络的命令

netstat

9、设置端口复用

bind绑定之前,设置端口复用

int flag = 1;
setsockopt( lfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof( flag));
//int ret = bind( lfd, (struct sockaddr *) &server, sizeof(server));

10、IO多路转接select\poll\epoll

委托内核做io检测,程序员专注通信:通过调用select函数让内核去做io检测,通过select函数的返回,知道有多少个客户端有通信,再去遍历。
select\poll
线性表,
只数数,不认字,能知道,在建立连接的“100”个客户端中,知道有‘30’个有通信,但是不知道是谁,需要内核自己遍历检查,

epoll
红黑树
知道哪个快递需要接受,直接处理那“30”个快递就好了,

11、内核大致1如何实现IO转接?

什么是io多路转接技术?
1、先构造一张有关文件描述符的列表,将要监听的文件描述符添加到该列表
2、然后调用一个函数,监听该表中的文件描述符,直到这些描述符表中的一个进行的io操作时,该函数才返回,
* 1 该函数为阻塞函数
* 2、函数对文件描述符的检测操作是由内核完成的

3、在返回时,他告诉进程有多少**(哪些)**描述符要进行I\O操作,
图6、7

还有一个:

函数原型:

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

int nfds 要检测的文件描述符中最大的fd+1 或者写成1024
文件描述符集类型:fd_set rdset; 传入传出参数
文件描述符操作函数

void FD_ZERO( fd_set * set);  // 全部清空
void FD_CLR( int fd, fd_set * set);  // 从集合中删除某一项
void FD_SET( int fd, fd_set * set); // 将某个文件描述符添加到集合
int FD_ISSET( int fd, fd_set * set); // 判断某个文件描述符是否在集合中

12、select工作流程

fd_set reads; // 用户区
调用select之后,内核拷贝一份数据到内核区,然后遍历整个线性表,然后根据表中元素对应的文件描述符,去读写对应的在内核缓冲区的文件,并更新内核区的线性表,然后再拷贝一份到用户区

13、select,一个进程就可以搞定多个客户端的连接。

委托内核做IO检测。异步的
弊端:1、使用数组实现,数组大小1024,能记录的文件描述符个数, 的局限,
reads从用户空间拷贝到内核空间,然后内核修改之后,再从内核空间拷贝到用户空间,开消大,
3、每次调用select时,内核都需要遍历所有的fd,连接增加,效率下降非常快

优点:跨平台

14 poll

使用的是链表
epoll是树,

4、epoll-UDP

5、 广播组播,本地套接字

6、libevent

7、xml,json

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值