https://github.com/sogou/workflow/issues/170
https://www.zhihu.com/column/c_127088776
https://github.com/sogou/workflow
你要搞后端,huh。
多进程协程+多线程开发模型。
tcp/udp/http网络协议。
select/poll/epoll非阻塞io调用。
fp,vfs,fs多少得了解点吧。
unix ipc全都得会。
无缓存的文件流io,grpc,erc。。
再算上redis,mysql,apache,zookeeper,k8s,kafka,docker等等一堆的组件得学。
外门语言py,go,cmake,makefile,sh,lua我这些都不算了,算附带的。
//回调函数callback
#include <stdio.h>
void max(int a,int b)
{
printf("now call max :");
int t = a > b ? a : b;
printf("max number is %d\n",t);
}
void min(int a,int b)
{
printf("now call min:");
int t = a < b ? a : b;
printf("min number is %d\n",t);
}
typedef void (*myfun)(int a,int b);
void callback(myfun fun,int a,int b)
{
fun(a,b);
}
//运算符的重载
/*
除了类属关系运算符"."、成员指针运算符".*"、作用域运算符"::"、sizeof运算符和三目运算符"?:"以外,C++中的所有运算符都可以重载
*/
#include <iostream>
using namespace std;
class coordinate
{
public:
coordinate(int m_x,int m_y)
{
x = m_x;
y = m_y;
}
void operator++();
pubilc:
int x;
int y;
}
void coordinate::operator++()
{
x = x + 1;
y = y + 1;
}
int main()
{
coordinate a(4,5);
++a;
cout << a.x << a.y <<endl;
return 0;
}
(strtok函数)
什么可以直接比较什么不可以直接比较,然后是数组和指针的区别char buf[]和char * buf;
//数组的运算
char *dns_str(char *wanDns_temp,char *wanDns)
{
printf("the num is :%s\n",wanDns_temp);
char *buf = wanDns_temp;
char pos[123] = {};
int i = 0;
while(*buf != ' ')
{
wanDns[i] = *buf;
i++;
buf++;
}
printf("the pos is :%s\n",wanDns);
}
int main()
{
char tempDns[32] = {"5.5.5.5 6.6.6.6"};
char wanDns[32] = {};
dns_str(tempDns,wanDns);
printf("the wanDns is:%s\n",wanDns);
return 0;
}
数组和指针
//网络大端序
unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);
#include <stdio.h>
#include <arpa/inet.h>
int main()
{
unsigned short host_port = 0x1234;
unsigned short net_port;
unsigned long host_addr = 0x12345678;
unsigned long net_addr;
net_port = htons(host_port);
net_addr = htonl(host_addr);
printf("Host ordered port: %#x \n",host_port);
printf("Network ordered port: %#x \n",net_port);
printf("Host ordered address: %#lx \n",host_addr;
printf("Network ordered address: %#lx \n",net_port);
return 0;
}
//点分十进制转换为32位整数型数据
#include <arpa/inet.h>
/*
*成功时返回32位大端序整数型值,失败返回INADDR_NONE
*同时可以检测无效的的IP地址
*/
in_addr_t inet_addr(const char *string);
#include <stdio.h>
#include <arpa/inet.h>
int main()
{
char *addr1 = "1.2.3.4";
char *addr2 = "1.2.3.256";//1个字节能表示的最大整数为255,这里利用错误地址验证inet_addr函数的错误检测能力
unsigened long conv_addr = inet_addr(addr1);
if(conv_addr == INADDR_NONE)
printf("Error occured! \n");
else
printf("Network ordered integer addr:%#1x \n",conv_addr);
conv_addr = inet_addr(addr2);//这里函数的调用出现异常
if(conv_addr == INADDR_NONE)
printf("Error occureded\n");
else
printf("Network ordered interger addr: %#1x \n\n",conv_addr);
return 0;
}
//异步回调
typedef void(*pcb)(int a);
typedef struct parameter{
int a;
pcb callback;
}paraneter;
void* callback_thread(void *p1)
{
parameter *p = (parameter *)p1;
while(1)
{
printf("GetcallBack print! \n");
sleep(3);
p->callback(p->a);
}
}
extern SetCallBackFun(int a,pcb callback)
{
printf("SetCallBackFun printf\n");
parameter *p = malloc(sizeof(parameter));
p->a = 10;
p->callback = callback;
//创建线程
pthread_t thing1;
pthread_create(&thing1,NULL,callback_thread,(viod *)p);
pthread_join(thing1,NULL);
}
void fCallBack(int a)
{
printf("a = %d\n",a);
printf("fCallBack printf !\n");
}
int main()
{
SetCallBackFun(4,fCallBack);
return 0;
}
//callback模型与future模型的比
/*
*实际编程中若要调用inet_addr函数,需将转换后的IP地址信息代**入sockaddr_in结构体中声明的in_addr结构体变量。而inet_aton**函数则不需要此过程。原因在于,若传递in_addr结构体变量地址**值,函数会自动把结构填入该结构体变量。
*
*/
//inet_aton.c
#include <stdio.h>
#include <stdlib.h>
#include <arpa/ient.h>
void error_handling(char *message);
int main(int argc,char *argv[])
{
char *addr = "127.232.124.79";
struct sockaddr_in addr_inet;
if(!inet_aton(addr,&addr_inet.sin_addr))
error_handling("Conversion error");
else
printf("Network ordered integer addr : %#x \n",
addr_inet.sin_addr.s_addr);
return 0;
}
/*
*Makefile如何定义一个变量为空格
*
*/
//#号前有多少个空格,space就被定义为多少空格
empty:=
space:=$(empty) #
//两个$(empty)之间有多少个空格,space就被定义为多少空格
empty:=
space:=$(empty) $(empty)
/*
*查找字符串函数:$(findstring <find> <in>)
*/
/*
*char *inet_ntoa(struct in_addr adr);
*/
/*
*该函数将通过参数传入的整数型IP地址转换为字符串格式并返回
*返回值为char指针,返回字符串地址意味着字符串已保存到内存空*间,但该函数未向程序员要求分配内存,而是在内部申请了内存并*保存了字符串。也就是说,调用完该函数后,应立即将字符串信息*复制到其他内存空间。因为,若在次调用inet_ntoa函数,则有可**能覆盖之前保存的字符串信息。
*
*/
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
struct sockaddr_in addr1,addr2;
char *str_ptr;
char str_arr[20];
addr1.sin_addr.s_addr = htonl(0x1020304);
addr2.sin_addr.s_addr = htonl(0x1010101);
str_ptr = inet_ntoa(addr1.sin_addr);
strcpy(str_arr,str_ptr);
printf("Dotted-Decimal notationl: %s \n",str_ptr);
inet_ntoa(addr2.sin_addr);
printf("Dotted-Decimal notation2 : %s \n",str_ptr);
printf("Dotted-Decimal notationl3: %s \n",str_arr);
return 0;
}
/*
服务器网络地址初始化
memset函数将每个字节初始化为同一值:第一个参数为结构体变量adr的地址,即初始化对象addr;第二个参数为0,因此初始化为0;最后一个参数传入addr的长度,因此addr的所有字节均初始化为0.这样的目的是为了将sockaddr_in结构体中的成员sin_zero初始化为0.
*/
struct sockaddr_in addr;
char * serv_ip = "217.0.0.0"
char * serv_port = "9190"
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(serv_ip);
addr.sin_port = htons(atoi(serv_port));
/*
服务器端的准备工作通过bind函数完成,而客户端则通过connect函数完成。因此,函数调用前需要准备的地址类型也不同。服务器端声明sockaddr_in结构体变量,将其初始化为赋予服务器端IP和套接字的端口号,然后调用bind函数;而客户端则声明sockaddr_in结构体,并初始化为要与之连接的服务器套接字的IP和端口号,然后调用connect函数
*/
/*
INADDR_ANY
利用常数INADDR_ANY分配服务器的IP地址。若采用这种方式,则可以自动获取运行服务器端的计算机地址。
初始化服务器端套接字时应分配所属计算机的IP地址,因为初始化时使用的IP地址非常明确,那为什么需要进行IP初始化呢?如前所诉,同一计算机中可以分配多个IP地址,实际IP地址的个数和计算机中安装的NIC的数量相等。即使是服务端套接字,也需要决定接受那个IP传来的数据。因此,服务器端套接字初始化过程中要求IP地址。如果只有一个NIC,则直接使用INADDR_ANY。
*/
/*
向套接字分配网络地址
*/
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
/*
WSAStringToAddress & WSAAddressToString
两个在Windows下独有的函数,功能和inet_ntoa和inet_addr完全相同
*/
#include <winsock2.h>
/*
ip层
IP本身是面向消息的、不可靠的协议。每次传输数据时会帮我们选择路径,但并不一致。如果传输中发生路径错误,则选择其他路径;但如果发生数据丢失或错误,则无法解决。IP协议无法应对数据错误
*/
/*
*TCP原理
TCP 套接字的数据收发无边界。服务器端即使调用1次write函数传输40字节的数据,客户端也有可能通过4次read函数调用每次读取10字节。但此处有一些疑问,服务器端一次传输了40字节,而客户端居然可以缓慢的分批接收。客户端在接收10字节后,剩下的30字节在何处等候?
实际上,write函数调用后并非立即传输数据,read函数调用后也并非马上接收数据。更准确的说,write函数调用瞬间,数据将移至输出缓冲;read函数调用瞬间,从输入缓冲读取数据。
*/
/*
调用write函数时,数据将移至输出缓冲,在适当的时候(不管是分别传送还是一次性传送)传送对方的输入缓冲。这时对方将调用read函数从输入缓冲读取数据。这些I\O缓冲特性可以整理如下
I\O缓冲在每个TCP套接字中单独存在
I\O缓冲在创建套接字时自动生成
即使关闭套接字也会继续传递输出缓(write)冲中遗留的数据
关闭套接字将丢失输入缓冲中的数据
TCP的滑动窗口可以确保数据不会在缓冲溢出而丢死数据
*/
/*
https://www.jianshu.com/p/57690226c934
TCP内部工作原理1:与对方套接字的连接
1、与对方套接字建立连接
2、与对方套接字进行数据交换
3、断开与对方套接字的连接
*/
/*
如果只考虑可靠性,
*/
/*udp核心函数
#inlcude <sys/socket.h>
ssize_t sendto(int sock,void *buff,size_t nbytes,int flags,struct sockaddr *to,socklen_t addrlen)
ssize_t recvfrom(int sock,void *buff,size_t nbytes,int flags,struct sockaddr *from,socklen_t *addrlen)
*/
/*
sigaction
struct sigaction
{
void (*sa_handler)(int)
sigset_t sa_mask;
int sa_flags;
}
*/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void timeout(int sig)
{
if(sig == SIGALRM)
puts("Time out!");
alarm(2);
}
int main(int argc,char *argv[])
{
int i;
struct sigaction act;
act.sa_handler = timeout;
sigemptyset(&act.sa_mask);
act.sa_flag = 0;
sigaction(SIGALRM,&act,0);
alarm(2);
for(i = 0;i < 3;i++)
{
puts("wait ...");
sleep(100);
}
return 0;
}
/*
利用信号处理技术消灭僵尸进程
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
void read_childproc(int sig)
{
int status;
pid_t id = waitpid(-1.&status,WNOHANG);
if(WIFEXITED(status))
{
printf("Removed proc id:\n",id);
printf("Child send :%d \n",WEXISTATUS(status));
}
}
int main()
{
pid_t pid;
struct sigaction act;
act.sa_handler = read_childproc;
sigemptyset(&act.sa_mask);
&act.sa_flags = 0;
sigaction(SIGCHLD,&act,0);
pid = fork();
if(pid == 0)
{
puts("Hi ! i am child process");
sleep(10);
return 12;
}else{
printf("Child proc id :%d \n",pid);
pid = fork();
if(pid == 0)
{
puts("Hi!i am child process");
sleep(10);
exit(24);
}else{
int i;
printf("Child proc id: %d \n",pid);
for(i = 0;i < 5;i++)
{
puts("wait ...");
sleep(5);
}
}
}
return 0;
}
/*
多进程回声服务器
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
void read_childproc(int sig);
int main()
{
int serv_sock,clnt_sock;
struct sockaddr_in serv_adr,clnt_adr;
pid_t pid;
struct sigaction act;
socklen_t adr_sz;
itn str_len,state;
char buf[BUF_SIZE];
if(argc != 2){
printf("Usage : %s <port>\n",argv[0]);
exit(1);
}
act.sa_handler = read_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
state = sigaction(SIGCHLD,&act,0);
serv_sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_adr,0,sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1)
error_handling("bind() error");
if(listen(serv_sock,5) == -1)
error_handling("listen (0 error");
while(1)
{
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&adr_sz);
if(clnt_sock == -1)
continue;
else
puts("new client conneted...");
pid = fork();
if(pid == -1)
{
close(clnt_sock);
continue;
}
if(pid == 0)
{
close(serv_sock);//关闭子进程中服务器端的套接字
while((str_len = read(clnt_sock,buf,BUF_SIZE)) != 0)
write(clnt_sock,buf,str_len);
close(clnt_sock);
puts("client disconnetced...");
return 0;
}else{
close(clnt_sock);//关闭父进程中客户端的套接字
}
close(serv_sock);
return 0;
}
}
void read_childproc(int sig)
{
pid_t pid;
int status;
pid = waitpid(-1,&status,WNOHANG);
printf("remove proc id :%d \n",pid);
}
/*
*1个套接字中存在2个文件描述符时,只有2个文件描述符都终止(销毁)后,才能销毁*套接字。即使子进程销毁了客户端连接的套接字文件描述符,也无法完全销毁套接字**,因此,调用fork函数后,要将无关紧要的套接字文件描述符关掉。
*/
/*
*管道并非属于进程的资源,而是和套接字一样,属于操作系统(也就不是fork函数的复制对象),所以,两个进程通过操作系统提供的内存空间进行通信
*/
/*
*客户端分割TCP的I/O程序
*/
/*
*进程间通信
*/
/*
*管道pipe
*int pipe(int filedes[2]);
*filedes[0]:管道出口
*filedes[1]:管道入口
*/
#include <stdio.h>
#include <unistd.h>
#define BUF_SIZE 30
int main(int argc,char *argv[])
{
int fds[2];
char str[] = "Who are you?";
char buf[BUF_SIZE];
pid_t pid;
pid(fds);
pid = fork();
if(pid == 0)
{
write(fds[1],str,sizeof(str));
}else{
read(fds[0],buf,BUF_SIZE);
puts(buf);
}
return 0;
}
/*
*利用进程保存消息的回声服务器
*/
int main(int argc,char *argv[])
{
int serv_sock,clnt_sock;
struct sockaddr_in serv_adr,clnt_adr;
int fds[2];
pid_t pid;
struct sigaction act;
socklen_t adr_sz;
int str_len,state;
char buf[BUF_SIZE];
if(argc!=2){
printf("Usage : %s <port>\n,argv[0]");
exit(1);
}
act.sa_handler = read_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
state = sigaction(SIGCHLD,&act,0);
serv_sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_adr,0,sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1)
error_handling("bind()error");
if(listen(serv_sock,5) == -1)
error_handling("listen()error");
pipe(fds);
pid = fork();
if(pid == 0)
{
FILE *fp = fopen("echomsg.txt","wt");
char msgbuf[BUF_SIZE];
int i,len;
for(i = 0;i < 10;i++)
{
len = read(fds[0],msgbuf,BUF_SIZE);
fwrite((void*)msgbuf,1,len,fp);
}
fclose(fp);
return 0;
}
while(1)
{
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&adr_sz);
if(clnt_sock == -1)
continue;
else
puts("new clinet connect...");
pid = fork();
if(pid == 0)
{
close(serv_sock);
while((str_len = read(clnt_sock,buf,BUF_SIZE))!=0)
{
write(clnt_sock,buf,str_len);//回声
write(fds[1],buf,str_len);
}
close(clnt_sock);
puts("clinet disconnetced...");
return 0;
}else
close(clnt_sock);
}
close(serv_sock);
return 0;
}
/*
*I/O复用:select函数
*为了构建并发服务器,只要客户端连接请求就会创建新进程。这的确是实际操作中采用的一种方案,但是因为创建进程需要大量的运算和内存空间,由于每个进程都具有独立的内存空间,所以相互间的数据交换也要求采用相对复杂的方法(IPC属于相对复杂的方法)。
IO复用能在不创建进程的同时向多个客户端提供服务。
*/
fd_set数组是以位为单位进行操作的,使用相关宏进行操作置位
FD_ZERO(fd_set *fdset);
FD_SET(int fd,fd_set *fdset);
FD_CLR(int fd,fd_set *fdset);
FD_ISSET(int fd,fd_set *fdset);
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)
/*
*select 函数调用示例
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#define BUF_SIZE 30;
int main()
{
fd_set reads,temps;
int result,str_len;
char buf[BUF_SIZE];
struct timeval timeout;
FD_ZERO(&reads);
FD_SET(0,&reads);//0 is standed input(console)
/*
timeout.tv_sec = 5;
timeout.tv_usec = 5000;
*/
while(1)
{
temps = reads;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
result = select(1,&temp,0,0,&timeout);
if(result == -1)
{
puts("select () error!");
}else if(result == 0){
puts("time out !");
}else{
if(FD_ISSET(0,&temps))
{
str_len = read(0,buf,BUF_SIZE);
buf[str_len] = 0;
printf("message form console ; %s\n",buf);
}
}
}
return 0;
}
/*实现IO复用服务端*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#define BUD_SIZE 100
void error_handling(char *buf);
int main()
{
int serv_sock,clnt_sock;
struct sockaddr_in serv_adr,clnt_adr;
struct timeval timeout;
fd_set reads,cpy_reads;
socklen_t adr_sz;
int fd_max,str_len,fd_num,i;
char buf[BUF_SIZE];
if(argc != 2){
printf("Usage : %s <port>\n,argv[0]");
exit(1);
}
serv_sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_adr,0,sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1)
error_handling("bind()error");
if(listen(serv_sock,5) == -1)
error_handling("listen()error");
FD_ZERO(&reads);
FD_SET(serv_sock,&reads);
fd_max = serv_sock;
while(1)
{
cpy_reads = reads;
timeout.tv_sec = 5;
timeout.tv_usec = 5000;
if((fd_num = select(fd_max+1,&cpy_reads,0,0,&timeout)) ==-1)
break;
if(fd_num == 0)
continue;
for(i = 0;i < fd_max+1;i++)
{
if(FD_ISSET(i,&cpy_reads))
{
if(i == serv_sock)
{
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&adr_sz);
FD_SET(clnt_sock,&read);
if(fd_max < clnt_sock)
fd_max = clnt_sock;
printf("connected client : %d\n",clnt_sock);
}else{
//read message
str_len = read(i,buf,BUF_SIZE);
if(str_len == 0) //close request
{
FD_CLR(i,&read);
close(i);
printf("close client : %d \n",i);
}else{
write(i,buf,str_len);//echo
}
}
}
}
}
close(serv_sock);
return 0;
}
/*
*多种IO函数
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#define BUF_SIZE 30
void error_handling(char *message);
void urg_handler(int signo);
int acpt_sock;
int recv_sock;
int main(int argc,char *argv[])
{
struct sockaddr_in recv_adr,serv_adr;
int str_len,state;
socklen_t serv_adr_sz;
struct sigaction act;
char buf[BUF_SIZE];
if(argc != 2)
{
printf("Usage : %s <port>\n,argv[0]");
exit(1);
}
act.sa_handler = urg_handler;
sigemptyset(&act.sa_mask);
act.sa_flag = 0;
acpt_sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_adr,0,sizeof(recv_adr));
recv_adr.sin_family = AF_INET;
recv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
recv_adr.sin_port = htons(atoi(argv[1]));
if(bind(acpt_sock,()))
}
/*TTL设置*/
/*
*程序中的TTL的设置。程序中的TTL设置是通过套接字的可选项完成的。与设置TTL相关的协议为IPPROTO_IP,选项名为IP_MULTICAST_TTL.因此用如下代码把TTL设置为64
*/
int send_sock;
int time_live = 64;
send_sock = socket(PF_INET,SOCK_STREAM,0);
setsockopt(send_sock,IPPROTO_IP,IP_MULTICAST_TTL,(void *)&time_live,sizeof(time_live));
IO流的分离
/*
*复制文件描述符后“流”的分离
*/
无论复制出多少文件描述符,均应调用shutdown函数发送EOF并进入半关闭状态。
/*
*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 BUF_SIZE 100
#define EPOLL_SIZE 50
void error_handling(char *buf)
int main(int argc,char argv[])
{
int serv_sock,clnt_sock;
struct sockaddr_in serv_adr,clnt_adr;
socklen_t adr_sz;
int str_len,i;
char buf[BUF_SIZE];
struct epoll_event *ep_events;
struct epoll_event event;
itn epfd,event_cnt;
if(argc != 2){
printf("Usage : %s <port>\n",argv[0]);
exit(1);
}
serv_sock = socket(AF_INET,SOCK_STREAM,0);
memset(&serv_adr,0,sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1)
error_handling("bind() error");
if(listen(serv_sock,5) == 1)
error_handling("listen () error");
epfd = epoll_create(EPOLL_SIZE);
ep_events = malloc(sizeof(struct epoll_events)*EPOLL_SIZE);
event.events = EPOLLIN;
event.data.fd = serv_sock;
epoll_ctl(epfd,EPOLL_CTL_ADD,serv_sock,&event);
while(1)
{
event_cnt = epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);
if(event_cnt == -1)
{
puts("epoll_wait()error");
break;
}
for(i=0;i<event_cnt;i++)
{
if(ep_events[i].data.fd == serv_sock)
{
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&adr_sz);
event.events = EPOLLIN;
event.data.fd = clnt_sock;
epoll_ctl(epfd,EPOLL_CTL_ADD,clnt_sock,&event);
printf("connected client : %d \n",clnt_sock);
}else{
str_len = read(ep_events[i].data.fd,buf,BUF_SIZE);
if(str_len == 0)//close requests
{
epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
close(ep_events[i].data.fd);
printf("close client:%d\n",ep_events[i].data.fd);
}else{
write(ep_events[i].data.fd,buf,str_len);//echo
}
}
}
}
close(serv_sock);
close(epfd);
return 0;
}
/*
条件触发和边缘触发
条件触发方式中,只要输入缓冲有数据就会一直通知该事件
服务器端输入缓冲收到50字节的数据时,服务器端操作系统将通知该事件(注册到发生变化的文件描述符中)。但服务器端读取20字节后还剩30字节的情况下,仍会注册事件。也就是说,条件触发方式下,只要输入0缓冲中还剩下数据,就将以事件的方式在次注册。
边缘触发的事件特性:边缘触发中输入缓冲收到数据时仅注册1次改该事件。即使输入缓冲中还留有数据,也不会在进行注册。
epoll默认以条件触发的方式工作
*/
//条件触发
#include <>
#define BUF_SIZE 4
#define EPOLL_SIZE 50
int main()
{
int serv_sock,clnt_sock;
struct sockaddr_in serv_adr,clnt_adr;
socklen_t adr_sz;
int str_len,i;
char buf[BUF_SIZE];
struct epoll_event *ep_events;
struct epoll_event event;
int epfd,event_cnt;
if(argc != 2)
{
printf("Usage : %s <port>\n ",argv[0]);
exit(1);
}
serv_sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_adr,0,sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock,(strcut sockaddr*)&serv_adr,sizeof(serv_adr)) == -1)
error_handling("bind()error");
if(listen(send_sock,5) == -1)
error_handling("listen()error");
epfd = epoll_create(EPOLL_SIZE);
ep_events = malloc(sizeof(struct epoll_event)*EPOLL_SIZE);
event.events = EPOLLIN;
event.data.fd = serv_adr;
epoll_ctl(epfd,EPOLL_CTL_ADD,serv_sock,&event);
while(1)
{
event_cnt = epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);
if(event_cnt == -1)
{
puts("epoll_wait()error");
break;
}
puts("return epoll_wait");
for(i = 0;i < event_cnt;i++)
{
if(ep_events[i].data.fd == serv_sock)
{
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&adr_sz);
event.events = EPOLLIN;
event.data.fd = clnt_sock;
epoll_ctl(epfd,EPOLL_CTL_ADD,clnt_sock,&event);
printf("conneted client : %d\n",clnt_sock);
}else{
str_len = read(ep_events[i].data.fd,buf,BUF_SIZE);
if(str_len == 0) //close request
{
epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
close(ep_events[i].data.fd);
printf("close client : %d \n",ep_events[i].data.fd);
}else{
write(ep_events[i].data.fd,buf,str_len);
}
}
}
}
}
//边缘触发:
event.events = EPOLLIN|EPOLLET;
//select模型是以条件触发的方式工作的,输入缓冲中如果还剩有数据,肯定会注册事件。
边缘触发的服务器端实现中必知的两点:
1、通过errno变量验证错误的原因
2、为了完成非阻塞(Non-blocking)I/O,更改套接字特性
int fcntl(int filedes,int cmd,...)
int flag = fcntl(fd,F_GETFL,0);
fcntl(fd,F_SETFL,flag|O_NONBLOCk);
通过第一条语句获取之前的设置的属性信息,通过第二条语句在此基础上添加非阻塞O_NONBLOCK标志。调用read&write函数时,无论是否存在数据,都会形成非阻塞文件(套接字)。fcntl函数的适用范围很广。
边缘触发方式下,以阻塞方式工作的read&write函数有可能引起服务器端的长时间停顿。因此,边缘触发方式中一定要采取非阻塞read&wrote函数。
示例:
#include <fcntl.h>
#include <erron.h>
#define BUF_SIZE 4
#define EPOLL_SIZE 50
void setnonblockingmode(int fd);
void error-handling(char *buf);
int main(int argc,char *argv[])
{
int serv_sock,clnt_sock;
struct sockaddr_in serv_adr,clnt_adr;
socklen_t adr_sz;
int str_len,i;
char buf[BUF_SIZE];
struct epoll_event *ep_events;
struct epoll_event event;
int epfd,event_cnt;
if(argc!=2)
{
printf("Usage : %s <port>\n",argv[0]);
exit(1);
}
serv_sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_adr,0,sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1)
error_handling("bind()error");
if(listen(serv_sock,5) ==-1)
error_handling("listen () error");
epfd = epoll_create(EPOLL_SIZE);
ep_events = malloc(sizeof(struct epoll_event)*EPOLL_SIZE);
setnonblockingmode(serv_sock);
event.events = EPOLLIN;
event.data.fd = serv_sock;
epoll_ctl(epfd,EPOLL_CTL_ADD,serv_sock,&event);
while(1)
{
event_cnt = epoll_wait(epfd,ep_events,EPOLL_SIZE,-1);
if(event_cnt == -1)
{
puts("epoll_wait()error");
break;
}
puts("return epoll_wait");
for(i=0;i < event_cnt;i++)
{
if(event_cnt == serv_sock)
{
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&adr_zs);
event.events = EPOLLIN | EPOLLET;
event.data.fd = clnt_sock;
epoll_ctl(epfd,EPOLL_CTL_ADD,clnt_sock,&event);
printf("connect client ; %d \n",clnt_sock);
}else{
while(1)
{
str_len=read(ep_events[i].data.fd,buf,BUF_SIZE);
if(str_len == 0){//close request???
epoll_ctl(epfd,EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);
printf("close client : %d\n",ep_events[i].data.fd);
break;
}else if(str_len < 0){
if(errno == EAGAIN)
break;
}else{
write(ep_events[i].data.fd,buf,str_len);
}
}
}
}
}
close(serv_sock);close(epfd);
return 0;
}
void setnonblockingmode(int fd)
{
int flag = fcntl(fd,F_GETFL,0);
fcntl(fd,F_SETFL,flag|O_NONBLOCK);
}
/*
*linux的线程学习
*/
每个进程的内存空间都由保存全局变量的数据区、向malloc等函数的动态分配提供空间的堆、函数运行时使用的栈构成。每个进程都拥有这种独立空间。为了得到多条代码执行流而复制整个内存区域的负担太重。如果只需要以获得多个代码执行流为主要目的,则只需要分离栈区域。通过这种方式可以的好处是:
1、上下文切换时不需要切换数据区和堆
2、可以利用数据区和堆交换数据
实际上这就是线程。线程为了保持多条代码执行流而隔离开了栈区域。
多个线程将共享数据区和堆,为了保持这种结构,线程将在进程内创建并运行。
进程:在操作系统构成单执行流的单位
线程:在进程构成单执行流的单位
线程的创建及运行
#incude <pthread.h>
int pthread_create(
pthread_t * restrict thread,const pthread_attr_t *restrict attr,
void *(* start_routine)(void *),void *restrict arg
);
#include <stdio.h>
#include <pthread.h>
void * thread_main(void *arg)
int main(int argc,char *argv[])
{
pthread_t t_id;
int thread_param = 5;
if(pthread_create(&t_id,NULL,thread_main,(void*)&thread_param)!=0)
{
puts("pthread_create()error");
return -1;
};
sleep(10);puts("end of main");
return 0;
}
void *thread_main(void *arg)
{
int i;
int cnt = *((int *)arg);
for(i = 0;i < cnt;i++)
{
sleep(1);puts("running thread");
}
return NULL;
}
/*
*调用该函数的进程将进入等待状态,直到一个参数为ID的线程终止为止。而且可以得到线程的main函数返回值。
*
*/
int pthread_join(pthread_t thread,void **status);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *thread_main(void *arg);
int main(int argc,char *argv[])
{
pthread_t t_id;
int thread_param = 5;
void * thr_ret;
if(pthread_create(&t_id,NULL,thread_main,(void*)&thread_param)!=0)
{
puts("pthread_create()error");
return -1;
};
if(pthread_join(t_id,&thr_ret)!=0)
{
puts("pthread_join()error");
return -1;
}
printf("Thread return message : %s \n",(char*)thr_ret);
free(thr_ret);
return 0;
}
void *thread_main(void *arg)
{
int i;
int cnt=*((int *)arg);
char *msg = (char *)malloc(sizeof(char)*50);
strcpy(msg,"hello,iam thread~\n");
for(i = 0;i < cnt;i++)
{
sleep(1);puts("runing thread");
}
return (void*)msg;
}
/*线程返回值的方法,注意返回值是threa_main函数内部动态分配的内存空间地址值*/
根据临界区是否引起问题,函数可分为
1、线程安全函数
2、非线程安全函数
/*
*工作线程模式
*/
#include <stdio.h>
#include <pthread.h>
void * thread_summation(void *arg);
int sum = 0;
int main(int argc,char *argv[])
{
pthrea_t id_t1,id_t2;
itn range1[] = {1,5};
int range2[] = {6,10};
pthread_create(&id_t1,NULL,thread_summation,(void *)range1);
pthread_create(&id_t2,NULL,thread_sunmation,(void *)range2);
pthread_join(id_t1,NULL);
pthread_join(id_t2,NULL);
printf("result :%d\n",sum);
return 0;
}
void *thread_summation(void *arg)
{
int start = ((int*)arg)[0];
int end = ((int *)arg)[1];
while(start<=end)
{
sum+=start;
start++;
}
return NULL;
}
互斥量
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t * mutex);
利用互斥量锁住或释放临界区时使用的函数
int pthread_mutex_lock(pthread_mutex_t * mutex)
int pthread_mutex_unlock(pthread_mutex_t * mutex)
/**
多线程并发服务器端的实现
**/
#include <"">
#define BUF_SIZE 100
#define MAX_CLNT 256
void *handle_clnt(void *arg);
void send_msg(char *msg,int len);
void error_handling(char *msg);
int clnt_cnt = 0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutx;
int main(int argc,char &argv[])
{
int serv_sock,clnt_sock;
struct sockaddr_in serv_adr,clnt_adr;
int clnt_adr_sz;
pthread_t t_id;
if(argc != 2)
{
printf("Usage : %s<port>\n",argv[0]);
exit(1);
}
pthread_mutex_init(&mutx,NULL);
serv_sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_sock,0,sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1)
error_handling("bind()error");
if(listen(serv_sock,5) ==-1)
error_handling("listen() error");
while(1)
{
clnt_adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&clnt_adr_sz);
pthread_mutex_lock(&mutx);
clnt_socks[clnt_cnt++] = clnt_sock;
pthread_mutex_unlock(&mutx);
pthread_create(&t_id,NULL,handle_clnt,(void*)&clnt_sock);
pthread_detach(t_id);
printf("Connect client IP : %s \n",inet_ntoa(clnt_adr.sin_addr));
close(serv_sock);
return 0;
}
}
void *handle_clnt(void *arg)
{
int clnt_sock = *((int *)arg);
int srt_len = 0,i;
char msg[BUF_SIZE];
while((str_len = read(clnt_sock,msg,sizeof(msg)) != 0)
send_msg(msg,str_len);
pthread_mutex_lock(&mutx);
for(i=0;i<clnt_cnt;i++)//remove disconnected client
{
if(clnt_sock == clnt_sock[i])
{
while(i++<clnt_cnt-1)
clnt_socks[i] = clnt_socks[i+1];
break;
}
}
clnt_cnt--;
pthread_mutex_unlock(&mutx);
close(clnt_sock);
return NULL;
}
void send_msg(char *msg,int len) // send to all
{
int i;
pthread_mutex_lock(&mutx);
for(i=0;i<clnt_cnt;i++)
write(clnt_sock[i],msg,len);
pthread_mutex_unlock(&mutx);
}
void error_handling(char *msg)
{
}
/*client_c*/
#include <>
#define BUF_SIZE 100
#define NAME_SIZE 20
void *send_msg(void *arg)
void *recv_msg(void *arg)
void error_handling(char *msg)
char name[NAME_SIZE] = "[DEFAULT]";
char msg[BUF_SIZE];
int main(int argc,char *argv[])
{
int sock;
strcut sockaddr_in serv_addr;
pthread_t snd_thread,rcv_thread;
void *thread_return;
if(argc != 4){
printf("Usage : %s <IP><port><name>\n",argv[0]);
exit(1);
}
sprintf(name,"[%s]",argv[3]);
sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) == -1)
error_handling("connect()error");
pthread_create(&snd_thread,NULL,send_msg,(void*)&sock);
pthread_create(&rcv_thread,NULL,recv_msg,(void*)&sock);
pthread_jion(snd_thread,&thread_return);
pthread_jion(rev_thread,&thread_return);
close(sock);
return 0;
}
void *send_msg(void *arg) //send thread main
{
int sock = *((int*)arg);
char name_msg[NAME_SIZE+BUF_SIZE];
while(1)
{
fgets(msg,BUF_SIZE,stdin);
if(!strcmp(msg,"q\n")||!strcmp(msg,"Q\n"))
{
close(sock);
exit(1);
}
sprintf(name_msg,"%s %s",name,msg);
write(sock,name_msg,strlen(name_msg));
}
return NULL;
}
void *recv_msg(void *arg) //read thread main
{
int sock = *((int *)arg);
char name_msg[NAME_SIZE+BUF_SIZE];
int str_len;
while(1)
{
str_len = read(sock,name_msg,NAME_SIZE+BUF_SIZE-1);
if(str_len == -1)
return (void*)-1;
name_msg[str_len] = 0;
fputs(name_msg,stdout);
}
return NULL;
}
void error_handling(char *msg)
{
}
/*************************************************************/
/****************************实现udp的可靠传输***************/
/*server端*/
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netistd.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <stdarg.h>
#include <string.h>
#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
/*包头*/
typedef struct
{
int id;
int buf_size;
}PackInfo
/*接受包*/
struct SendPack
{
PackInfo head;
char buf[BUFFER_SIZE];
}data;
int main()
{
/*发送Id*/
int send_id = 0;
/*接收ID*/
int receive_id = 0;
/*创建UDP套接口*/
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(atoi(SERVER_PORT));
/*创建socket*/
int server_socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(server_socket_fd == -1)
{
perror("Create Socket Failed:");
exit(1);
}
/*绑定套接口*/
if(-1 == (bind(server_socket_fd,(struct sockaddr*)&server_addr,sizeof(server_addr))))
{
perror("Server Bind Failed:");
exit(1);
}
/*数据传输*/
while(1)
{
/*定义一个地址,用于捕获客户端地址*/
struct sockaddr_in client_addr;
socklen_t client_addr_length = sizeof(client_addr);
/*接受数据*/
char buffer[BUFFER_SIZE];
bzero(buffer,BUFFER_SIZE);
if(recvfrom(server_socket_fd,buffer,BUFFER_SIZE,0,(struct sockaddr*)&client_addr,sizeof(client_addr)))
{
perror("Receive Data Failed:");
exit(1);
}
/*从buffer中拷出file_name*/
char file_name[FILE_NAME_MAX_SIZE+1];
bzero(file_name,FILE_NAME_MAX_SIZE+1);
strncpy(file_name,buffer,strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));
printf();
}
}
/************************************************************/
/***********************linux hhtp***************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>
#define BUF_SIZE 1024
#deiine SMALL_BUF 100
void* request_handler(void* arg);
void send_data(FILE *fp,char* ct,char* file_name);
char* content_type(char *file);
void send_error(FILE *fp);
void error_handling(char* message);
int main(int argv,char *argv[])
{
int serv_sock,clnt_sock;
struct sockaddr_in serv_addr,clnt_adr;
int clnt_adr_size;
char buf[BUF_SIZE];
pthread_t t_id;
if(argc != 2)
{
printf("Usage : %s<port>\n",argv[0]);
exit(1);
}
serv_sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_adr,0,sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock,(struct sockaddr*)serv_adr,sizeof(serv_adr)) ==-1)
error_handling("bind() error");
if(listen(serv_sock,20) ==-1)
error_handling("listen() error");
while(1)
{
clnt_adr_size = sizeof(clnt_adr);
clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&clnt_adr_size);
printf("Connection Requset : %s:%d\n",inet_ntoa(clnt_adr.sin_addr),ntohs(clnt_adr.sin_port));
pthread_create(&t_id,NULL,request_handler,&clnt_sock);
pthread_detach(t_id);
}
close(serv_sock);
return 0;
}
void *request_handler(void *arg)
{
int clnt_sock = *((int *)arg);
char req_line[SMALL_BUF];
FILE *clnt_read;
FILE *clnt_write;
char method[10];
char ct[15];
char file_name[30];
clnt_read = fdopen(clnt_sock,"r");
clnt_write = fdopen(dup(clnt_sock),"w");
fgets(reg_line,SMALL_BUF,clnt_read);
if(strstr(req_line,"HTTP/") == NULL)
{
send_error(clnt_write);
fclose(clnt_read);
fclose(clnt_write);
return ;
}
fclose(clnt_send);
send_data(clnt_write,ct,file_name);
}
void send_data(FILE *fp,char *ct,char *file_name)
{
char protocol[] = "HTTP/1.0 200 OK\r\n";
char server[] = "Server:Linux Web Server \r\n";
char cnt_len[] = "Content-length : 2048\r\n";
char cnt_type[SMALL_BUF];
char buf[BUF_SIZE];
FILE *send_file;
sprintf(cnt_type,"Content-type:%s\r\n\r\n",ct);
send_file = fopen(file_name,"r");
if(send_file == NULL)
{
send_error(fp);
return ;
}
/*传输头信息*/
fputs(protocol,fp);
fputs(server,fp);
fputs(cnt_len,fp);
fputs(cnt_type,fp);
/*传输请求数据*/
while(fgets(buf,BUF_SIZE,send_file) != NULL)
{
fputs(buf,fp);
fflush(fp);
}
fflush(fp);
fclose(fp);
}
char* content_type(char* file)
{
char extension[SMALL_BUF];
char file_name[SMALL_BUF];
strcpy(filename,file);
strtok(file_name,".");
strcpy(extension,strtok(NULL,"."));
if(!strcmp(extension,"html") || !strcmp(extension,"htm"))
return "text/html";
else
return "text/plain";
}
void send_error(FILE *fp)
{
char protocol[] = "HTTP/1.0 400 Bad Requset\r\n";
char server[] = "Server:Linux Web Server \r\n";
char cnt_type[] = "Content-type:text/html\r\n\r\n";
char content[] = "<html><head><title>NETWORK</title></head>"
"<body><fontsize=+5><br>发生错误!查看请求文件名和请求方式!"
"</font></body></html>