自我总结之Linux网络编程。
前言:
网络:TCP/IP模型:物理层、数据链路层、网络层、传输层网络数据传输:以太网帧、IP报文、TCP/UDP报文
一、进程间通信(IPC):
匿名管道,命名管道,socketpair,信号,信号量,锁,文件锁,共享内存等
原理:在进程外的公共区域申请内存,然后双方通过某种方式去访问公共区域内存分类:
1、小数据量通信:管道,socketpair
2、大数据量通信:共享内存
3、进程间同步:socketpair、管道、锁、文件锁、信号量
(一)匿名管道:
内核buffer大小(ubuntu64k),满后write阻塞,
read时,管道没有数据,阻塞等待。read时,write段已经关闭,管道有数据就读,没有返回0表示结束
1、创建:pipe产生两个文件描述符表示管道两端(读写);
2、读写:read,有数据就读; 无数据写端关闭,返回0; 写端未关闭,阻塞
write:如果管道有空间,写数据,长度以来管道buffer的剩余空间,返回值为写入的长度
如果管道无空间,那么阻塞;
如果read端已关闭,那么产生一个SIGPIPE信号,程序终止。如果程序处理了SIGPIPE,
那么程序不会终止,write返回-1,错误码标记为EPIPE;
3、应用:单工:单方通信; 半双工:两个方向通信,同一时刻只能有一个方向通信; 全双工:同时两方通信
(二)命名管道:
可用于非亲缘关系的进程;
创建:mkfifo打开读端:open(“管道文件名”, O_RDONLY); 如果此时没有其他进程打开写端,那么该open阻塞;
打开写端:open(“管道文件名”, O_WRONLY);
(三)socketpair:
和匿名管道类似,不过是全双工
创建:int fd[2];socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
(四)mmap时间共享内存
有亲缘关系的进程mmap共享:匿名映射;
无亲缘关系的进程mmap共享:内存文件;
使用shm_open打开共享内存文件,以/起头,中间不能有/
(五)文件锁
int fd = open("a");
ret = flock(fd, LOCK_SH|LOCK_NB);
flock(fd, LOCK_UN);
int fd = open("a");
flock(fd, LOCK_SH);
flock(fd, LOCK_UN);
int fd = open("a");
flock(fd, LOCK_EX);
flock(fd, LOCK_UN);
(六)锁:pthread_mutex_init
二、TCP套接字通信(TCP/IP协议):
基于流的面向连接的通信.
流程:服务器:创建socket,绑定地址bind,监听listen; 客户端:创建socket,发送链接请求connect接收连接请求accept 发送数据send
接收数据recv
代码:server:
int main()
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9999);
addr.sin_addr.s_addr = INADDR_ANY;
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
listen(fd, 10);
int newfd = accept(fd, NULL, NULL);
char buf[1024];
recv(newfd, buf, sizeof(buf), 0);
printf("recv data:%s\n", buf);
send(newfd, buf, strlen(buf)+1, 0);
close(newfd);
close(fd);
}
client:
int main()
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9999);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
connect(fd, (struct sockaddr*)addr, sizeof(addr));
send(fd, "hello", 6, 0);
char buf[1024];
recv(fd, buf, sizeof(buf), 0);
return 0;
}
套接字描述符:使用 int shutdown(int sockfd, int how); 关闭一个方向的通信
网络字节序:大端字节序
设置套接字选项:fcntl/
int len = 256;
setsockopt(sockfd, SOL_SOCKET,SO_RCVBUF,&len, sizeof(len));
关闭套接字:close,对端关闭,read/recv返回0,本端写,产生SIGPIPE,若忽略SIGPIPE,那么写返回-1,错误码EPIPE;
三、UDP套接字:基于报文的通信
创建socket: int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
绑定地址:同TCP
发送和接收: sendto, 如果UDP调用了connect,也可以使用send
广播和多播:
关闭socket:close
UDP的数据是基于报文的,客户端调用一次send,产生一个UDP报文,接收一次只能接收一个报文
MTU(1400);
四、通信服务模型:
使用多进程:每个客户端对应一个进程,资源消耗大(早期apache服务器)
使用多线程:每个客户端对应一个线程,资源消耗大(优化过的Apache)
&&多路IO复用技术:iocp(windows)
1、select(跨平台):在少量文件描述符集合中,效率高,只能监听最多1024文件描述符
2、epoll(linux):支持大规模网络服务,只支持linux
使用多路IO复用技术和线程池:两个线程,一个负责epoll_wait和accept,另外一个线程负责接收和处理数据;
支持大规模网络服务,并且支持高并发;
使用多进程并发和多路IO复用技术:多个进程同事监听一个端口,如果外部有链接,多个进程通过内核实现的竞争机制会有一个进程被唤醒
效率非常高,Nginx就是用这种方式
#libevent:
event_base:
evconnlistener:对应一个坚挺的服务器
event
流程:创建一个集合->往集合增加各种事件->给时间一些任务(监听或者读写任务)->给时间设定回调函数(当任务完成时,event可以通过回调函数通知上层)
while(1) //监听event_base,看哪个有事件
code:
void conn_eventcb(struct bufferevent*bev,short events, void * user_data)
{
if(events & BEV_EVENT_EOF)
else if(events & BEV_EVENT_ERROR)
bufferevent_free(bev);
}
void conn_writecb(struct bufferevent*bev, void *user_data)
{
struct evbuffer*output = bufferevent_get_output(bev);
if(evbuffer_get_length(output) == 0) bufferevent_free(bev);
}
void listener_cb(struct evconnlistener*l, evutil_socket_t sock,//文件描述符
struct sockaddr*addr, int socklen, void *ptr))
{
struct event_base*base = ptr;
struct bufferevent*bev = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
if(bev == NULL)exit(0);
buffervnt_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
bufferevent_enble(bev, EV_WRITE);
bufferevent_disable(bev, EV_READ);
bufferevent_write(bev, "hello", 5); //异步IO
}
int main()
{
struct event_base* base = event_base_new();
if(base == NULL)
return 0;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8899);
addr.sin_addr.s_addr = INADDRY_ANY;
struct evconnlistener *listener = evconnlistener_new_bind(base,
listener_cb(callback),(void*)base(callback argument),
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE(opt),
-1, (struct sockaddr*)&addr(listen addr), sizeof(addr));
if(listener == NULL) return 0;
event_base_dispatch(base);
return 0;
}
五、HTTP协议
通信模型:支持客户端/服务器模式,简单快速,灵活,短连接,无状态
HTTP服务器/web服务器:apache,nginx,iis(windows)
客户端:浏览器
curl命令:
HTTP请求:请求行,消息报头,请求正文
HTTP响应:状态行,消息报头,响应正文
&&libcurl:基本流程:初始化CURL环境,创建CURL对象,设置CURL,执行CURL
bool getUrl(char *filename)
{
CURL *curl;
CURLcode res;
FILE* fp;
if((fp = fopen(filename, "w")) == NULL)
return false;
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: Agent-007");
curl = curl_easy_init();
if(curl){
curl_easy_setopt(curl, CURLOPT_PROXY, "10.99.60.201:9999");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, "www.baidu.com");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, fp);
res = curl_easy_perform(curl);
if(res != 0){
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
fclose(fp);
return true;
}
}
ssize_t curl_callback(char* data, int m, int n, void* ptr)
{
// "abc def"
// string str = data; // OK??? NO
string& ret = *(string*)ptr;
string str;
std::copy(data, data+m*n, back_inserter(str));
ret += str;
return m*n;
}
bool postUrl(char *filename)
{
CURL *curl;
char* res = "curl test";
curl = curl_easy_init();
if(curl){
curl_easy_setopt(curl,CURLOPT_POSTFIELDS, res);
curl_easy_setopt(curl, CURLOPT_URL, "www.baidu.com");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ret);
//ssl
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
CURLcode code = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
}