常见协议头部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);
}
} 


京公网安备 11010502036488号