在这里插入图片描述

socket()函数

在这里插入图片描述
在这里插入图片描述

bind()函数

在这里插入图片描述
通用结构体
在这里插入图片描述
基于Internet通信的结构体
在这里插入图片描述

listen()函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

accept()函数

在这里插入图片描述

connect()函数

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

TCP循环服务器模型

未解决的问题
等待连接的时候会阻塞
发数据的时候会阻塞

服务器代码

/*
 *  @file           server.c
 *  @brief          TCP编程API的服务器
 *  @version 1.1    连接客户端后会被阻塞,连不上其它的客户端
 *  @author         北豼
 *  @date           2022年5月13日
 */

#include "net.h"

int main(void)
{
        int fd = -1;
        struct sockaddr_in sin;

        /*1.创建socket fd*/
        if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                perror("socket");
                exit(1);
        }

        /*2.绑定*/
        /*2.1填充struct sockaddr_in结构体变量*/
        bzero(&sin, sizeof(sin));               //将struct sockaddr_in结构体变量清零
        sin.sin_family = AF_INET;
        sin.sin_port = htons(SERV_PORT);        //主机字节序的端口号转换为网络字节序的端口号
#if 0
        //主机字节序的IP转换为网络字节序的IP
        sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
#else
        //主机字节序的IP转换为网络字节序的IP
        if (inet_pton(AF_INET, SERV_IP_ADDR, (void *)&sin.sin_addr) != 1)
        {
                perror("inet_pton");
                exit(1);
        }
#endif
        /*2.2绑定*/
        if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
        {
                perror("bind");
                exit(1);
        }

        /*3.调用listen()把主动套接字变成被动套接字*/
        if ( listen(fd, BACKLOG) < 0)                   //BACKLOG = 5
        {
                perror("listen");
                exit(1);
        }

        /*4.阻塞等待客户端请求*/
        int newfd = -1;
        newfd = accept(fd, NULL, NULL);                 //后面的两个参数可以接收客户端的属性信息
        if(newfd < 0)
        {
                perror("accept");
                exit(1);
        }

        printf("accept sucessfully!\n");

        /*5.读写*/
        /*和newfd进行数据读写*/
        int ret = -1;
        char buf[BUFSIZ];

        while (1)
        {
                bzero(buf, BUFSIZ);
                do
                {
                        ret =  read(newfd, buf, BUFSIZ-1);
                }while (ret < 0 && EINTR == errno);             //如果读取的过程中遇到了中断,则再次从newfd中读取数据到buf中
                if (ret < 0)
                {
                        perror("read");
                        exit(1);
                }
                if (!ret)               //对方已经关闭
                {
                        break;
                }

                printf("Receive data: %s\n", buf);

                if (!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR)))      //用户输入了quit字符(忽略大小写)
                {
                        printf("Client is exiting!\n");
                        break;
                }
        }

        close(newfd);
        close(fd);

        return 0;
}

客户端代码

/*
 *  @file           client.c
 *  @brief          TCP编程API的客户端
 *  @version 1.1    连接客户端后会被阻塞,连不上其它的客户端
 *  @author         北豼
 *  @date           2022年5月13日
 */

#include "net.h"

int main(void)
{
        int fd = -1;
        struct sockaddr_in sin;

        /*1.创建socket fd*/
        if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                perror("socket");
                exit(1);
        }

        /*2.连接服务器*/
        /*2.1填充struct sockaddr_in结构体变量*/
        bzero(&sin, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(SERV_PORT);        //网络字节序的端口号
#if 0
        sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
#else
        if (inet_pton(AF_INET, SERV_IP_ADDR, (void *)&sin.sin_addr) != 1)
        {
                perror("inet_pton");
                exit(1);
        }
#endif

        if ((connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0))
        {
                perror("connect");
                exit(1);
        }

        /*3.读写数据*/
        char buf[BUFSIZ];
        while (1)
        {
                bzero(buf, BUFSIZ);
                if (fgets(buf, BUFSIZ-1, stdin) == NULL)
                {
                        continue;
                }
                write(fd, buf, strlen(buf));

                if (!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR)))      //用户输入了quit字符
                {
                        printf("Client is exiting!\n");
                        break;
                }

        }

        /*4.关闭套接字*/
        close(fd);
}

头文件

#ifndef _MAKEU_NET_H_
#define _MAKEU_NET_H_

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.153.13"
#define BACKLOG 5

#define QUIT_STR "quit"

#endif

在这里插入图片描述

TCP多进程并发服务器

服务器

/*
 *  @file           server_3.c
 *  @brief          TCP编程API的服务器
 *  @version 1.4    优化3:用多进程处理已经建立好连接的客户端数据
                    优化4:允许绑定地址快速重用
 *  @author         北豼
 *  @date           2022年5月13日
 */

#include "net_3.h"
#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>

void cli_data_handle(void *arg);

void sig_child_handle(int signo)
{
        if (SIGCHLD == signo)
        {
                waitpid(-1, NULL, WNOHANG);
        }
}

int main(void)
{
        int fd = -1;
        struct sockaddr_in sin;

        signal(SIGCHLD, sig_child_handle);

        /*1.创建socket fd*/
        if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                perror("socket");
                exit(1);
        }

        /*优化4:允许绑定地址快速重用*/
        int b_reuse = 1;
        setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));

        /*2.绑定*/
        /*2.1填充struct sockaddr_in结构体变量*/
        bzero(&sin, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(SERV_PORT);        //网络字节序的端口号

        /*优化1:让服务器程序能绑定在任意的IP上*/
#if 1
        sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
        if (inet_pton(AF_INET, SERV_IP_ADDR, (void *)&sin.sin_addr) != 1)
        {
                perror("inet_pton");
                exit(1);
        }
#endif
        /*2.2绑定*/
        if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
        {
                perror("bind");
                exit(1);
        }

        /*3.调用listen()把主动套接字变成被动套接字*/
        if ( listen(fd, BACKLOG) < 0)
        {
                perror("listen");
                exit(1);
        }

        int newfd = -1;
        struct sockaddr_in cin;
        socklen_t addrlen = sizeof(cin);
        
        while (1)
        {
                pid_t pid = -1;

                /*4.阻塞等待客户端请求*/
                if ((newfd = accept(fd, (struct sockaddr *)&cin, &addrlen)) < 0)
                {
                        perror("accept");
                        break;
                }
                /*创建一个子进程用于处理已建立连接的客户的交互数据*/
                if ((pid = fork()) < 0)
                {
                        perror("fork");
                        break;
                }
                if (pid == 0)           //子进程
                {
                        close(fd);
                        char ipv4_addr[16];
                        if (!inet_ntop(AF_INET, (void *)&cin.sin_addr, ipv4_addr, sizeof(cin)))
                        {
                                perror("inet_ntop");
                                exit(1);
                        }
                        printf("Client(%s:%d) is connected!\n", ipv4_addr, ntohs(cin.sin_port));
                        cli_data_handle(&newfd);
                        return 0;
                }
                else                    //父进程
                {
                        close(newfd);
                }

        }

        close(fd);
        return 0;
}

void cli_data_handle(void *arg)
{
        int newfd = *(int *)arg;
        printf("child handling process: newfd = %d\n", newfd);

        /*5.读写*/
        /*和newfd进行数据读写*/
        int ret = -1;
        char buf[BUFSIZ];
        while (1)
        {
                bzero(buf, BUFSIZ);
                do
                {
                        ret =  read(newfd, buf, BUFSIZ-1);
                }while (ret < 0 && EINTR == errno);
                if (ret < 0)
                {
                        perror("read");
                        exit(1);
                }
                if (!ret)               //对方已经关闭
                {
                        break;
                }

                printf("Receive data: %s\n", buf);

                if (!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR)))      //用>户输入了quit字符
                {
                        printf("Client(fd = %d) is exiting!\n", newfd);
                        break;
                }
        }
        close(newfd);

}

客户端

/*
 *  @file           client_3.c
 *  @brief          TCP编程API的客户端
 *  @version 1.4    增加了参数的输入./client_3 serv_ip serv_port
 *  @author         北豼
 *  @date           2022年5月13日
 */

#include "net_3.h"

void usage(char *s)
{
        printf("\n%s serv_ip serv_port\n", s);
        printf("\n\t serv_ip: server ip address");
        printf("\n\t serv_port: server port(>5000)\n\n");
}

int main(int argc, char **argv)
{
        int fd = -1;
        int port = -1;
        struct sockaddr_in sin;

        if (argc != 3)
        {
                usage(argv[0]);
                exit(1);
        }
        port = atoi(argv[2]);           //把字符串转换成整数
        if(port < 5000)
        {
                usage(argv[0]);
                exit(1);
        }

        /*1.创建socket fd*/
        if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                perror("socket");
                exit(1);
        }

        /*2.连接服务器*/
        /*2.1填充struct sockaddr_in结构体变量*/
        bzero(&sin, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(port);        //网络字节序的端口号

#if 0
        sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
#else
        if (inet_pton(AF_INET, argv[1], (void *)&sin.sin_addr) != 1)
        {
                perror("inet_pton");
                exit(1);
        }
#endif

        if ((connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0))
        {
                perror("connect");
                exit(1);
        }

        /*3.读写数据*/
        char buf[BUFSIZ];
        while (1)
        {
                bzero(buf, BUFSIZ);
                if (fgets(buf, BUFSIZ-1, stdin) == NULL)
                {
                        continue;
                }
                write(fd, buf, strlen(buf));

                 if (!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR)))      //>用户输>入了quit字符
                {
                        printf("Client is exiting!\n");
                        break;
                }

        }

        /*4.关闭套接字*/
        close(fd);
}

TCP多线程服务器

服务器代码

/*
 *  @file           server_2.c
 *  @brief          TCP编程API的服务器
 *  @version 1.3    优化1:让服务器程序能绑定在任意的IP上
                    优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号
                    优化3:用多线程处理已经建立好连接的客户端数据
                    优化4:允许绑定地址快速重用
 *  @author         北豼
 *  @date           2022年5月13日
 */

#include "net_2.h"
#include <pthread.h>

void cli_data_handle(void *arg);

int main(void)
{
        int fd = -1;
        struct sockaddr_in sin;

        /*1.创建socket fd*/
        if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                perror("socket");
                exit(1);
        }

        /*优化4:允许绑定地址快速重用*/
        int b_reuse = 1;
        setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));

        /*2.绑定*/
        /*2.1填充struct sockaddr_in结构体变量*/
        bzero(&sin, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(SERV_PORT);        //网络字节序的端口号

        /*优化1:让服务器程序能绑定在任意的IP上*/
#if 1
        sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
        if (inet_pton(AF_INET, SERV_IP_ADDR, (void *)&sin.sin_addr) != 1)
        {
                perror("inet_pton");
                exit(1);
        }
#endif
        /*2.2绑定*/
        if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
        {
                perror("bind");
                exit(1);
        }

        /*3.调用listen()把主动套接字变成被动套接字*/
        if ( listen(fd, BACKLOG) < 0)
        {
                perror("listen");
                exit(1);
        }

        /*优化3:用多线程处理已经建立好连接的客户端数据*/
        int newfd = -1;
        pthread_t tid;
        struct sockaddr_in cin;
        socklen_t addrlen = sizeof(cin);

        while (1)
        {
                /*4.阻塞等待客户端请求*/
                /*优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号*/
                if ((newfd = accept(fd, (struct sockaddr *)&cin, &addrlen)) < 0)
                {
                        perror("accept");
                        exit(1);
                }

                char ipv4_addr[16];
                if (!inet_ntop(AF_INET, (void *)&cin.sin_addr, ipv4_addr, sizeof(cin)))
                {
                        perror("inet_ntop");
                        exit(1);
                }

                printf("Client(%s:%d) is connected!\n", ipv4_addr, ntohs(cin.sin_port));
                printf("accept sucessfully!\n");

                pthread_create(&tid, NULL, (void *)cli_data_handle, (void *)&newfd);
        }

        close(fd);
        return 0;
}

void cli_data_handle(void *arg)
{
        int newfd = *(int *)arg;
        printf("handler thread: newfd = %d\n", newfd);

        /*5.读写*/
        /*和newfd进行数据读写*/
        int ret = -1;
        char buf[BUFSIZ];
        while (1)
        {
                bzero(buf, BUFSIZ);
                do
                {
                        ret =  read(newfd, buf, BUFSIZ-1);
                }while (ret < 0 && EINTR == errno);
                if (ret < 0)
                {
                        perror("read");
                        exit(1);
                }
                if (!ret)               //对方已经关闭
                {
                        break;
                }

                printf("Receive data: %s\n", buf);

                if (!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR)))      //用户输入了quit字符
                {
                        printf("Client(fd = %d) is exiting!\n", newfd);
                        break;
                }
        }
        close(newfd);

}

客户端代码

/*
 *  @file           client_2.c
 *  @brief          TCP编程API的客户端
 *  @version 1.3    增加了参数的输入./client_2 serv_ip serv_port
 *  @author         北豼
 *  @date           2022年5月13日
 */

#include "net_2.h"

void usage(char *s)
{
        printf("\n%s serv_ip serv_port\n", s);
        printf("\n\t serv_ip: server ip address");
        printf("\n\t serv_port: server port(>5000)\n\n");
}

int main(int argc, char **argv)
{
        int fd = -1;
        int port = -1;
        struct sockaddr_in sin;

        if (argc != 3)
        {
                usage(argv[0]);
                exit(1);
        }
        port = atoi(argv[2]);           //把字符串转换成整数
        if(port < 5000)
        {
                usage(argv[0]);
                exit(1);
        }

        /*1.创建socket fd*/
        if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                perror("socket");
                exit(1);
        }
        
        /*2.连接服务器*/
        /*2.1填充struct sockaddr_in结构体变量*/
        bzero(&sin, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(port);        //网络字节序的端口号
#if 0
        sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
#else
        if (inet_pton(AF_INET, argv[1], (void *)&sin.sin_addr) != 1)
        {
                perror("inet_pton");
                exit(1);
        }
#endif

        if ((connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0))
        {
                perror("connect");
                exit(1);
        }

        /*3.读写数据*/
        char buf[BUFSIZ];
        while (1)
        {
                bzero(buf, BUFSIZ);
                if (fgets(buf, BUFSIZ-1, stdin) == NULL)
                {
                        continue;
                }
                write(fd, buf, strlen(buf));

                 if (!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR)))      //用户输>入了quit字符
                {
                        printf("Client is exiting!\n");
                        break;
                }

        }

        /*4.关闭套接字*/
        close(fd);
}

头文件

#ifndef _MAKEU_NET_H_
#define _MAKEU_NET_H_

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.153.13"
#define BACKLOG 5

#define QUIT_STR "quit"

#endif

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐