Internet-->Linux网络编程基础

本文介绍了网络编程的基本概念,包括主机间通讯所需的步骤、TCP/IP与UDP/IP的区别、网络字节序等。并给出了基于TCP/IP协议的服务端和客户端编程示例,以及如何通过多路I/O复用提高服务器处理能力。
摘要由CSDN通过智能技术生成

网络编程预备知识:
主机间通讯所需要的必要步骤:
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 200

int 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 200

int 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多路复用epoll
static void do_epoll(int listenfd);
//事件处理函数
static void
handle_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 void
handle_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 20

static void handle_connection(int sockfd);
static void
handle_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 void
handle_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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值