一、什么是网络

网络是一组计算机或网络设备通过有形的线缆或无形的媒介如无线,连接起来,按照一定的规则,进行通信的集合。

二、TCP/IP模型

1.应用层:HTTP(万维网服务),FTP(文件传输),SMTP(电子邮件),SSH(安全远程登陆),DNS(名称<-> IP地址寻找,域名系统)
2.传输层:TCP(面向字节流), UDP(面向报文)

3.网络层:IP协议就在这里,它负责对数据加上IP地址和其他的数据以确定传输的目标。
4.网络接口层(数据链路层):这个层次为待传送的数据加入一个以太网协议头,并进行CRC编码,为最后的数据传输做准备。

三、四元组

源和目标 IP地址(来自网络层)源和目标端口号标识
网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

四、TCP三次握手示意图

五、TCP流程图

六、常用API函数

int socket(int domain, int type, int protocol); //新建一个socket端口
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);// 绑定socket端口
int listen(int sockfd, int backlog);//建立监听
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//进入等待客户端连接模式,并阻塞当前进程/线程
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//客户端连接到服务器ssize_t send(int sockfd, const void *buf, size_t len, int flags); //通过已建立到通信socket发送数据,类似于函数
writessize_t recv(int sockfd, void *buf, size_t len, int flags);//接收另一端通过socket发送的数据

//网络通信相关辅助函数如下
//主机字节序/网络字节序端口号 互转
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

//IP 地址转换
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);

七、示例程序
客户端:

 	int sock_fd;
    struct sockaddr_in server_addr;
    int ret;
    char send_buf[1000];
    socklen_t send_len;

    if (argc != 2)
    {
        fprintf(stderr, "Usage:%s hostname\n\a", argv[0]);
        exit(1);
    }

    /* socket */
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);//AF_INET:IPV4;SOCK_STREAM:TCP
    if (-1 == sock_fd)
    {
        fprintf(stderr, "socket error:%s\n", strerror(errno));
        exit(1);
    }

    /* set sockaddr_in parameter*/
    memset(&server_addr, 0, sizeof(struct sockaddr_in));//clear
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT_NUMBER);
    ret = inet_aton(argv[1], &server_addr.sin_addr);  //将一个字符串IP地址转换为一个32位的网络序列IP地址
    if (0 == ret)
    {
        fprintf(stderr, "server_ip error.\n");
        close(sock_fd);
        exit(1);
    }

    /* connect */
    ret = connect(sock_fd, (const struct sockaddr*)&server_addr, sizeof(struct sockaddr));
    if (-1 == ret)
    {
        fprintf(stderr, "connect error:%s\n", strerror(errno));
        close(sock_fd);
        exit(1);
    }

    while (1)
    {
        if (fgets(send_buf, 999, stdin))
        {
            /* send */
            send_len = send(sock_fd, send_buf, strlen(send_buf), 0);
            if (send_len <= 0)
            {
                fprintf(stderr, "send error:%s\n", strerror(errno));
                close(sock_fd);
                exit(1);
            }
        }
    }

    /* close */
    close(sock_fd);

服务器:

    int sock_fd, new_fd;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    int ret;
    socklen_t addr_len;
    int recv_len;
    unsigned char recv_buf[1000];
    int client_num = -1;

    signal(SIGCHLD, SIG_IGN); //在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程。按系统默认将忽略此信号。如果 父进程希望被告知其子系统的这种状态,则应捕捉此信号。信号的捕捉函数中通常调用wait函数以取得进程ID和其终止状态。这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号

    /* socket */
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);//AF_INET:IPV4;SOCK_STREAM:TCP
    if (-1 == sock_fd)
    {
        fprintf(stderr, "socket error:%s\n\a", strerror(errno));
        exit(1);
    }

    /* set server sockaddr_in */
    memset(&server_addr, 0, sizeof(struct sockaddr_in));//clear
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY:This machine all IP
    server_addr.sin_port = htons(PORT_NUMBER);

    /* bind */
    ret = bind(sock_fd, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr));
    if (-1 == ret)
    {
        fprintf(stderr, "bind error:%s\n\a", strerror(errno));
        close(sock_fd);
        exit(1);
    }

    /* listen */
    ret = listen(sock_fd, BACKLOG);
    if (-1 == ret)
    {
        fprintf(stderr, "listen error:%s\n\a", strerror(errno));
        close(sock_fd);
        exit(1);
    }

    /* accept */
    while (1)
    {
        addr_len = sizeof(struct sockaddr);
        new_fd = accept(sock_fd, (struct sockaddr*)&client_addr, &addr_len);
        if (-1 == new_fd)
        {
            fprintf(stderr, "accept error:%s\n\a", strerror(errno));
            close(sock_fd);
            exit(1);
        }

        client_num++;
        fprintf(stderr, "Server get connetion form client%d: %s\n", client_num, inet_ntoa(client_addr.sin_addr));//将一个字符串IP地址转换为一个32位的网络序列IP地址
        if (!fork())
        {
            /* Child process */
            while (1)
            {
                /* recv */
                recv_len = recv(new_fd, recv_buf, 999, 0);
                if (recv_len <= 0)
                {
                    fprintf(stderr, "recv error:%s\n\a", strerror(errno));
                    close(new_fd);
                    exit(1);
                }
                else
                {
                    recv_buf[recv_len] = '\0';
                    printf("Get msg from client%d: %s\n", client_num, recv_buf);
                }
            }
            close(new_fd);
        }
    }
    /* close */
    close(sock_fd);
    exit(0);