Linux C Socket

Linux C Socket

TCP/UDP 服务器和客户端的动作

动作TCPUDP客户端
建立套接字(socket)YYY
绑定IP地址和端口号(bind)YYY
监听(listen)YNN
接受请求(accept)YNN
连接(connect)NNY

字节序转换
机器有大端字节序和小端字节序
而网络字节序是大端的
ntohl, ntohs 网络字节序转换为主机字节序
htonl, htons 主机字节序转换为网络字节序
网络地址转换
inet_ntoa sockaddr_in结构体中sin_addr参数转换为IP地址字符串
inet_addr IP地址字符串转换为sockaddr_in结构体中sin_addr参数

简单的TCP服务器

TCP需要先用accept接收客户端的套接字, 在处理完一个客户机的消息后才能处理下一个客户机的消息.

服务器端(server.c)
c code

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <strings.h>

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#define TCP SOCK_STREAM
#define UDP SOCK_DGRAM

#define MAXLINE 80

void if_error(int status_code, char *err_msg)
{
    if (status_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int net_setup(int sock_type, char *ip_addr, int port, int max_connect)
{
    struct sockaddr_in serv_addr;
    int sock_fd, ret;

    /* socket */
    sock_fd = socket(AF_INET, sock_type, 0);
    if_error(sock_fd, "socket");

    /* bind */
    bzero(&serv_addr, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    serv_addr.sin_addr.s_addr = (NULL == ip_addr ? INADDR_ANY : inet_addr(ip_addr));

    ret = bind(sock_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if_error(ret, "bind");

    /* TCP listen */
    if (sock_type == TCP) {
        ret = listen(sock_fd, max_connect);
        if_error(ret, "listen");
    }
    return sock_fd;
}

int main(int argc, char **argv)
{
    int sock_fd, acc_fd, recv_bytes;
    char buf[MAXLINE];

    struct sockaddr_in cli_addr = {0};
    socklen_t cli_addr_len = sizeof(struct sockaddr);

    /* setup tcp socket */
    sock_fd = net_setup(TCP, NULL, 8080, 10);

    while (1) {
        /* accept */
        acc_fd = accept(sock_fd, (struct sockaddr*)&cli_addr, &cli_addr_len);
        if_error(acc_fd, "accept");
        /* print conn msg */
        printf("ip(%s), port(%d), client connect !\n",
                inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
        /* deal with cli msg */
        while (1) {
            /* recv msg */
            recv_bytes = recv(acc_fd, buf, sizeof(buf), 0);
            if_error(recv_bytes, "recv");

            /* cli exit */
            if (recv_bytes == 0) break;

            /* print cli msg */
            buf[recv_bytes] = '\0';
            printf("from ip(%s), port(%d), client message: %s",
                    inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);
        }
        /* close accept fd */
        close(acc_fd);
        /* print disconn msg */
        printf("ip(%s), port(%d), client disconnect !\n",
                inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
    }
    close(sock_fd);
    return 0;
}

客户端(client.c)
c code

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <strings.h>

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#define TCP SOCK_STREAM
#define UDP SOCK_DGRAM

#define MAXLINE 80

void if_error(int status_code, char *err_msg)
{
    if (status_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int net_conn(int sock_type, char *ip_addr, int port)
{
    struct sockaddr_in server_addr;
    int sock_fd, ret;

    /* socket */
    sock_fd = socket(AF_INET, sock_type, 0);
    if_error(sock_fd, "socket");

    /* bind */
    bzero(&server_addr, sizeof(server_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = (NULL == ip_addr ? INADDR_ANY : inet_addr(ip_addr));

    /* connect */
    ret = connect(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if_error(ret, "connect");

    return sock_fd;
}

int main(int argc, char **argv)
{
    int sock_fd, send_bytes;
    char buf[MAXLINE];

    /* connect tcp server */
    sock_fd = net_conn(TCP, "127.0.0.1", 8080);

    while (1) {
        /* get msg from kbd */
        fgets(buf, sizeof(buf), stdin);
        if (strncmp(buf, "exit", 4) == 0) break;

        /* send msg */
        send_bytes = send(sock_fd, buf, sizeof(buf), 0);
        if_error(send_bytes, "send");
    }
    close(sock_fd);
    return 0;
}

UDP服务器

客户端的代码只需在连接时改成UDP, 其他不变.
sock_fd = net_conn(UDP, "127.0.0.1", 8080);

UDP不需要accept, 所以可以循环处理多个客户端的消息.

服务器端(server.c)
c code

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <strings.h>

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#define TCP SOCK_STREAM
#define UDP SOCK_DGRAM

#define MAXLINE 80

void if_error(int status_code, char *err_msg)
{
    if (status_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int net_setup(int sock_type, char *ip_addr, int port, int max_connect)
{
    struct sockaddr_in serv_addr;
    int sock_fd, ret;

    /* socket */
    sock_fd = socket(AF_INET, sock_type, 0);
    if_error(sock_fd, "socket");

    /* bind */
    bzero(&serv_addr, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    serv_addr.sin_addr.s_addr = (NULL == ip_addr ? INADDR_ANY : inet_addr(ip_addr));

    ret = bind(sock_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if_error(ret, "bind");

    /* TCP listen */
    if (sock_type == TCP) {
        ret = listen(sock_fd, max_connect);
        if_error(ret, "listen");
    }
    return sock_fd;
}

int main(int argc, char **argv)
{
    int sock_fd, recv_bytes;
    char buf[MAXLINE];

    struct sockaddr_in cli_addr = {0};
    socklen_t cli_addr_len = sizeof(struct sockaddr);

    /* setup udp socket */
    sock_fd = net_setup(UDP, NULL, 8080, 0);

    while (1) {
        /* recv msg */
        recv_bytes = recvfrom(sock_fd, buf, sizeof(buf), 0,
                            (struct sockaddr*)&cli_addr, &cli_addr_len);
        if_error(recv_bytes, "recvfrom");

        /* cli exit */
        if (recv_bytes == 0) continue;

        /* print cli msg */
        buf[recv_bytes] = '\0';
        printf("from ip(%s), port(%d), client message: %s",
                inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);
    }
    close(sock_fd);
    return 0;
}

TCP服务器如何处理多个客户机

可以使用线程/进程来处理多个客户机的消息, 这里使用进程.
使用2次fork避免僵尸进程
客户端代码不变
服务器端(server.c)
c code

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <strings.h>

#include <unistd.h>

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#define TCP SOCK_STREAM
#define UDP SOCK_DGRAM

#define MAXLINE 80

void if_error(int status_code, char *err_msg)
{
    if (status_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int net_setup(int sock_type, char *ip_addr, int port, int max_connect)
{
    struct sockaddr_in serv_addr;
    int sock_fd, ret;

    /* socket */
    sock_fd = socket(AF_INET, sock_type, 0);
    if_error(sock_fd, "socket");

    /* bind */
    bzero(&serv_addr, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    serv_addr.sin_addr.s_addr = (NULL == ip_addr ? INADDR_ANY : inet_addr(ip_addr));

    ret = bind(sock_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if_error(ret, "bind");

    /* TCP listen */
    if (sock_type == TCP) {
        ret = listen(sock_fd, max_connect);
        if_error(ret, "listen");
    }
    return sock_fd;
}

int main(int argc, char **argv)
{
    int sock_fd, acc_fd, recv_bytes;
    char buf[MAXLINE];

    pid_t chi_pid, grand_chi_pid;

    struct sockaddr_in cli_addr = {0};
    socklen_t cli_addr_len = sizeof(struct sockaddr);

    /* setup tcp socket */
    sock_fd = net_setup(TCP, NULL, 8080, 10);

    while (1) {
        /* accept */
        acc_fd = accept(sock_fd, (struct sockaddr*)&cli_addr, &cli_addr_len);
        if_error(acc_fd, "accept");

        /* print conn msg */
        printf("ip(%s), port(%d), client connect !\n",
                inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));

        /* chi process deal with cli msg */
        chi_pid = fork();
        if_error(chi_pid, "fork");

        if (chi_pid == 0) {
        /* child process */
            grand_chi_pid = fork();
            if_error(grand_chi_pid, "fork");

            if (grand_chi_pid == 0) {
            /* grand child process */
                while (1) {
                    /* recv msg */
                    recv_bytes = recv(acc_fd, buf, sizeof(buf), 0);
                    if_error(recv_bytes, "recv");

                    /* cli exit */
                    if (recv_bytes == 0) break;

                    /* print msg */
                    buf[recv_bytes] = '\0';
                    printf("child pid(%d), from ip(%s), port(%d), client message: %s",
                            getpid(), inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);
                }
                /* close accept fd */
                close(acc_fd);
                /* print disconn msg */
                printf("ip(%s), port(%d), client disconnect !\n",
                        inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
                exit(0);
            }
            /* chi process exit */
            exit(0);
        } else {
            chi_pid = wait(NULL);
            if_error(chi_pid, "wait");
        }
    }
    close(sock_fd);
    return 0;
}

I/O复用TCP服务器

TCP服务器利用select完成处理多个客户机
服务器端(server.c)
c code

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <strings.h>

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#define TCP SOCK_STREAM
#define UDP SOCK_DGRAM

#define MAXLINE 80

void if_error(int status_code, char *err_msg)
{
    if (status_code < 0) {
        perror(err_msg);
        exit(errno);
    }
}

int net_setup(int sock_type, char *ip_addr, int port, int max_connect)
{
    struct sockaddr_in serv_addr;
    int sock_fd, ret;

    /* socket */
    sock_fd = socket(AF_INET, sock_type, 0);
    if_error(sock_fd, "socket");

    /* bind */
    bzero(&serv_addr, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    serv_addr.sin_addr.s_addr = (NULL == ip_addr ? INADDR_ANY : inet_addr(ip_addr));

    ret = bind(sock_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if_error(ret, "bind");

    /* TCP listen */
    if (sock_type == TCP) {
        ret = listen(sock_fd, max_connect);
        if_error(ret, "listen");
    }
    return sock_fd;
}

int main(int argc, char **argv)
{
    int sock_fd, acc_fd, recv_bytes;
    char buf[MAXLINE];

    fd_set reads, reads_init;
    struct timeval timeout, timeout_init;

    int i, fd_max, fd_num;

    struct sockaddr_in cli_addr = {0};
    socklen_t cli_addr_len = sizeof(struct sockaddr);

    /* setup tcp socket */
    sock_fd = net_setup(TCP, NULL, 8080, 10);

    /* monitor serv socket */
    FD_ZERO(&reads_init);
    FD_SET(sock_fd, &reads_init);
    fd_max = sock_fd;

    /* set timeout time */
    timeout_init.tv_sec = 5;
    timeout_init.tv_usec= 0;

    while (1) {
        /* init reads & timeout for select */
        reads = reads_init;
        timeout = timeout_init;

        /* select */
        fd_num = select(fd_max+1, &reads, NULL, NULL, &timeout);
        if_error(fd_num, "select");

        /* no change */
        if (fd_num == 0) {
            printf("timeout!\n");
            continue;
        }

        for (i = 0; i <= fd_max; i++) {
            /* if changed */
            if (FD_ISSET(i, &reads)) {
                /* serv socket */
                if (i == sock_fd) {
                    acc_fd = accept(sock_fd, (struct sockaddr*)&cli_addr, &cli_addr_len);
                    if_error(acc_fd, "accept");

                    /* add cli socket to fd set */
                    FD_SET(acc_fd, &reads_init);
                    if (fd_max < acc_fd) fd_max = acc_fd;
                    printf("client %d connected .\n", acc_fd);
                } else {
                    /* cli socket, recv msg */
                    recv_bytes = recv(i, buf, sizeof(buf), 0);
                    if_error(recv_bytes, "recv");

                    /* remove socket from set */
                    if (recv_bytes == 0) {
                        FD_CLR(i, &reads_init);
                        close(i);
                        printf("client %d disconnected .\n", i);
                    } else {
                        buf[recv_bytes] = '\0';
                        printf("client %d message: %s", i, buf);
                    }
                }
            }
        }
    }
    close(sock_fd);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值