常见协议头部c++结构体
同步,异步,阻塞,非阻塞的概念
TIME_WAIT问题的原理
bind()一个刚刚断开的tcp端口失败了,显示端口状态是TIME_WAIT的原因
http服务器架构学习
recvfrom和sendto
与recv和send类似,多出来的参数用于保存struct sockaddr
recvfrom 默认阻塞,读取的字节数可设为>=实际收到的字节数,可以将目标地址保存在struct sockaddr
sendto可以将目标地址设置在struct sockaddr中
recvmsg和sendmsg
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); ssizt_t sendmsg(int sockfd, struct msghdr *msg, int flags); struct msghdr { void *msg_name; /* protocol address */ socklen_t msg_namelen; /* sieze of protocol address */ struct iovec *msg_iov; /* scatter/gather array */ int msg_iovlen; /* # elements in msg_iov */ void *msg_control; /* ancillary data ( cmsghdr struct) */ socklen_t msg_conntrollen; /* length of ancillary data */ int msg_flags; /* flags returned by recvmsg() */ } struct iovec { void *iov_base; /* starting address of buffer */ size_t iov_len; /* size of buffer */ } struct cmsghdr { socklen_t cmsg_len; /* length in bytes, including this structure */ int cmsg_level; /* originating protocol */ int cmsg_type; /* protocol-specific type */ /* followed by unsigned char cmsg_data[] */ }iovec以(iov_base,iov_len)的形式指定msg_iovlen个分散的缓冲区,适合自定义网络协议字段。
两个成员分别是套接字地址及其大小,类似recvfrom和sendto的第二和第三个参数。
对于已连接套接字,则可直接将两个参数设置为NULL。
对于recvmsg,msg_name是一个值-结果参数,会返回发送端的套接字地址。
msg_iov和msg_iovlen两个成员用于指定数据缓冲区数组,即iovec结构数组。
参考:https://blog.csdn.net/u014209688/article/details/71311973
msg_iov和msg_iovlen两个成员用于指定数据缓冲区数组,即iovec结构数组。
参考:https://blog.csdn.net/u014209688/article/details/71311973
epoll
入门
epoll服务器思路
内部原理
特殊性质
如果对一个socket的文件描述符调用close()函数,使其引用计数为0的时候,epoll会自动删除该文件描述符
epoll_ctl的封装
int epfd=epoll_create1(0); //省去了struct epoll_event赋值的操作 int add_event(int epfd,int fd,int mask) { struct epoll_event ev={0}; ev.events=mask; ev.data.fd=fd; return epoll_ctl(rt,EPOLL_CTL_ADD,fd,&ev); } int del_event(int rt,int fd) { return epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL); } int mod_event(int rt,int fd,int mask) { struct epoll_event ev={0}; ev.events=mask; ev.data.fd=fd; return epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev); }
服务端针对socket的read
如果客户端已经关闭了scoket,服务端的read不管有没有读满指定的字节数,都会返回
但服务器read一个长连接的socket时,如果读不满指定的字节就会阻塞,一种解决方法如下
char buf[BUF_SZ]; int remote_sock_fd=socket(); ...... int nread; ioctl(remote_sock_fd,FIONREAD,&nread); if(!nread) { /* 对方连接关闭 */ } else while(nread) { int ndone=min(BUF_SZ,nread); read(remote_sock_fd,buf,ndone); nread-=ndone; }
struct sockaddr_in的封装(tcp服务端)
int init_local_fd(char *ip,int port)//指定ip和端口 {//省去了struct sockaddr_in的赋值,直接得到本地监听的文件描述符 local_fd=socket(AF_INET, SOCK_STREAM, 0); socklen_t sz_of_sockaddr=(socklen_t)sizeof(struct sockaddr); struct sockaddr_in local_adr={0}; local_adr.sin_family=AF_INET; local_adr.sin_port=htons(port); local_adr.sin_addr.s_addr=inet_addr(ip); bind(local_fd,(struct sockaddr*)&local_adr,sz_of_sockaddr); return local_fd;//返回绑定了地址的文件描述符 } //bind本质就是把一个本地地址绑定到一个fd上,之后对本地地址的操作就不涉及struct sockaddr_in了
struct sockaddr_in的封装(tcp客户端)
int init_remtote_fd(char *ip,int port) { int remtote_fd=socket(AF_INET,SOCK_STREAM,0);//获取一个fd struct sockaddr_in remote_adr={0};//填写服务器地址 remote_adr.sin_family=AF_INET; remote_adr.sin_addr.s_addr=inet_addr(ip); remote_adr.sin_port=htons(port); int ret=connect(fd,(struct sockaddr*)&remote_adr,sizeof(remote_adr));//连接服务器 if(ret<0)perror("connect"); return remtote_fd;//返回服务器fd } //connect本质就是把一个远程地址绑定到一个fd上,之后对远程地址的操作就不涉及struct sockaddr_in了
struct sockaddr_in的封装(udp服务端)
int init_local_fd(char *ip,int port)//指定ip和端口 {//省去了struct sockaddr_in的赋值,直接得到本地监听的文件描述符 int local_fd=socket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in local_adr={0}; local_adr.sin_family=AF_INET; local_adr.sin_port=htons(port); local_adr.sin_addr.s_addr=inet_addr(ip); bind(local_fd,(struct sockaddr*)&local_adr,sz_of_sockaddr); if(local_fd<0)perror("bind()"); return local_fd;//返回绑定了地址的文件描述符 }
void recv_and_send(char *ip,int port)//接受客户端的消息,再发回去 { int local_fd=init_local_fd(ip,port); int ret; struct sockaddr_in remote_adr; printf("local_fd %d\n",local_fd); while(1) { ret=recvfrom(local_fd,buf,sizeof(buf)-1,0,(struct sockaddr*)&remote_adr,&sz_of_sockaddr); if(ret<0){perror("recvfrom");return;} else { buf[ret]=0; puts(buf); } sendto(local_fd,buf,ret,0,(struct sockaddr*)&remote_adr,sz_of_sockaddr); } }
struct sockaddr_in的封装(udp服务端)
socklen_t sz_of_sockaddr=(socklen_t)sizeof(struct sockaddr); char buf[1024]="hello\n"; int init_local_fd(char *ip,int port) { int local_fd=socket(AF_INET,SOCK_DGRAM,0);//获取一个fd struct sockaddr_in remote_adr={0};//填写服务器地址 remote_adr.sin_family=AF_INET; remote_adr.sin_addr.s_addr=inet_addr(ip); remote_adr.sin_port=htons(port); int ret; ret=sendto(local_fd,buf,strlen(buf),0,(struct sockaddr*)&remote_adr,sz_of_sockaddr); if(ret<0)perror("sendto()"); //先向服务器发一条初始消息 return local_fd; }
void recv_and_send(char *ip,int port)//服务端ip,port { int local_fd=init_local_fd(ip,port); int ret; struct sockaddr_in remote_adr; printf("local_fd %d\n",local_fd); while(1) {//接受服务端消息 ret=recvfrom(local_fd,buf,sizeof(buf)-1,0,(struct sockaddr*)&remote_adr,&sz_of_sockaddr); if(ret<0){perror("recvfrom");return;} puts(buf); //发送自己的数据 scanf("%s",buf);//scanf输入 int len=strlen(buf); buf[len]='\n'; buf[++len]=0; sendto(local_fd,buf,len,0,(struct sockaddr*)&remote_adr,sz_of_sockaddr); } }