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

原创粉丝点击