TCP/IP网络编程复习(下)

本文主要探讨了在Linux环境下,TCP/IP网络编程的高级主题,包括套接字和标准I/O的优缺点、I/O流分离的实现、epoll优于select的原因及其边缘触发模式,以及多线程服务器端的实现细节。详细阐述了如何利用epoll实现高效的并发服务器,并讨论了线程安全和同步技术的重要性。
摘要由CSDN通过智能技术生成
注:上和中的内容在linux和windows下的实现区别不大,可能有细微差别,当然我实现的都是linux中的内容,windows可以看看这本书或者查查实现,下面开启基于LINUX的编程

第十五章 套接字和标准I/O

  • 标准I/O的优点:1.具有良好的移植性(Portability) 2.可以利用缓冲提高性能
  • 创建套接字时os会提供I/O缓冲,而使用标准I/O函数将得到额外另一种缓冲的支持,所以会先传到I/O函数缓冲再传到套接字缓冲,最后发到网络中,设置缓冲主要是为了提升性能,但套接字缓冲主要是为了实现TCP协议而设置的(如TCP的重传机制),标准I/O函数缓冲的主要目的是为了提升性能
  • 1个字节发10次和10个字节发1次的比较,前者向套接字输出缓冲移动的时间差不多是后者的10倍,而头部的长度也使得前者的数据量要远大于后者的数据量
  • 标准I/O函数的缺点:1.不容易进行双向通信;2.有时可能频繁调用fflush函数;(切换读写状态的时候)3.需要以FILE结构体指针的形式返回文件描述符
FILE * fdopen(int fildes,const char * mode);
成功时返回转换的FILE结构体指针,失败时返回NULL,其中mode为模式信息
int fd=open("data.dat",O_WRONLY|O_CREAT|O_TRUNC);
FILE *fp;
fp=fdopen(fd,"w");
fclose(fp);
调用fclose后,文件描述符也变成毫无意义的整数

int fileno(FILE * stream);
成功时返回转换后的文件描述符,失败返回-1
  • 具体例子见p253,值得注意的是fputs(message,writefp)后要接个fflush(writefp)来立即将数据传输到客户端

第十六章 关于I/O流分离的其他内容

  • 要对fdopen函数调用时生成的FILE指针进行半关闭操作,图片见p259,因为一开始创建读写文件指针的时候只有一个文件描述符(对应着那个套接字),所以要复制一个文件描述符
int dup(int fildes);
int dup2(int fildes,int fildes2);
成功时返回复制的文件描述符,失败-1
fildes为需要复制的文件描述符;fildes2为明确指定的文件描述符整数值

部分代码,具体见p263
readfp=fdopen(clnt_sock,"r");
writefp=fdopen(dup(clnt_sock),"w");

shutdown(fileno(writefp),SHUT_WR);
fclose(writefp);
fclose(readfp);
这才是完整的FILE*下的半关闭,一个文件描述符是可以双向通信的,需要关闭一部分
调用shutdown函数时,无论复制出多少文件描述符,都将进入半关闭状态,同时传递EOF
无论复制出多少文件描述符,均应调用shutdown函数发送EOF并进入半关闭状态
  • !!!它利用这种形式我也是不知道为什么,不能直接shutdown实现半关闭吗,为什么要dup一下呢,这点希望大佬解释一下,或者我以后再看

第十七章 优于select(一般无法接入上百个客户端)的epoll

  • select的缺点:1.调用select函数后常见的对所有文件描述符的循环语句;2.每次调用select函数时都要向该函数传递监视对象信息(影响更大);因为select与文件描述符有关,准确的说是监视套接字变化的函数,套接字由os管理,所有select绝对要借助os才能完成功能,应用程序向os传递数据将对程序造成很大的负担。这一缺点的弥补方式:仅向os传递1次监视对象,监视范围或内容发生变化时只通知发生变化的事项,linux用epoll支持这种方法,而windows用IOCP支持
  • select的优点:1.服务器端接入者少;2.程序应具有兼容性
  • select中为了保存监视对象文件描述符,直接声明了fd_set变量,但epoll下由os负责保存监视对象文件描述符,因此需要向os请求创建保存文件描述符的空间,此时就要使用epoll_create;用epoll_ctl来添加和删除监视对象文件描述符;用epoll_wait函数,通过如下结构体epoll_event将变化的文件描述符单独集中到一起
struct epoll_event
{
   
	__unit32_t events;
	epoll_data_t data;
}
typedef union epoll_data
{
   
	void * ptr;
	int fd;
	__unit32_t u32;
	__unit64_t u64;
}epoll_data_t;
int epoll_create(int size);
成功时返回epoll文件描述符,失败返回-1
size为epoll例程的大小,但他只是提议值,os会参考这个值自己决定(2.6.8之后的内核将完全忽略size,根据情况来调整epoll例程的大小)
而调用epoll_create函数创建的文件描述符保存空间称为“epoll例程”
epoll_create函数创建的资源与套接字相同,也由os管理,因此调用该函数也会返回文件描述符,该函数返回的文件描述符主要用于区分epoll例程,需要终止时,也需要调用close函数
int epoll_ctl(int epfd,int op,int fd,struct epoll_event * event);
成功0失败-1;epfd为例程的文件描述符,op用于指定监视对象的添加,删除,更改等操作,event表示监视对象的事件类型
op:EPOLL_CTL_ADD;EPOLL_CTL_DEL;EPOLL_CTL_MOD(更改注册的文件描述符的关注事件发生情况)
epoll_event结构体用于保存发生事件的文件描述符集合,但也可以在epoll例程中注册文件描述符时,用于注册关注的事件
注册的例子:
struct epoll_event event;
event.events=EPOLLIN;
event.fd=sockfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event);

int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);
成功时返回发生事件的文件描述符数,失败时返回-1
epfd为例程的文件描述符,events为保存发生事件的文件描述符集合的结构体变量值(这个所指缓冲需要动态分配),maxevents为第二个参数可以保存的最大事件数,timeout以1/1000s为单位的等待时间,传递-1时,一直等待直到发生事件
调用例子:
int event_cnt;
struct epoll_event * ep_events;
ep_events=malloc(sizeof(struct epoll_event)*EPOLL_SIZE);
event_cnt=epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);
  • 下面为epoll_event的成员的events变量可以的值
事件类型
EPOLLIN 需要读取数据
EPOLLOUT 输出缓冲为空,可以立即发送数据
EPOLLPRI 收到OOB
EPOLLRDHUP 断开连接或半关闭的情况,在边缘触发方式下非常有用
EPOLLERR 发生错误的情况
EPOLLET 以边缘触发的方式得到事件通知
EPOLLONESHOT 发生第一次事件后,相应文件描述符不再收到事件通知
##epoll服务器端
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/epoll.h>

#define EPOLL_SIZE 50
#define BUF_SIZE 100
void error_handling(char *message);

int main(int argc,char *agrv[])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值