socket单连接服务器

socket单连接服务器

首先来了解一下,单连接socekt服务器用到的posix标准函数

int socket(int domain, int type, int protocol);
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
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);
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
使用以上函数时有下面两种方式判断是否执行成功,例如使用socket:
1、使用如下方式,需要注意‘=’的优先级比 < 低,因此需要加()
if((socket_fd = socket(domain,type,protocol)) < 0)
{
  perror();
}
2、推荐此种使用方式,直观不易出错
socket_fd = socket(domain,type,protocol);
if(socket_fd < 0)
{
    perror();
}

具体代码如下


#define ethernet_DATA_MAX_LEN   5000
#define ethernet_PORT   1002

static bool haveConnectFlag;
void socket_init(void);
void* ethernet_recv_thread_handle(void *arg);
int ethernet_msg_send(uint8_t *buf);


int main()
{
    socket_init();
}
void socket_init(void)
{
    pthread_t ethernet_recv_thread; 
    /*初始化socket,如果不成功间隔1s一直初始化*/
    while(1)
    {
        sleep(1);
        /*创建socket套接字*/
        socket_ethernet_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(socket_ethernet_fd < 0)
        {
            printf("socket error: %s(errno: %d)", strerror(errno), errno);
            close(socket_ethernet_fd);
            continue;
        }

        memset(&socketaddr, 0, sizeof(socketaddr));
        socketaddr.sin_family = AF_INET;
        socketaddr.sin_port = htons(ethernet_PORT);
        socketaddr.sin_addr.s_addr = htonl(INADDR_ANY);

        getsockopt(socket_ethernet_fd, IPPROTO_TCP, TCP_MAXSEG, &mss, (socklen_t *)&mssLen);
        printf("server initial max MSS is %d.\r\n", mss);
        
        /*设置socket_ethernet_fd套接字的选项值,使下一次使用ip和端口号不需要等待*/
        ret = setsockopt(socket_ethernet_fd, SOL_SOCKET, SO_REUSEADDR, &ReUseOn, sizeof(ReUseOn));
        if(ret < 0)
        {
            printf("setsockopt error: %s(errno: %d)", strerror(errno), errno);
            close(socket_ethernet_fd);
            continue;
        }

        /*绑定TCP协议族、IP和端口号*/
        ret = bind(socket_ethernet_fd, (struct sockaddr*)&socketaddr, sizeof(socketaddr));
        if(ret < 0)
        {
            printf("bind error: %s(errno: %d)", strerror(errno), errno);
            close(socket_ethernet_fd);
            continue;
        }

        /*监听socket_ethernet_fd套接字*/
        ret = listen(socket_ethernet_fd, 10);
        if(ret < 0)
        {
            printf("listen error: %s(errno: %d)", strerror(errno), errno);
            close(socket_ethernet_fd);
            continue;
        }
        else
        {
            break;
        }
    }
    
    /*创建接收线程*/
    pthread_create(&ethernet_recv_thread, NULL, ethernet_recv_thread_handle, NULL);
}

void ethernet_recv_thread_handle(void *arg)
{
    uint8_t buf[ethernet_DATA_MAX_LEN] = {0};
    ssize_t recv_len;
    int mss = 0, mssLen = sizeof(mss);
    int keepalive = 1;      //enable keepalive
    int keepidle = 3;       //idle time,will send explore data after this time 
    int keepinterval = 1;   //send interval
    int keepcount = 5;      //send times
    int ret = -1;
    
    /*最外层while用于处理连接*/
    while(1)
    {
        haveConnectFlag = false;
        printf("waiting ethernet socket accept");
        ethernet_cnnt_fd = accept(socket_ethernet_fd, (struct sockaddr*)NULL, NULL);
        if(ethernet_cnnt_fd < 0)
        {
            printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
            continue;
        }
        haveConnectFlag = true;

        getsockopt(socket_ethernet_fd, IPPROTO_TCP, TCP_MAXSEG, &mss, (socklen_t *)&mssLen);
        printf("client max MSS is %d.\r\n", mss);

        /*设置TCP心跳选项值*/
        ret = setsockopt(ethernet_cnnt_fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
        if(ret < 0)
        {
            printf("SO_KEEPALIVE set error:%s(errno: %d)", strerror(errno), errno);
        }

        ret = setsockopt(ethernet_cnnt_fd, SOL_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
        if(ret < 0)
        {
            printf("TCP_KEEPIDLE set error:%s(errno: %d)", strerror(errno), errno);
        }

        ret = setsockopt(ethernet_cnnt_fd, SOL_TCP, TCP_KEEPINTVL, &keepinterval, sizeof(keepinterval));
        if(ret < 0)
        {
            printf("TCP_KEEPINTVL set error:%s(errno: %d)", strerror(errno), errno);
        }

        ret = setsockopt(ethernet_cnnt_fd, SOL_TCP, TCP_KEEPCNT, &keepcount, sizeof(keepcount));
        if(ret < 0)
        {
            printf("TCP_KEEPCNT set error:%s(errno: %d)", strerror(errno), errno);
        }
        
        /*此while用于处理连接中,接收数据*/
        while(1)
        {
            ethernet_recv_time_pre = time(NULL);
            printf("waiting ethernet socket recv");
            recv_len = recv(ethernet_cnnt_fd, buf, sizeof(buf),0);
            if(recv_len > 0)
            {
                printf("recv data:%s\n",buf);
            }
            else if(recv_len == 0)
            {
                ethernet_datatype = 0;
                printf("close ethernet_cnnt_fd");
                close(ethernet_cnnt_fd);
                break;
            }
            else//error
            {
                ethernet_datatype = 0;
                printf("ethernet recv error:%s(errno: %d)", strerror(errno), errno);
                close(ethernet_cnnt_fd);
                break;
            }
        }
    }
}

int ethernet_msg_send(uint8_t *buf)
{
    int ret = -1;
    /*在有连接情况下,发送数据给客户端*/
    if(true == haveConnectFlag)
    {
        ret = send(ethernet_cnnt_fd, buf, len, 0);
        printf("ethernet send msg! len = %d\n",len);
    }
    else
    {
        printf("no ethernet connect,can`t send msg!");
    }
    return ret; 
}

注意:
1、socket是基于TCP流传输的,在实际传输数据过程中会出现粘包或者分段等情况,需要根据应用层数据协议来处理粘包或者分段;
2、查看指定端口号在当前系统中使用情况:netstat -an | grep 端口号;
3、以上代码均未写头文件,读者可自行在man手册中找到函数原型和头文件,读者也可进一步了解函数手册;

posted @ 2020-09-29 14:04  lightwear  阅读(274)  评论(0)    收藏  举报