socket最基础的例子——别看!

一个tcp select的socket server,注意里面对select的时候和对信号的处理:

#include <stdio.h>
#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

#include "sockComm.h"

#define MAX_LISTEN_QUEUE 5
#define RECV_BUFFER_SIZE 8
#define SELECT_TIMEOUT_ITV 5

void ouch(int sig)
{
    printf("ctrl C caught!\n");
}

/* 一个基于TCP的socket server */
int main()
{
    int serverSockfd;
    int ret = -1;
    struct sockaddr_in server_in;
    struct sockaddr_in client_in[MAX_LISTEN_QUEUE];
    int newSockfd[MAX_LISTEN_QUEUE];
    char * localIp = "192.168.43.75";
    char * recvBuf = NULL;
    int recvLen;
    int sin_size = 0;
    int i = 0;

    int opt = 1;
    fd_set fdSetRead;
    struct timeval tv = {0};

    struct sigaction sact;
    sact.sa_handler = ouch;
    sigemptyset(&sact.sa_mask); //将mask清空
    sact.sa_flags = SA_RESTART; //影响系统调用被信号中断后的走向
    //sact.sa_flags = SA_RESETHAND; //处理一次后恢复默认动作。

    sigaction(SIGINT, &sact, 0);

    tv.tv_sec = SELECT_TIMEOUT_ITV;
    tv.tv_usec = 0;

    serverSockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == serverSockfd)
    {
        SOCK_ERR("create client socket error");
        return -1;
    }

    server_in.sin_family = AF_INET;
    server_in.sin_port = htons(6603);
    server_in.sin_addr.s_addr = inet_addr(localIp);
    memset(&server_in.sin_zero, 0, sizeof(server_in.sin_zero));
    //bzero(&(server_in.sin_zero), sizeof(server_in.sin_zero));

    /* linux的bug,重新建立连接会报错 */
    if (setsockopt(serverSockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
    {
        close(serverSockfd);
        SOCK_ERR("Error: Could not set reuse address option");
        return -1;
    }

    ret = bind(serverSockfd, (struct sockaddr *)&server_in, sizeof(struct sockaddr));
    if (-1 == ret)
    {
        SOCK_ERR("bind err");
        close(serverSockfd);
        return -1;
    }

    ret = listen(serverSockfd, MAX_LISTEN_QUEUE);
    if (-1 == ret)
    {
        SOCK_ERR("listen err");
        close(serverSockfd);
        return -1;
    }

    recvBuf = (char *)malloc(RECV_BUFFER_SIZE * sizeof(char));
    memset(recvBuf, 0, RECV_BUFFER_SIZE * sizeof(char));

    //while (1)
    {
        //for (i = 0; i < MAX_LISTEN_QUEUE; i++)
        {
            sin_size = sizeof(struct sockaddr);
            newSockfd[i] = accept(serverSockfd, (struct sockaddr *)&client_in[i], &sin_size);
            if (-1 == newSockfd[i])
            {
                SOCK_ERR("accept error");
            }
            else
            {
                //int flags = fcntl(newSockfd[i], F_GETFL, 0); 
                //fcntl(newSockfd[i], F_SETFL, flags | O_NONBLOCK);
                while (1)
                {
                    tv.tv_sec = SELECT_TIMEOUT_ITV;
                    tv.tv_usec = 0;
                    FD_ZERO(&fdSetRead);
                    FD_SET(newSockfd[i], &fdSetRead); //加入read监听队列
                    ret = select(newSockfd[i] + 1, &fdSetRead, NULL, NULL, &tv);
                    printf("tv.sec = %d\n", tv.tv_sec); //每次select完之后,tv被赋值为尚未等待的时长,例如设置5s,但select等了2s就返回了,那tv中保存3s。
                    if (-1 == ret)
                    {
                        SOCK_ERR("select error");
                        if (errno == EINTR) //注意,即使信号设置了SA_RESTART,select也不会restart
                        {
                            continue;
                        }
                        close(newSockfd[i]);
                        break;
                    }
                    else if (0 != ret)
                    {
                        /* 这时fdSetRead会被设置为只包含那些有数据的sockfd集合 */
                        /* 判断是不是想要的sockfd有数据了,
                        因为只要有连接上有数据select就会返回 */
                        if (!FD_ISSET(newSockfd[i], &fdSetRead))
                        {
                            continue;
                        }

                        recvLen = recv(newSockfd[i], recvBuf, RECV_BUFFER_SIZE * sizeof(char), 0);
                        if (recvLen == 0)
                        {
                            SOCK_LOG("peer closed.\n");
                            close(newSockfd[i]); //需要同时将newSockfd[i]从监听队列移除
                            continue;
                        }
                        else if (-1 == recvLen)
                        {
                            perror("recv error");
                        }
                        else
                        {
                            SOCK_LOG("%d recv: %s, len = %d\n", newSockfd[i], recvBuf, recvLen);
                        }

                        if (!strncmp("end", recvBuf, 3))
                        {
                            break;
                        }
                    }
                    else
                    {
                        SOCK_LOG("no data\n");

                        /* 如果是非阻塞(这里MSG_DONTWAIT或fcntl设置F_SETFL为O_NONBLOCK),没有数据也强收的话,会直接返回EAGAIN,即Resource temporarily unavailable */
                        recv(newSockfd[i], recvBuf, RECV_BUFFER_SIZE * sizeof(char), 0);
                    }
                    //sleep(1);
                }
                close(newSockfd[i]);
            }
        }

        //sleep(2);
    }

    free(recvBuf);
    close(serverSockfd);
    SOCK_LOG("end\n");
    return 0;
}

client端的代码:

#if 0
socket可使用的协议族的域最常用的是AF_UNIX(AF_LOCAL)和AF_INET,这些协议族在linux/socket.h中定义。

AF_UNIX的套接字地址定义成sockaddr_un结构,在sys/un.h中:
struct sockaddr_un
{
    __SOCKADDR_COMMON (sun_);
    char sun_path[108];     // Path name.
};
其中__SOCKADDR_COMMON的定义为:
#define __SOCKADDR_COMMON(sa_prefix) \
  sa_family_t sa_prefix##family
所以实际上是:
struct sockaddr_un
{
    sa_family_t sun_family;
    char sun_path[108];
};

AF_INET的套接字地址定义为sockaddr_in结构,这里的in代表internet。在linux/in.h中:
#define __SOCK_SIZE__   16      /* sizeof(struct sockaddr)  */
struct sockaddr_in {
  sa_family_t       sin_family; /* Address family       */
  __be16            sin_port;   /* Port number          */
  struct in_addr    sin_addr;   /* Internet address     */
  /* Pad to size of 'struct sockaddr', 即在后面添0直到整个结构体大小和sizeof(sockaddr)相同. */
  unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -
            sizeof(unsigned short int) - sizeof(struct in_addr)];
};

/* Internet address. */
struct in_addr {
    __be32  s_addr;
};

其他数据结构:
typedef uint32_t in_addr_t;

typedef unsigned short int sa_family_t;

struct sockaddr {
    sa_family_t sa_family;  /* address family, AF_xxx   */
    char        sa_data[14];    /* 14 bytes of protocol address */
};

有一点很重要,就是一个指向struct sockaddr_in的指针可以声明指向一个sturct sockaddr的结构。

#endif

#include <stdio.h>
#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <string.h>
#include <stdlib.h>

#include "sockComm.h"

#define MAX_LISTEN_QUEUE 5
#define SEND_BUFFER_SIZE 20

/* 一个基于TCP的socket client */
int main()
{
    int clientSockfd = -1;
    int ret = -1;
    struct sockaddr_in dest_in;
    char * destIp = "192.168.3.100";
    char * sendBuf[] = 
        {
            "hello server 000!", 
            "hello server 111!", 
            "hello server 222!", 
            "end", 
            NULL,
        };
    int i;

    clientSockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == clientSockfd)
    {
        SOCK_ERR("create client socket error");
        goto end;
    }

    dest_in.sin_family = AF_INET;
    dest_in.sin_port = htons(6603);
    dest_in.sin_addr.s_addr = inet_addr(destIp);
    memset(&dest_in.sin_zero, 0, sizeof(dest_in.sin_zero));
    //bzero(&(cin.sin_zero), sizeof(cin.sin_zero));

    ret = connect(clientSockfd, (struct sockaddr *)&dest_in, sizeof(struct sockaddr));
    if (-1 == ret)
    {
        SOCK_ERR("connect err");
        goto end;
    }

    i = 0;
    while (sendBuf[i] != NULL)
    {
        sleep(8);
        ret = send(clientSockfd, sendBuf[i], SEND_BUFFER_SIZE * sizeof(char), 0);
        if (-1 == ret)
        {
            SOCK_ERR("send err");
            goto end;
        }
        SOCK_LOG("send: %s\n", sendBuf[i]);
        i++;
    }

end:
    if (-1 != clientSockfd)
    {
        close(clientSockfd);
    }
#if 0
    if (sendBuf)
    {
        free(sendBuf);
    }
#endif
    SOCK_LOG("end\n");
    return 0;
}

sockComm.h

#ifndef __SOCK_COMM_H__
#define __SOCK_COMM_H__

#ifdef SOCK_DEBUG
#define SOCK_LOG    printf
#define SOCK_ERR    perror
#else
#define SOCK_LOG    
#define SOCK_ERR    
#endif

#endif

Makefile:

CC = gcc
LD = ld

CFLAGS += -DSOCK_DEBUG
CLI_OBJS = client_sel.o
SER_OBJS = server_sel.o

sockclient: sockclient.o
    @$(CC) $< -o $@

sockclient.o: $(CLI_OBJS)
    @$(LD) -r -o $@ $(CLI_OBJS)

%.o: %.c
    @$(CC) -c $(CFLAGS) -o $@ $<

sockserver: sockserver.o
    @$(CC) $< -o $@

sockserver.o: $(SER_OBJS)
    @$(LD) -r -o $@ $(SER_OBJS)

%.o: %.c
    @$(CC) -c $(CFLAGS) -o $@ $<

all: clean sockclient sockserver

clean:
    @rm -f *.o sockclient sockserver

使用epoll的server(client完全相同):

#include <stdio.h>
#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <sys/poll.h> //poll, pollfd
#include <netdb.h> //hostent
#include <string.h> // bzero, memset
#include <stdlib.h>
#include <sys/epoll.h> // epoll
#include <asm/fcntl.h> // for fcntl and its params

#include "sockComm.h"

#define MAX_LISTEN_QUEUE 20
#define RECV_BUFFER_SIZE 8
#define EPOLL_TIMEOUT_ITV_SEC 3
#define EPOLL_MAX_SIZE 128

int setnonblocking(int sockfd)     
{     
    if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)        
        return -1;  
    return 0;     
}    

/* 一个基于TCP的socket server */
int main()
{
    int serverSockfd;
    int ret = -1;
    struct sockaddr_in server_in;
    struct sockaddr_in client_in[MAX_LISTEN_QUEUE];
    int newSockfd[MAX_LISTEN_QUEUE];
    char * localIp = "192.168.3.100";
    char * recvBuf = NULL;
    int recvLen;
    int sin_size = 0;
    int i = 0;

    int opt = 1;
    int epfd = -1;
    struct epoll_event event, events[MAX_LISTEN_QUEUE];
    int nfds = 0;
    int n;

    serverSockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == serverSockfd)
    {
        SOCK_ERR("create client socket error");
        return -1;
    }
#if 0 //test: get localhost ip addr
    char * hostname;
    int hostnameLen;
    struct hostent * ht;

    hostname = (char *)malloc(20 * sizeof(char));
    memset(hostname, 0, 20 * sizeof(char));
    gethostname(hostname, 20);
    printf("hostname is %s\n", hostname);
    ht = gethostbyname(hostname);
    localIp = inet_ntoa (*((struct in_addr *)(ht->h_addr_list[0])));
    SOCK_LOG("server ip is %s\n", localIp);
    free(hostname);
#endif
    server_in.sin_family = AF_INET;
    server_in.sin_port = htons(6603);
    server_in.sin_addr.s_addr = inet_addr(localIp);
    //memset(&server_in.sin_zero, 0, sizeof(server_in.sin_zero));
    bzero(&(server_in.sin_zero), sizeof(server_in.sin_zero));

    /* linux的bug,重新建立连接会报错 */
    if (setsockopt(serverSockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
    {
        close(serverSockfd);
        SOCK_ERR("Error: Could not set reuse address option");
        return -1;
    }

    ret = bind(serverSockfd, (struct sockaddr *)&server_in, sizeof(struct sockaddr));
    if (-1 == ret)
    {
        SOCK_ERR("bind err");
        close(serverSockfd);
        return -1;
    }



    ret = listen(serverSockfd, MAX_LISTEN_QUEUE);
    if (-1 == ret)
    {
        SOCK_ERR("listen err");
        close(serverSockfd);
        return -1;
    }

    recvBuf = (char *)malloc(RECV_BUFFER_SIZE * sizeof(char));
    if (!recvBuf)
    {
        SOCK_ERR("malloc recvBuf err");
        close(serverSockfd);
        return -1;
    }
    memset(recvBuf, 0, RECV_BUFFER_SIZE * sizeof(char));

    //while (1)
    {
        //for (i = 0; i < MAX_LISTEN_QUEUE; i++)
        {
            sin_size = sizeof(struct sockaddr);
            newSockfd[i] = accept(serverSockfd, (struct sockaddr *)&client_in[i], &sin_size);
            if (-1 == newSockfd[i])
            {
                SOCK_ERR("accept error");
            }
            else
            {
                epfd = epoll_create(EPOLL_MAX_SIZE); /* epoll要用到一个fd。 */
                if (-1 == epfd)
                {
                    SOCK_ERR("epoll error");
                    goto end;
                }

                /* 添加fd进去。并注册要监听的事件。 */
                event.events = EPOLLIN;
                event.events &= ~EPOLLET; //默认是电平触发,所以这句话没必要。
                    event.data.fd = newSockfd[i];
                ret = epoll_ctl(epfd, EPOLL_CTL_ADD, newSockfd[i], &event);
                if (-1 == ret)
                {
                    SOCK_ERR("epoll_ctl error");
                }

                /* 开始接收数据。 */
                while (1)
                {
                    /* 监听到的fd集合以及发生的事件会记录在events中。 */
                    nfds = epoll_wait(epfd, events, MAX_LISTEN_QUEUE, EPOLL_TIMEOUT_ITV_SEC * 1000);
                    if (-1 == nfds)
                    {
                        SOCK_ERR("epoll wait error");
                        close(newSockfd[i]);
                        break;
                    }
                    else if (nfds != 0) //有监听到,nfds是fd的数量
                    {
                        for (n = 0; n < nfds; n++)
                        {
                            if (events[n].data.fd == newSockfd[i] && (events[n].events & EPOLLIN))
                            {
                                //setnonblocking(events[n].data.fd);

                                /* 如果是边沿触发的话,这里要用while保证每次都收完再回去epoll_wait。
                                不然小buffer的话可能收不完整。 */
                                /* while ( */recvLen = recv(events[n].data.fd, recvBuf, RECV_BUFFER_SIZE * sizeof(char), 0);
                                {
                                    if (-1 == recvLen)
                                    {
                                        SOCK_ERR("recv error");
                                        break;
                                    }
                                    else
                                    {
                                        SOCK_LOG("%d recv: %s, len = %d\n", events[n].data.fd, recvBuf, recvLen);
                                    }

                                    if (!strncmp("end", recvBuf, 3))
                                    {
                                        epoll_ctl(epfd, EPOLL_CTL_DEL, events[n].data.fd, NULL);
                                        //break;
                                        goto end;
                                    }   
                                }
                            }
                        }
                    }
                    else
                    {
                        SOCK_LOG("no data\n");
                    }
                    sleep(1);
                }
                close(newSockfd[i]);
            }
        }

        //sleep(2);
    }
end:
    free(recvBuf);
    close(serverSockfd);
    if (epfd != -1)
        close(epfd);
    SOCK_LOG("end\n");
    return 0;
}

一个简单UDP server:

#include <stdio.h>
#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <string.h>
#include <stdlib.h>

#include "sockComm.h"

#define MAX_LISTEN_QUEUE 5
#define RECV_BUFFER_SIZE 4

/* 一个基于UDP的socket server */
int main()
{
    int serverSockfd;
    int ret = -1;
    struct sockaddr_in server_in;
    struct sockaddr_in client_in[MAX_LISTEN_QUEUE];
    int newSockfd[MAX_LISTEN_QUEUE];
    char * localIp = "192.168.3.100";
    char * remoteIp = "192.168.3.100";
    char * recvBuf = NULL;
    int recvLen;
    int sin_size = 0;
    int i = 0;

    int opt = 1;

    serverSockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == serverSockfd)
    {
        SOCK_ERR("create client socket error.\n");
        return -1;
    }

    server_in.sin_family = AF_INET;
    server_in.sin_port = htons(6601);
    server_in.sin_addr.s_addr = inet_addr(localIp);
    memset(&server_in.sin_zero, 0, sizeof(server_in.sin_zero));
    //bzero(&(server_in.sin_zero), sizeof(server_in.sin_zero));

    /* linux的bug,重新建立连接会报错 */
    if (setsockopt(serverSockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
    {
        SOCK_ERR("Error: Could not set reuse address option");
        goto end;
    }

    ret = bind(serverSockfd, (struct sockaddr *)&server_in, sizeof(struct sockaddr));
    if (-1 == ret)
    {
        SOCK_ERR("bind err");
        goto end;
    }
#if 0
    ret = listen(serverSockfd, MAX_LISTEN_QUEUE);
    if (-1 == ret)
    {
        SOCK_ERR("listen err");
        goto end;
    }
#endif
    recvBuf = (char *)malloc(RECV_BUFFER_SIZE * sizeof(char));
    memset(recvBuf, 0, RECV_BUFFER_SIZE * sizeof(char));

    //while (1)
    {
        //for (i = 0; i < MAX_LISTEN_QUEUE; i++)
        {
            #if 0
            sin_size = sizeof(struct sockaddr);
            newSockfd[i] = accept(serverSockfd, (struct sockaddr *)&client_in[i], &sin_size);
            if (-1 == newSockfd[i])
            {
                perror("accept error.\n");
            }
            else
            #endif

            client_in[i].sin_family = AF_INET;
            client_in[i].sin_port = htons(6601);
            client_in[i].sin_addr.s_addr = inet_addr(remoteIp);
            memset(&client_in[i].sin_zero, 0, sizeof(client_in[i].sin_zero));

            {
                sin_size = sizeof(struct sockaddr);
                recvLen = recvfrom(serverSockfd, recvBuf, RECV_BUFFER_SIZE * sizeof(char), 0,
                    (struct sockaddr *)&client_in[i], &sin_size);
                if (-1 == recvLen)
                {
                    SOCK_ERR("recv error");
                    goto end;
                }
                else
                {
                    SOCK_LOG("recv: %s, len = %d\n", recvBuf, recvLen);
                }
            }
        }

        //sleep(2);
    }
end:
    if (-1 != serverSockfd)
    {
        close(serverSockfd);
    }
    if (recvBuf)
    {
        free(recvBuf);
    }

    SOCK_LOG("end\n");
    return 0;
}

UDP client:

#if 0
socket可使用的协议族的域最常用的是AF_UNIX(AF_LOCAL)和AF_INET,这些协议族在linux/socket.h中定义。

AF_UNIX的套接字地址定义成sockaddr_un结构,在sys/un.h中:
struct sockaddr_un
{
    __SOCKADDR_COMMON (sun_);
    char sun_path[108];     // Path name.
};
其中__SOCKADDR_COMMON的定义为:
#define __SOCKADDR_COMMON(sa_prefix) \
  sa_family_t sa_prefix##family
所以实际上是:
struct sockaddr_un
{
    sa_family_t sun_family;
    char sun_path[108];
};

AF_INET的套接字地址定义为sockaddr_in结构,这里的in代表internet。在linux/in.h中:
#define __SOCK_SIZE__   16      /* sizeof(struct sockaddr)  */
struct sockaddr_in {
  sa_family_t       sin_family; /* Address family       */
  __be16            sin_port;   /* Port number          */
  struct in_addr    sin_addr;   /* Internet address     */
  /* Pad to size of 'struct sockaddr', 即在后面添0直到整个结构体大小和sizeof(sockaddr)相同. */
  unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -
            sizeof(unsigned short int) - sizeof(struct in_addr)];
};

/* Internet address. */
struct in_addr {
    __be32  s_addr;
};

其他数据结构:
typedef uint32_t in_addr_t;

typedef unsigned short int sa_family_t;

struct sockaddr {
    sa_family_t sa_family;  /* address family, AF_xxx   */
    char        sa_data[14];    /* 14 bytes of protocol address */
};

有一点很重要,就是一个指向struct sockaddr_in的指针可以声明指向一个sturct sockaddr的结构。

#endif

#include <stdio.h>
#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <string.h>
#include <stdlib.h>

#include "sockComm.h"

#define MAX_LISTEN_QUEUE 5
#define SEND_BUFFER_SIZE 20

/* 一个基于UDP的socket client */
int main()
{
    int clientSockfd = -1;
    int ret = -1;
    struct sockaddr_in dest_in;
    char * destIp = "192.168.3.100";
    char * sendBuf = "hello server!";

    clientSockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == clientSockfd)
    {
        SOCK_ERR("create client socket error");
        goto end;
    }

    dest_in.sin_family = AF_INET;
    dest_in.sin_port = htons(6601);
    dest_in.sin_addr.s_addr = inet_addr(destIp);
    memset(&dest_in.sin_zero, 0, sizeof(dest_in.sin_zero));
    //bzero(&(cin.sin_zero), sizeof(cin.sin_zero));
#if 0
    ret = connect(clientSockfd, (struct sockaddr *)&dest_in, sizeof(struct sockaddr));
    if (-1 == ret)
    {
        SOCK_ERR("connect err.\n");
        goto end;
    }
#endif
    ret = sendto(clientSockfd, sendBuf, SEND_BUFFER_SIZE * sizeof(char), 0,
            (struct sockaddr *)&dest_in, sizeof(struct sockaddr));
    if (-1 == ret)
    {
        SOCK_ERR("send err");
        goto end;
    }
    SOCK_LOG("send: %s\n", sendBuf);

end:
    if (-1 != clientSockfd)
    {
        close(clientSockfd);
    }
#if 0
    if (sendBuf)
    {
        free(sendBuf);
    }
#endif
    SOCK_LOG("end\n");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值