Linux网络编程

Linux网络编程

TCP/IP与OSI

网络互联促成了TCP/IP协议的产生:
  • TCP协议分成两个不同的协议:
    • 用来检测网络传输差错的传输控制协议TCP
    • 专门负责对不同网络进行互联的互联网协议IP
  • 从此,TCP/IP协议产生。
网络体系结构:
  • 网络采用分而治之的方法设计,将网络的功能划分成不同的模块,以分层的形式有机组合在一起。
  • 每层实现不同的功能,其内部实现方法对外部其他层次来说都是透明的。每层向上层提供服务。同时使用下层提供的服务。
  • 网络体系结构即指网络的层次结构和每层所使用协议的集合。
  • 两类非常重要的体系结构:OSI与TCP/IP
OSI模型:
  • 应用层
  • 表示层
  • 会话层
  • 传输层
  • 网络层
  • 数据链路层
  • 物理层
TCP模型:
  • 应用层(FTP、HTTP、DNS、SMTP)----应用部分----
  • 传输层(TCP、UDP)------Linux内核部分-----
  • 网络层(IP、ICMP、IGMP)
  • 网络接口和物理层。(以太网、令牌环网、FDDI等)

socket编程基础

流式套接字(SOCK_STREAM):唯一对应着TCP
  • 提供了一个面向连接、可靠的数据传输服务、数据无差错、无重复的发送和接收。内设置流量控制。
数据包套接字(SOCK_DGRAM):唯一对应着UDP
  • 提供无连接服务,数据包以独立数据包的形式发送,不提供无差错保证。数据可能会丢失重复,顺序发送,但可能会乱序接收。
原始套接字(SOCK_RAW):对应着多个协议,发送穿透了传输层
  • 可以对较低层次协议,如IP、ICMP直接访问。

IP地址

IP地址是Internet中主机标志。
  • IP地址分为IPV4和IPV6。IPV4采用32位的整数表示。IPV6采用128位整数表示。
  • mobileIPV6:local IP(本地注册IP)、roam IP(漫游IP)
IPV4地址
  • 点分形式:192.168.0.246
  • 32位整数
特殊的IP地址
  • 局域网的IP:192.XXX.XXX.XXX、10.xxx。xxx。xxx
  • 广播IP:XXX.XXX.XXX.255 255.255.255.255(全网广播)
  • 组播IP:224.xxx.xxx.xxx ~ 239.xxx.xxx.xxx

端口号

  • 为了区分一台主机接收到的数据包应该转交给哪个任务来进行处理,使用端口号来进行区分。
  • TCP端口号和UDP端口号是独立的。
  • 端口号一般有LANA
  • 保留端口:1~1023(FTP:23 SSH:22 HTTP:80 HTTPS:469)

TCP编程

tcp编程API:

  • socket();//建立socket套接字
  • bind();//套接字绑定端口
  • listen();//监听端口
  • accept();//接收客户端链接
  • connect();//与服务端建立socket链接
  • read();//读取套接字内容,会阻塞
  • write();//向socket套接字中写内容。
  • close();//关闭socket链接。
TCP并发服务器:
  • TCP循环服务器:
socket();
bind();
listen();
while(1){
    accept();
    process();
    close();
}
  • TCP 多进程并发服务器:
socket();
bind();
listen();
while(1){
    accept();
    if(fork() == 0){
    process();
    close();
    exit();
    }
    close();
}
  • TCP多线程并发服务器:
socket();
bind(();
listen();
while(1){
    accept();
    if(pthread_create() != -1){
        process();
        close();
        exit();
    }
    close();
}

UDP网络编程

API
  • send()//类似write()
  • recv()//类似read()
服务端
  • socket()
  • bind()//绑定IP、端口
  • recvfrom()//阻塞等待接收消息
  • sendto()//发送消息
  • close()
客户端
  • socket()
  • sendto()//发送消息
    • sendto(int socket, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
    • dest_addr–发送目标的sock信息。
    • addrlen–发送目标的sock结构体长度。
  • recvfrom()//接收消息
    • recvfrom(int sockfd, void *buf, size_t, len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
    • src_addr–发送端的socket信息。
  • close()//关闭socket套接字

udp-server:

/*************************************************************************
	> File Name: udp_demo.c
	> Author: 
	> Mail: 
	> Created Time: 2019年05月25日 星期六 07时04分23秒
 ************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<strings.h>
#include<string.h>
#include<arpa/inet.h>

#define BUFSIZE 128
#define SERV_PORT 8888
#define QUIT_STR "quit"

int main(){
    //create socket
    int fd = -1;
    fd = socket(AF_INET,SOCK_DGRAM, 0);
    if(fd < 0){
        perror("socket fail\n");
        return -1;
    }
    int b_reuse = 1;
    //bind setopt
    //setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));
    //bind
    struct sockaddr_in sin;
    bzero(&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SERV_PORT);
    sin.sin_addr.s_addr = inet_addr("127.0.0.1");
    int ret = bind(fd, (struct sockaddr *)&sin, sizeof(sin));
    if(ret < 0){
        perror("bind fail\n");
        return -1;
    }
    char buf[BUFSIZE];
    struct sockaddr_in client_sin;
    socklen_t addrlen = sizeof(client_sin);
    char ipv4_addr[16];
    while(1){
        bzero(buf, BUFSIZE);
        bzero(ipv4_addr, sizeof(ipv4_addr));
        printf("recv msg waiting...\n");
        ret = recvfrom(fd, buf, sizeof(buf), 0,(struct sockaddr *)&client_sin, &addrlen);
        printf("recv msg success\n");
        if(ret < 0){
            perror("recvfrom fail\n");
            continue;
        }
        if(!inet_ntop(AF_INET, (void *)&client_sin.sin_addr, ipv4_addr, sizeof(client_sin))){
            perror("inet_ntop fail\n");
            return -1;
        }
        printf("Recived from(%s:%d), data: %s\n", ipv4_addr, ntohs(client_sin.sin_port), buf);
        if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){//recv quit
            printf("Client(%s:%d) is exiting!\n", ipv4_addr, ntohs(client_sin.sin_port));
            break;
        }
    }
    printf("function over\n");
    return 0;
}

udp-client

/*************************************************************************
    > File Name: udp_demo.c
    > Author: 
    > Mail: 
    > Created Time: 2019年05月26日 星期日 07时03分53秒
 ************************************************************************/

#include<stdio.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<strings.h>
#include<string.h>
#include<stdlib.h>

#define BUFSIZE 128
#define QUIT_STR "quit"

void usage(char *s){
    printf("\n This is udp demo!\n");
    printf("\nUsage:\n\t %s serv_ip serv_port", s);
    printf("\n\t serv_ip: udp server ip address");
    printf("\n\t serv_port: udp server port \n\n");
}

int main(int argc, char **argv){
    int fd = -1;
    struct sockaddr_in sin;
    if(argc != 3){
        usage(argv[0]);
        printf("31\n");
        return -1;
    }
    int port = 0;
    port = atoi(argv[2]);
    if(port < 0 ||(port > 50000)){
        usage(argv[0]);
        printf("input port = [%d] 38\n", port);
        return -1;
    }

    if(fd = socket(AF_INET, SOCK_DGRAM, 0) < 0){
        perror("socket fail\n");
        return -1;
    }
    bzero(&sin, sizeof(sin));
    printf("-->port=[%d], addr=[%s]\n", port, argv[1]);
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    sin.sin_addr.s_addr = inet_addr(argv[1]);
//    if(inet_pton(AF_INET, argv[1], (void *)&sin.sin_addr) != 1){
//        perror("inet_pton\n");
//        return -1;
//    }

    char buf[BUFSIZE];
    printf("UDP client starting ...OK\n");
    while(1){
        bzero(buf, BUFSIZE);
        printf("Please input the string to server:\n");
        if(fgets(buf, BUFSIZE -1, stdin) == NULL){
            perror("fgets fail\n");
            continue;
        }
        sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)&sin, sizeof(sin));
        if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){
            printf("Client is exited\n");
            break;
        }
    }
    printf("function over!\n");
    return 0;
}

网络编程 I/O多路复用

阻塞I/O模式

  • 阻塞 I/O模式是最普通使用的I/O模式,大部分程序使用的都是阻塞模式的I/O
  • 缺省情况下,套接字建立后所处于的模式就是阻塞I/O模式。
  • 前面学习的很多读写函数在调用过程中会发生阻塞。
    • 读操作中的read、recv、recvfrom
    • 写操作中的write、send
    • 其他操作:accept、connect

非阻塞I/O模式

  • 当我们将一个套接字设置为非阻塞模式,我们相当于高速系统内核,如果请求的I/O操作不能干马上完成就马上返回一个错误给我。
  • 当一个应用程序使用非阻塞模式的套接字,它需要使用一个循环来不停地测试是否一个文件描述符有数据可度,(成为polling)
  • 应用程序不停的polling内核来检查是否I/O操作已经就绪,这将是一个极浪费CPU资源的操作。
  • 这种模式使用中不普遍。
    使用fcntl()函数来设置一个套接字描述符为非阻塞模式

多路复用

Linux中每个进程默认情况下,最多可以打开1024个文件,最多有1024个文件描述符
文件描述符的特点:

  • 非负整数
  • 从最小可用的数字来分配
  • 每个进程启动时默认打开0、1、2三个文件描述符。
    多路复用API
  • select()
int main(){
    fd_set rset;
    fd = socket();
    bind();
    listen();
    maxfd = fd;
    FD_ZERO(&rset);
    FD_SET(fd, &rset);//将建立好的套接字加入集合中。
    struct timeval tout;
    tout.tv_sec = 5;
    tout.tv_usec = 0;
    select(maxfd +1 , &rset, NULL, NULL, &tout);
    if(FD_ISSET(fd, &rset)){
        newfd = accept(fd, ...);    
    }
    //依次判断已建立的客户端是否有数据。
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值