Linux网络TCP服务器和客户端

只能接收一个连接请求的TCP服务器 。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>

void sys_err(char *error)
{
    perror(error);
    exit(1);
}

int main(int argc, char **argv)
{
    int lfd;
    int ret;
    struct sockaddr_in serv_addr;
    //创建socket,指定IPv4协议族 TCP协议
    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1)
    {
	    sys_err("socket error");
    }
    //初始化一个地址结构 man 7 ip 查看对应信息
    serv_addr.sin_family = AF_INET;                    //选择协议族为IPv4
    serv_addr.sin_port = htons(6666);                  //绑定端口号 
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);     //监听本地所有IP地址
    //绑定服务器地址结构
    ret = bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (ret == -1)
    {
	    sys_err("bind error");
    }

    //设置监听上限,设定链接上限,注意此处不阻塞,同一时刻允许向服务器发起链接请求的数量
    ret = listen(lfd, 128);
    if (ret == -1)
    {
	    sys_err("listen error");
    }

    //等待并接收连接请求
    struct sockaddr_in client_addr;
    socklen_t len = sizeof(client_addr);
    //监听客户端链接, 会阻塞
    int cfd = accept(lfd, (struct sockaddr *)&client_addr, &len);
    if (cfd == -1)
    {
	    sys_err("accept error");
    }

    printf("accept successful\n");
    char ipbuf[64] = { 0 };
    printf("client ip:%s, port:%d\n", 
        inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)), 
        ntohs(client_addr.sin_port));

    while (1)
    {
	    char buf[1024] = { 0 };
	    int len = read(cfd, buf, sizeof(buf));
	    if (len == -1)
	    {
	        sys_err("read error");
	    }
	    else if (len == 0)
	    {
	        printf("客户端断开连接\n");
	        close(cfd);
	        break;
	    }
	    else
	    {
	        printf("recv buf:%s\n",buf);

	        for(int i=0; i<len; i++)
	        {
		        buf[i] = toupper(buf[i]);
	        }
	        printf("send buf: %s", buf);
	        write(cfd, buf, len);
	    }
    }

    close(lfd);
    return 0;
}

 

 简单的客户端实现

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <fcntl.h>

void sys_err(char *error)
{
    perror(error);
    exit(1);
}

int main(int argc, char **argv)
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1)
    {
	sys_err("socket error");
    }
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(6666);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr.s_addr);

    //与服务器建立连接
    int ret = connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (ret == -1)
    {
	sys_err("connect error");
    }

    while (1)
    {
	char buf[512];
	fgets(buf, sizeof(buf), stdin);
	//将输入的数据发给服务器
	write(fd, buf, strlen(buf));

	//接收服务器发过来的数据
	int len = read(fd, buf, sizeof(buf));
	if (len == 0)
	{
	    sys_err("read error");
	}
	else if (len == 0)
	{
	    printf("服务器端关闭了连接\n");
	    break;
	}
	else
	{
	    printf("recv buf: %s\n", buf);
	}
    }
    
    close(fd);
    return 0;
}

多进程服务器实现多个客户端连接

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <ctype.h>

void sys_err(const char *str)
{
    perror(str);
    exit(-1);
}

//子进程回收
void recycle(int num)
{
    int ret;
    while ((ret = waitpid(-1, NULL, WNOHANG)) > 0)
    {
        printf("recycle pid %d\n", ret);
    }
}

int main(int argc, char **argv)
{
    pid_t pid;
    int lfd, cfd;
    int ret;
    struct sockaddr_in server;
    struct sockaddr_in client;
    socklen_t client_len;
    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1)
    {
        sys_err("socket error");
    }
    //绑定服务器地址信息
    server.sin_family = AF_INET;
    server.sin_port   = htons(6666);
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    ret = bind(lfd, (struct sockaddr *)&server, sizeof(server));
    if (ret == -1)
    {
        sys_err("bind error");
    }
    ret = listen(lfd, 128);
    if (ret == -1)
    {
        sys_err("listen error");
    }
    //注册子进程回收处理
    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = recycle;
    sigemptyset(&act.sa_mask);
    sigaction(SIGCHLD, &act, NULL);
    //开始循环接收客户端的连接请求
    while (1)
    {
        int readlen;
        char bufip[64] = { 0 };
        char buf[1024] = { 0 };
        client_len = sizeof(client);
        // 父进程接收连接请求
        // accept阻塞的时候被信号中断, 处理信号对应的操作之后
        // 回来之后不阻塞了, 直接返回-1, 这时候 errno==EINTR
        cfd = accept(lfd, (struct sockaddr *)&client, &client_len);
        while ( cfd == -1 && errno == EINTR)
        {
            cfd = accept(lfd, (struct sockaddr *)&client, &client_len);
        }
        printf("connect sucessful\n");
        pid = fork();
        if (pid < 0)
        {
            sys_err("fork error");
        }
        else if (pid == 0)
        {
            //关闭用于监听的fd
            close(lfd);
            //子进程
            printf("client ip:%s, port %d\n",
                    inet_ntop(AF_INET, &client.sin_addr.s_addr, bufip, sizeof(bufip)),
                    ntohs(client.sin_port));

            while (1)
            {
                readlen = read(cfd, buf, sizeof(buf));
                if (readlen == -1)
                {
                    sys_err("read error");
                }
                else if (readlen == 0)
                {
                    printf("客户端断开连接\n");
                    close(cfd);
                    break;
                }
                else
                {
                    //正常读取
                    printf("recv from %s, buf: %s\b", bufip, buf);
                    write(cfd, buf, readlen);
                }
            }
            return 1;
        }
        else
        {
            //父进程
            //关闭用于通信的fd
            close(cfd);
        }
    }
    close(lfd);
    return 0;
}

 

 多线程服务器实现多个客户端连接

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <errno.h>

extern errno;

typedef struct sockinfo
{
    int fd;
    pthread_t tid;
    struct sockaddr_in addr;
} sockinfo;

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}

void *worker(void *arg)
{
    char bufip[64];
    char buf[1024];
    sockinfo *sock = (sockinfo *)arg;
    printf("pthread id %lu, client ip: %s, port: %d\n",
            sock->tid,
            inet_ntop(AF_INET, &sock->addr.sin_addr.s_addr, bufip, sizeof(bufip)),
            ntohs(sock->addr.sin_port));

    while (1)
    {
        int len = read(sock->fd, buf, sizeof(buf));
        if (len == -1)
        {
            printf("read error %s\n", strerror(errno));
            pthread_exit(NULL);
        }
        else if (len == 0)
        {
            printf("有客户端断开连接了\n");
            close(sock->fd);
            break;
        }
        else
        {
            printf("recv from ip %s, buf %s\n", bufip, buf);
            write(sock->fd, buf, len);
        }
    }
    return NULL;
}

int main(int argc, char **argv)
{
    int lfd;
    int ret;
    sockinfo sock[256];
    int index = 0;
    socklen_t client_len;

    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1)
    {
        sys_err("socket error");
    }
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port   = htons(6666);
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    ret = bind(lfd, (struct sockaddr *)&server, sizeof(server));
    if (ret == -1)
    {
        sys_err("bind error");
    }
    ret = listen(lfd, 128);
    if (ret == -1)
    {
        sys_err("listen error");
    }

    // cfd = -1
    for (index=0; index < sizeof(sock)/sizeof(sock[0]); index++)
    {
        sock[index].fd = -1;
    }
    //等待接收连接请求
    while (1)
    {
        for (index=0; index < sizeof(sock)/sizeof(sock[0]); index++)
        {
            if (sock[index].fd == -1 )
            {
                break;
            }
        }

        if (index == 256)
        {
            break;
        }
        client_len = sizeof(struct sockaddr_in);
        sock[index].fd = accept(lfd, (struct sockaddr *)&sock[index].addr, &client_len);
        if (sock[index].fd == -1)
        {
            sys_err("accept error");
        }
        ret = pthread_create(&sock[index].tid, NULL, worker, &sock[index]);
        if (ret != 0)
        {
            sys_err("pthread create error");
        }
        pthread_detach(sock[index].tid);
    }
    close(lfd);
    pthread_exit(NULL);
}

 

使用select

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

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}

int main(int argc, char **argv)
{
    struct sockaddr_in server;
    int lfd;
    int ret;

    //创建socket
    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1)
    {
        sys_err("socket error");
    }
    //bind
    server.sin_family = AF_INET;
    server.sin_port   = htons(6666);
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    ret = bind(lfd, (struct sockaddr *)&server, sizeof(server));
    if (ret == -1)
    {
        sys_err("bind error");
    }
    //设置监听上限
    ret = listen(lfd, 128);
    if (ret == -1)
    {
        sys_err("listen error");
    }

    //等待接收连接请求
    struct sockaddr_in client;
    socklen_t client_len = sizeof(client);

    int maxfd = lfd;
    fd_set reads, temp;
    //初始化fd集
    FD_ZERO(&reads);
    FD_SET(lfd, &reads);

    while (1)
    {
        temp = reads;
        ret = select(maxfd+1, &temp, NULL, NULL, NULL);
        if (ret == -1)
        {
            sys_err("select error");
        }
        if (FD_ISSET(lfd, &temp))
        {
            //接受连接请求,不阻塞
            int cfd = accept(lfd, (struct sockaddr *)&client, &client_len);
            if (cfd == -1)
            {
                sys_err("accept error");
            }
            char ip[64];
            printf("new client IP:%s, port:%d\n",
                   inet_ntop(AF_INET, &client.sin_addr.s_addr, ip, sizeof(ip)),
                   ntohs(client.sin_port));
            //将新的fd添加到集合中
            FD_SET(cfd, &reads);
            //更新最大fd
            maxfd = maxfd < cfd ? cfd : maxfd;
        }
        //数据读写
        for (int i=lfd+1; i<=maxfd; i++)
        {
            if (FD_ISSET(i, &temp))
            {
                char buf[1024] = { 0 };
                int len = recv(i, buf, sizeof(buf), 0);
                if (len == -1)
                {
                    perror("recv error");
                }
                else if (len == 0)
                {
                    printf("client 断开连接\n");
                    close(i);
                    //从集合中删除i
                    FD_CLR(i, &reads);
                }
                else
                {
                    printf("recv buf: %s\n", buf);
                    send(i, buf, strlen(buf)+1, 0);
                }
            }
        }
    }
    close(lfd);
    return 0;
}

 

使用poll

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc, char **argv)
{
    int lfd;
    struct sockaddr_in server, client;
    socklen_t client_len;
    int ret;

    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1)
    {
        perror("socket error");
        exit(1);
    }
    server.sin_family = AF_INET;
    server.sin_port   = htons(6666);
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    ret = bind(lfd, (struct sockaddr *)&server, sizeof(server));
    if (ret == -1)
    {
        perror("bind error");
        exit(1);
    }
    ret = listen(lfd, 128);
    if (ret == -1)
    {
        perror("listen error");
        exit(1);
    }
    //poll结构体
    struct pollfd allfd[1024];
    int max_index = 0;
    for (int i=0; i<1024; i++)
    {
        allfd[i].fd = -1;
        allfd[i].events = POLLIN;
    }
    allfd[0].fd = lfd;
    allfd[0].events = POLLIN;

    while (1)
    {
        int i=0;
        ret = poll(allfd, max_index+1, -1);
        if (ret == -1)
        {
            perror("poll error");
            exit(1);
        }
        if (allfd[0].revents & POLLIN)
        {
            char ip[64];
            client_len = sizeof(client);
            int cfd = accept(lfd, (struct sockaddr*)&client, &client_len);
            if (cfd == -1)
            {
                perror("accept error");
                exit(1);
            }
            printf("client ip:%s, port:%d\n",
                    inet_ntop(AF_INET, &client.sin_addr.s_addr, ip, sizeof(ip)),
                    ntohs(client.sin_port));
            for (i=0; i<1024; i++)
            {
                if (allfd[i].fd == -1)
                {
                    allfd[i].fd = cfd;
                    break;
                }
            }
            //last
            max_index = max_index < i? i: max_index;
        }
        //foreach
        for (i=1; i<=max_index; i++)
        {
            int fd = allfd[i].fd;
            if (fd == -1)
            {
                continue;
            }
            if (allfd[i].revents & POLLIN)
            {
                char buf[1024] = {0};
                int len = recv(fd, buf, sizeof(buf), 0);
                if (len == -1)
                {
                    perror("recv error");
                    exit(1);
                }
                else if (len == 0)
                {
                    printf("客户已经断开连接\n");
                    allfd[i].fd = -1;
                    close(fd);
                }
                else
                {
                    printf("recv buf = %s\n",buf);
                    for (int j=0; j<len; j++)
                    {
                        buf[j] = toupper(buf[j]);
                    }
                    send(fd, buf, strlen(buf)+1, 0);
                }
            }
        }
    }
    close(lfd);
    return 0;
}

 

 

epoll 水平触发模式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <ctype.h>

int main(int argc, char **argv)
{
    int lfd;
    int ret;
    struct sockaddr_in server, client;
    socklen_t client_len;

    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1)
    {
        perror("socket error");
        exit(1);
    }
    //绑定服务器地址信息
    server.sin_family = AF_INET;
    server.sin_port   = htons(6666);
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    ret = bind(lfd, (struct sockaddr *)&server, sizeof(server));
    if (ret == -1)
    {
        perror("bind error");
        exit(1);
    }
    //设置监听上限
    ret = listen(lfd, 128);
    if (ret == -1)
    {
        perror("listen error");
        exit(1);
    }
    //开始epoll的相关设置
    //创建epoll树
    int epfd = epoll_create(2000);
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = lfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);

    struct epoll_event all[2000];
    while (1)
    {
        //使用epoll通知内核IO检测
        ret = epoll_wait(epfd, all, sizeof(all)/sizeof(all[0]), -1);
        //遍历数组中的前ret个元素
        for (int i=0; i<ret; i++)
        {
            int fd = all[i].data.fd;
            if (fd == lfd)
            {
                client_len = sizeof(client);
                //接受连接请求
                int cfd = accept(lfd, (struct sockaddr *)&client, &client_len);
                if (cfd == -1)
                {
                    perror("accept error");
                    exit(1);
                }
                //将新连接的客户端cfd挂到树上
                struct epoll_event temp;
                temp.events = EPOLLIN;
                temp.data.fd = cfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &temp);
                //打印ip信息
                char ip[64];
                printf("new client ip:%s,port%d\n",
                        inet_ntop(AF_INET, &client.sin_addr.s_addr, ip, sizeof(ip)),
                        ntohs(client.sin_port));
            }
            else
            {
                //客户端数据发送过来
                if (!all[i].events & EPOLLIN)
                {
                    continue;
                }
                char buf[1024] = { 0 };
                int len = recv(fd, buf, sizeof(buf), 0);
                if (len == -1)
                {
                    perror("recv error");
                    exit(1);
                }
                else if (len == 0)
                {
                    printf("客户端断开连接\n");
                    ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
                    if (ret == -1)
                    {
                        perror("epoll_ctl error");
                        exit(1);
                    }
                    close(fd);
                }
                else
                {
                    printf("recv buf:%s\n", buf);
                    write(fd, buf, len);
                }
            }
        }
    }
    close(lfd);
    return 0;
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值