TCP
客户端:bind(addr) connect() send() recv() close()
服务端:fd=socket() bind(addr) listen() clientfd = accept() recv() close()
listen只做迎接客户端的操作,不会影响后续工作(三次握手是在listen时确定的)
UDP
客户端:sendto() recvfrom()
服务端:socket() recvfrom(fd,addr,buffer,length,0) sendto()
一个函数涉及的工作越单一,复用性就越强
recvfrom不仅负责接收数据,还获得了发送方的地址
UDP服务器如何做多个客户端的并发?
1.数据包上加一层协议:前提是收端能够恢复顺序
2.为了真正区分每个客户端,一个fd用来recvfrom,然后用一个新的fd做sendtoA(模拟TCP三次握手)
socket
int sockfd = socket(); 相当于open打开一个文件
socket=插座
fd=(sip dip sport dport protocol) 是网络通信的句柄,文件描述符(IO属性),也可以当作一个客户端
从sigio谈信号
sigio 操作系统收到数据以后,发送sigio信号给服务器的进程
比如:kill -9 1234 操作系统给进程1234发一个信号id(-9)
信号接收 fd-->sigio
UDP每接收到一个包触发一次sigio
TCP处理的时候sigio会很多,如何让os不发送sigio? TCP中采用epoll来代替了sigio
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int sockfd;
void do_sigio(int signo){
char buffer[256] = {0};
struct sockaddr_in cli_addr;
int clilen = sizeof(struct sockaddr_in);
int len = recvfrom(sockfd, buffer, 256, 0, (struct sockaddr*)&cli_addr, &clilen);
printf("Recv Message: %s\r\n", buffer);
sendto(sockfd, buffer, len, 0, (struct sockaddr*)&cli_addr, clilen);
}
int main(){
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
signal(SIGIO, do_sigio); //设置SIGIO信号处理函数
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8888);
serv_addr.sin_addr.s_addr = INADDR_ANY;
// 改便文件描述符性质
fcntl(sockfd, F_SETOWN, getpid()); // 设置用来接受SIGIO信号的进程
int flags = fcntl(sockfd, F_GETFL, 0); //得到文件描述符的状态标志集
flags |= O_ASYNC | O_NONBLOCK; //为状态标志集添加O_ASYNC异步输入属性和非阻塞属性
fcntl(sockfd, F_SETFL, flags);
bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
while(1) sleep(1);
}
对于UDP,只要网卡收到数据就会触发操作系统发送sigio信号,进程执行do_sigio函数进行响应;
在TCP中,sigio会很多,多路复用采用select/poll/epoll进行。
1. 进程内部如何保存信号集合?
Linux中有31种信号(sigint,sigterm,sigio...)
每个信号都有一个int的数,对应了action数组的一个位置
sigaction->action[64]存储了所有信号集合
2. signal如何保存到进程的对应位置?
signal()---->内核调用do_sigaction()函数
比如sigio是29,就会把它存储到action[28]中
3. 进程如何发送信号?
kill -9 1234
对于系统调用sys_kill(int pid, int sig) 函数表示给某个进程pid发信号SIGKILL,不会发生阻塞,直接退出进程
多路IO复用 select/poll/epoll
select(maxfd+1, 可读集合, 可写集合, 有带外数据集合,超时时间); 需要三个数组 maxfd+1:io数量=fd个数
poll(pfd, length, timeout); 定时,在timeout时间内返回
epoll epoll_create(int size) 这里的size没有太大意义 只要大于0就可以
epoll_ctl(); 添加/删除一个io
epoll_wait(epfd, events,length,timeout);