Internet-->Linux网络编程基础
来源:互联网 发布:java如何实现线程 编辑:程序博客网 时间:2024/06/06 05:29
网络编程预备知识:
主机间通讯所需要的必要步骤:
1、OS(opertion system)
2、IP地址(在互联网中唯一标识一台主机)
3、OS上分配给进程的端口(如:8080)
4、网络传输协议、网络芯片驱动(底层驱动)、传输层统一字节序
OSI七层网络模型 TCP/IP四层网络模型
应用层
端口分配 表示层
会话层 应用层
误差控制 传输层 传输层
网间通讯 网络层 网络层
数据链路层
网卡驱动 物理层 物理层
TCP/IP协议簇:很多协议构成网络协议,相对较为重要的有几个:TCP(传输控制)、IP(网间)、UDP(数据包协议)、IGMP(组管理)、ICMP(控制信息协议);
网络中常用的协议是TCP,UDP也经常使用,TCP相对于UDP多了三次握手四次挥手(链接指定主机必要步骤,提供可靠依据),UDP则不会,因此UDP也称为不可靠传输。
关于网络字节序:
由于数据传输需要在不同的硬件之间,而不同硬件平台所使用的架构不尽相同,如:X86架构使用的是小端序,RSIC则使用大端序(默认是小端序,除非设置一下)。故网络传输需要统一数据存储方式,来完成不同平台的通讯。
Linux中,内核提供了对网络的统一操作接口,也称网络I/O操作。
socket操作网络I/O,特殊的文件描述符(就是利用套接字文件和Linux内置各层协议进行数据缓冲而后实现接收发送(类似于文件读取))。
服务单进程情况下,最多可以连接1021个客户端,在多进程或者多线程的情况下,可以达到多客户端同时处理的效果,但是由于进程和线程都参与系统调度,过多会极大降低系统的响应速度,耗费系统资源(调度器缓存、临时文件读取查找等各种骚操作)。
基于TCP/IP的客户端、服务端模型如下:
服务端:socket–>bind–>listen–>accept–>send/recv–>close(每个流程都是函数名)
客户端:socket–>connect–>send/recv/close(同上)
以上只是基本的流程,并且要注意的是,在服务端的运行过程中,会产生两个临时的套接字文件,虽然看不到,但是也应该清楚一个文件是用于连接的,等到连接了后,这个文件还在,如果没有继续扫描文件动态的话,服务器就进入收发数据阶段,一般情况的测试代码都是让服务器和客户端在while(1)这个死循环中持续地收发数据,因此基本不会执行到close那个函数。
基于UDP/IP协议的服务端、客户端模型如下:
服务端:socket–>bind–>sendto/recvfrom–>close
客户端:socket–>bind–>sendto/recvfrom–>close
对比TCP/IP和UDP/IP模型,其中的三次握手四次挥手被忽略,因此UDP被称为不可靠连接,不过TCP/IP的严格传输要求服务器需要占用资源实时响应,加大服务器端的负载,因此在实际的使用中,还是两个一同使用,混合编程取其长处避其短处。
多路客户端连接
一个服务端连接多个客户端进行同时服务,实现方法大致如下:
1、创建子进程;
2、创建线程;
3、多路I/O复用。(select)
#include <stdio.h>#include <stdlib.h>#include <string.h>//system includes#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#define BUF_SIZE 200int main(int argc,char *argv[]){ struct sockaddr_in server_online;//用于连接服务 //创建套接字 int server_temp=socket(AF_INET,SOCK_STREAM,0); if(-1 == server_temp) { printf("套接字创建失败!!\r\n"); return -1; } memset(&server_online,0,sizeof(struct sockaddr_in)); server_online.sin_family=AF_INET; server_online.sin_port = htons(8080); server_online.sin_addr.s_addr = inet_addr("0.0.0.0"); int result=bind(server_temp,(struct sockaddr *)&server_online,sizeof(struct sockaddr)); if(-1 == result) { printf("绑定失败!!\r\n"); return -1; } result = listen(server_temp,10); if(-1 == result) { printf("监听创建失败!!\r\n"); return -1; } printf("正在扫描端口!!\r\n"); struct sockaddr_in temp; char buf[BUF_SIZE]=""; socklen_t lenth=sizeof(struct sockaddr_in); while(1) { memset(&temp,0,sizeof(struct sockaddr_in)); int ret=accept(server_temp,(struct sockaddr *)&temp,&lenth);//创建收发数据用的文件描述符 if(-1 == result) { printf("连接错误,未能与客户端建立连接!!\r\n"); return -1; } printf("客户端连接!!\r\n"); if(0 == fork()) { while(1) { memset(buf,0,BUF_SIZE); result=recv(ret,buf,BUF_SIZE,0); if(-1 == result) { printf("Receive Error!!!\r\n"); continue; } printf("Receive Message %d:%s\r\n",strlen(buf),buf); result=send(ret,buf,BUF_SIZE,0); if(-1 == result) { printf("Send Message Error!!\r\n"); continue; } } exit(0); } close(ret); } return 0;}
客户端代码
#include <stdio#include <stdlib.h>#include <string.h>//system includes#include<netinet/in.h>#include<sys/types.h>#include<sys/socket.h>#define BUF_SIZE 200int main(int argc,char *argv[]){int client_temp=socket(AF_INET,SOCK_STREAM,0);char buf[BUF_SIZE]="";if(-1 == client_temp){printf("创建套接字失败!!\r\n");return -1;}struct sockaddr_in server_online;memset(&server_online,0,sizeof(struct sockaddr_in));server_online.sin_family=AF_INET;server_online.sin_port=htons(8080);server_online.sin_addr.s_addr=inet_addr("127.0.0.1");//配置连接服务器参数int result=connect(client_temp,(struct sockaddr *)&server_online,sizeof(struct sockaddr));//连接服务器if(-1 == result){printf("连接失败,请重试!!\r\n");return -1;}while(1){memset(buf,0,BUF_SIZE);printf("Your Message:\r\n");gets(buf);result = send(client_temp,buf,BUF_SIZE,0);if(-1 == result){printf("未发送,请重试!!\r\n");continue;}memset(buf,0,BUF_SIZE);result = recv(client_temp,buf,BUF_SIZE,0);if(-1 == result){printf("接收失败,请重试!!\r\n");continue;}printf("Receive Message %d:%s\r\n",strlen(buf),buf);}return 0;}
组播和广播
组播和广播都是利用网络中的特殊IP地址来进行的,其编程涉及到IP地址的规定:
规定中说明了IP地址分为五大类:
A、B、C、D、E(也不知道为什么这么草率地分类,起码有个名字好记点)
每个类中都有其区段(数字范围)
A:1.0.0.1~126.255.255.254
B:128.0.0.1~191.255.255.254
C:192.0.0.1~233.255.255.254
D:组播
E:保留
组播和广播分别向以下网段发送信息就可以实现:
广播:局域网:192.255.255.255
组播:224.0.0.1~239.255.255.254
unix域套接字
用于本地通讯,可以实现前后台进程交换数据:
流式:
服务端:socket–>listen–>accept–>recv/send
客户端:socket–>connect–>recv/send
数据报:
客户端/服务端:socket–>bind–>recvfrom/sendto
epoll高容量服务器模型
这个是Linux内核的优势,是由系统提供的大型服务器解决方案,是select的升级版本,解除select受描述符集合范围的限制。
epoll主要的操作函数有:文件–>sys/epoll.h
1、创建一个epoll的文件描述符:int epoll_create(int size);
2、注册epoll到系统:int epoll _ ctl (int epfd, int op
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <netinet/in.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/epoll.h>#include <unistd.h>#include <sys/types.h>#define IPADDRESS "127.0.0.1"#define PORT 8787#define MAXSIZE 1024#define LISTENQ 5#define FDSIZE 1000#define EPOLLEVENTS 100//函数声明//创建套接字并进行绑定static int socket_bind(const char* ip,int port);//IO多路复用epollstatic void do_epoll(int listenfd);//事件处理函数static voidhandle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf);//处理接收到的连接static void handle_accpet(int epollfd,int listenfd);//读处理static void do_read(int epollfd,int fd,char *buf);//写处理static void do_write(int epollfd,int fd,char *buf);//添加事件static void add_event(int epollfd,int fd,int state);//修改事件static void modify_event(int epollfd,int fd,int state);//删除事件static void delete_event(int epollfd,int fd,int state);int main(int argc,char *argv[]){ int listenfd; listenfd = socket_bind(IPADDRESS,PORT); listen(listenfd,LISTENQ); do_epoll(listenfd); return 0;}static int socket_bind(const char* ip,int port){ int listenfd; struct sockaddr_in servaddr; listenfd = socket(AF_INET,SOCK_STREAM,0); if (listenfd == -1) { perror("socket error:"); exit(1); } bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET,ip,&servaddr.sin_addr); servaddr.sin_port = htons(port); if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1) { perror("bind error: "); exit(1); } return listenfd;}static void do_epoll(int listenfd){ int epollfd; struct epoll_event events[EPOLLEVENTS]; int ret; char buf[MAXSIZE]; memset(buf,0,MAXSIZE); //创建一个描述符 epollfd = epoll_create(FDSIZE); //添加监听描述符事件 add_event(epollfd,listenfd,EPOLLIN); for ( ; ; ) { //获取已经准备好的描述符事件 ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1); handle_events(epollfd,events,ret,listenfd,buf); } close(epollfd);}static voidhandle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf){ int i; int fd; //进行选好遍历 for (i = 0;i < num;i++) { fd = events[i].data.fd; //根据描述符的类型和事件类型进行处理 if ((fd == listenfd) &&(events[i].events & EPOLLIN)) handle_accpet(epollfd,listenfd); else if (events[i].events & EPOLLIN) do_read(epollfd,fd,buf); else if (events[i].events & EPOLLOUT) do_write(epollfd,fd,buf); }}static void handle_accpet(int epollfd,int listenfd){ int clifd; struct sockaddr_in cliaddr; socklen_t cliaddrlen; clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen); if (clifd == -1) perror("accpet error:"); else { printf("accept a new client: %s:%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port); //添加一个客户描述符和事件 add_event(epollfd,clifd,EPOLLIN); }}static void do_read(int epollfd,int fd,char *buf){ int nread; nread = read(fd,buf,MAXSIZE); if (nread == -1) { perror("read error:"); close(fd); delete_event(epollfd,fd,EPOLLIN); } else if (nread == 0) { fprintf(stderr,"client close.\n"); close(fd); delete_event(epollfd,fd,EPOLLIN); } else { printf("read message is : %s",buf); //修改描述符对应的事件,由读改为写 modify_event(epollfd,fd,EPOLLOUT); }}static void do_write(int epollfd,int fd,char *buf){ int nwrite; nwrite = write(fd,buf,strlen(buf)); if (nwrite == -1) { perror("write error:"); close(fd); delete_event(epollfd,fd,EPOLLOUT); } else modify_event(epollfd,fd,EPOLLIN); memset(buf,0,MAXSIZE);}static void add_event(int epollfd,int fd,int state){ struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);}static void delete_event(int epollfd,int fd,int state){ struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);}static void modify_event(int epollfd,int fd,int state){ struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);}
客户端代码模型:
#include <netinet/in.h>#include <sys/socket.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/epoll.h>#include <time.h>#include <unistd.h>#include <sys/types.h>#include <arpa/inet.h>#define MAXSIZE 1024#define IPADDRESS "127.0.0.1"#define SERV_PORT 8787#define FDSIZE 1024#define EPOLLEVENTS 20static void handle_connection(int sockfd);static voidhandle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf);static void do_read(int epollfd,int fd,int sockfd,char *buf);static void do_read(int epollfd,int fd,int sockfd,char *buf);static void do_write(int epollfd,int fd,int sockfd,char *buf);static void add_event(int epollfd,int fd,int state);static void delete_event(int epollfd,int fd,int state);static void modify_event(int epollfd,int fd,int state);int main(int argc,char *argv[]){ int sockfd; struct sockaddr_in servaddr; sockfd = socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr); connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)); //处理连接 handle_connection(sockfd); close(sockfd); return 0;}static void handle_connection(int sockfd){ int epollfd; struct epoll_event events[EPOLLEVENTS]; char buf[MAXSIZE]; int ret; epollfd = epoll_create(FDSIZE); add_event(epollfd,STDIN_FILENO,EPOLLIN); for ( ; ; ) { ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1); handle_events(epollfd,events,ret,sockfd,buf); } close(epollfd);}static voidhandle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char *buf){ int fd; int i; for (i = 0;i < num;i++) { fd = events[i].data.fd; if (events[i].events & EPOLLIN) do_read(epollfd,fd,sockfd,buf); else if (events[i].events & EPOLLOUT) do_write(epollfd,fd,sockfd,buf); }}static void do_read(int epollfd,int fd,int sockfd,char *buf){ int nread; nread = read(fd,buf,MAXSIZE); if (nread == -1) { perror("read error:"); close(fd); } else if (nread == 0) { fprintf(stderr,"server close.\n"); close(fd); } else { if (fd == STDIN_FILENO) add_event(epollfd,sockfd,EPOLLOUT); else { delete_event(epollfd,sockfd,EPOLLIN); add_event(epollfd,STDOUT_FILENO,EPOLLOUT); } }}static void do_write(int epollfd,int fd,int sockfd,char *buf){ int nwrite; nwrite = write(fd,buf,strlen(buf)); if (nwrite == -1) { perror("write error:"); close(fd); } else { if (fd == STDOUT_FILENO) delete_event(epollfd,fd,EPOLLOUT); else modify_event(epollfd,fd,EPOLLIN); } memset(buf,0,MAXSIZE);}static void add_event(int epollfd,int fd,int state){ struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);}static void delete_event(int epollfd,int fd,int state){ struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);}static void modify_event(int epollfd,int fd,int state){ struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);}
代码源参考自:http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html
- Internet-->Linux网络编程基础
- (笔记)Internet网络基础
- linux网络编程基础
- Linux 网络编程基础
- Linux网络编程基础
- Linux网络编程基础
- linux 网络编程基础
- Linux网络编程基础
- linux网络编程基础
- linux网络编程基础
- Linux网络编程基础
- Linux网络编程基础
- Linux网络编程基础
- Linux 网络编程基础
- Linux 网络编程基础
- linux网络编程基础
- linux网络编程基础
- Linux网络编程基础
- 织梦首页怎么调用问答模块(ask)问题
- Linux内核中的kobject和kset介绍
- 测试系列-测试管理之我见
- Ubuntu -- 下如何查看CPU信息, 包括位数和多核信息
- 1133. Splitting A Linked List (25)
- Internet-->Linux网络编程基础
- Nginx+Tomcat负载均衡集群总结
- 字符串全排列
- 将npm换成淘宝镜像
- springboot的condition为什么能获取到properties文件的内容
- Spark 监控后台:javax.servlet.http.HttpServletRequest.isAsyncStarted()Z
- LeetCode 34. Search for a Range
- 论进程和线程的不同
- Dll注入经典方法完整版