博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
socket网络编程实践要点
阅读量:6540 次
发布时间:2019-06-24

本文共 10344 字,大约阅读时间需要 34 分钟。

1、创建udp的socket句柄

// 当host_port为0时,则表示让操作系统自动分配bool createUdpSocket(string host_ip,unsigned short host_port, int& sock_fd){    sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);    if(sock_fd <= 0)    {        return false;    }    struct sockaddr_in client_addr= {
0}; inet_pton(AF_INET,host_ip.c_str(), &(client_addr.sin_addr)); client_addr.sin_port =htons(host_port); client_addr.sin_family = AF_INET; if(::bind(sock_fd, (struct sockaddr*)&client_addr, sizeof(struct sockaddr_in))== -1) { close(sock_fd); sock_fd = -1; return false; } //获取操作系统分配的端口 struct sockaddr_storage sock_addr; socklen_t addr_size = sizeof(struct sockaddr_storage); getsockname(sock_fd, (struct sockaddr*)&sock_addr, &addr_size); sockaddr_in sin ; memcpy(&sin,&sock_addr,addr_size); host_ip = inet_ntoa(sin.sin_addr); host_port = ntohs(sin.sin_port); // 设置socket为非阻塞#ifdef WIN32 unsigned long arg = 1; ioctlsocket(sock_fd, FIONBIO, &arg) ; ioctlsocket(sock_fd, FIONBIO, &arg) ;#endif#ifdef LINUX int arg = fcntl(sock_fd, F_GETFL, 0); fcntl(sock_fd, F_SETFL, arg | O_NONBLOCK);#endif return true;}

 

2、发送udp数据包

int sendUdpData(int socket_fd,char* buff,int buff_len,struct sockaddr* dest_addr){    int count = 0 ;    ssize_t send_len = 0;    do    {        if(count > 0)        {            sleep_ms(5*count);        }        //考虑网络不好时,需要尝试发送多次        send_len = sendto(socket_fd, buff, ssize_t(buff_len), 0, dest_addr, sizeof(struct sockaddr));        count++;    }while(send_len <0 && errno == EAGAIN && count<=5);    return int(send_len);}

 

3、发送udp广播包

int sendBroadUdpData(int sock_fd,char* buff,int buff_len){    // 将端口设置为允许广播包    int broadcast = 1;#ifdef WIN32    setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast, sizeof(broadcast));#else    setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast));#endif    struct sockaddr_in svr_addr;    memset(&svr_addr, 0, sizeof(svr_addr));    svr_addr.sin_family = AF_INET;    svr_addr.sin_port = htons(BROAD_CAST_PORT);    int server_ip;    inet_pton(AF_INET, "255.255.255.255", (void *)&server_ip);    svr_addr.sin_addr.s_addr = server_ip;        sendUdpData(sock_fd, buff, buff_len, (struct sockaddr*)&svr_addr);}

 

4、接收udp的数据包

利用poll或epoll模型,当某个socket_fd有数据可读时,即可返回进行相应的处理

{    struct sockaddr_in client_addr;    int len = sizeof(struct sockaddr);    char recv_buff[1024];    int recv_len = 0;            recv_len = recvfrom(socket_fd, recv_buff, sizeof(recv_buff), 0, (struct sockaddr *)&client_addr, (socklen_t *)&len)    if (recv_len > 0)    {        processMsg(recv_buff, recv_len, client_addr);    }}

 

5、poll模型的构建,同时监控多个fd

{    int maxCount = 20;#ifdef LINUX    struct pollfd wait_fd[maxCount];#endif#ifdef WIN32    WSAPOLLFD wait_fd[maxCount];#endif    int real_count = 0;    listen_fds.clear();    GetListenFd(listen_fds);    for (int index = 0; index < (int)listen_fds.size(); index++)    {        wait_fd[real_count].fd = listen_fds[index];        wait_fd[real_count].events = POLLIN | POLLOUT;        real_count++;    }#ifdef LINUX    int res = ::poll(wait_fd, real_count, 100); //100毫秒超时#endif#ifdef WIN32    int res = WSAPoll(wait_fd, real_count, 100);#endif    if (res == -1)    {        usleep(10000);    }    else if (res)    {        int current_fd;        for (int index = 0; index < real_count; index++)        {            current_fd = wait_fd[index].fd;            if ((wait_fd[index].revents & POLLIN) > 0)            {                recvfrom(current_fd, recv_buff, sizeof(recv_buff), 0, (struct sockaddr *)&client_addr, (socklen_t *)&alen);                bool is_listen_fd = false; //是否是监听句柄                for (int pos = 0; pos < (int)listen_fds.size(); pos++)                {                    if (current_fd == listen_fds[pos])                    {                        is_listen_fd = true;                        break;                    }                }            }            if ((wait_fd[index].revents & POLLERR) > 0)                                usleep(10000);            }        }    }    else    {        printf("time out.\n");    }    }

 

struct sockaddr_in addr;socklen_t addr_len = sizeof (struct sockaddr_in);if ((len = recvfrom(sock, buff, len, 0, (struct sockaddr_in*)&addr, &addr_len)) < 0){    if (errno == EAGAIN || errno == EINTR)    {        len = 0;    }}addr_ip = addr.sin_addr.s_addr;addr_port = ntohs(addr.sin_port);

 

6、创建tcp句柄并监听客户端的连接请求

bool createTcpSocket(string host_ip,unsigned short host_port, int& sock_fd){    int new_sockfd = -1;    struct sockaddr_in svr_addr = {
0}; new_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //建立TCP套接字 if (new_sockfd == -1) { return false; } struct in_addr ip_addr; inet_aton(host_ip, &ip_addr) svr_addr.sin_addr.s_addr = ip_addr.s_addr; svr_addr.sin_port = htons(host_port); svr_addr.sin_family = AF_INET; if (bind(new_sockfd, (struct sockaddr *)&svr_addr, sizeof(struct sockaddr_in)) == -1) { close(new_sockfd); return false; } int recv_buf_size = 1024 * 10; if (setsockopt(new_sockfd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buf_size, sizeof(recv_buf_size)) != 0) { printf("setsockopt SO_RCVBUF error.\n "); } int send_buf_size = 1024 * 10; if (setsockopt(new_sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&send_buf_size, sizeof(send_buf_size)) != 0) { printf("setsockopt SO_SNDBUF error.\n "); } // 设置非阻塞 int val = fcntl(new_sockfd, F_GETFL, 0); if (fcntl(new_sockfd, F_SETFL, val|O_NONBLOCK) == -1) { return false; } // 设置句柄重用 int reuse = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) != 0) { return false; } if (new_sockfd > 0) { sock_fd = new_sockfd; if (listen(sock_fd, 1024) < 0) { close(sock_fd); sock_fd = -1; return false; } return true; } else { return false; }}

 

7、接受客户端的tcp连接请求

int OnRecv(){    int length = sizeof(struct sockaddr_in);    int clientfd = accept(sock_fd,(struct sockaddr *)&client_addr,(socklen_t*)&length);    if ( clientfd <= 0 )    {        return -1;    }    unsigned int clientip = client_addr.sin_addr.s_addr;        // 设置非阻塞    int val = fcntl(clientfd, F_GETFL, 0);    if (fcntl(clientfd, F_SETFL, val|O_NONBLOCK) == -1)    {        return false;    }        //设置客户端发送和接收缓冲区大小    int buff_size = 10 * 1024;    setsockopt(clientfd, SOL_SOCKET, SO_RCVBUF, (char*)&buff_size, sizeof(buff_size));    setsockopt(clientfd, SOL_SOCKET, SO_SNDBUF, (char*)&buff_size, sizeof(buff_size));        MsgHandler* msg_handler = new MsgHandler(clientfd); // 在msg_handler对象中接收该句柄发送的具体数据    }

 

8、接收客户端的具体数据

int MsgHandler::OnRecv(){    char recv_buff[10*1024];    memset(recv_buff, 0x00, sizeof(recv_buff));    int recv_len = 0;    do    {        recv_len = recv(clientfd, recv_buff, sizeof(recv_buff)-1, 0);    }while(recv_len < 0 && errno == EINTR);        if (recv_len <= 0)    {        if (recv_len == 0)        {            printf("socket closed by client.\n");            delete this;            return 0;        }    }    else if (errno == EAGAIN)    {        return 0; //暂时阻塞    }    else    {        delete this;        return 0;    }    return 0;}//本地维护一个接收缓冲区    #define MAX_BUFF_LEN (10 * 1024 * 1024)    recv_buff = new char[MAX_BUFF_LEN];    start_buff = recv_buff;    memmove(recv_buff,start_buff,data_len); //移动数据位置到缓冲区的头部    start_buff = recv_buff ;

 

9、利用tcp来发送消息

struct SendMsg{    unsigned int send; //消息已经发送的长度    string data;    SendMsg()    {        send = 0;        data.clear();    }    SemdMsg(char* msg, int msg_len):data(msg, msg_len), send(0)    {            }    void reset(){send = 0;}}queue
msg_queue;while (!msg_queue.empty()){ SendMsg& msg = msg_queue.front(); int msg_len = msg.data.size() - send; // 发送数据,保证需要发送的数据全部发送完 int send_len = SendTCPMsg((char*)msg.data.c_str()+msg.send, msg_len); if (send_len != msg_len) { msg_queue.front().send += send_len; break; } msg_queue.pop();}int SendTcpMsg(char* buff, int len){ int ret; int _snd=0; int _len = len; errno = 0; while(_len>0) { ret = send(_sock_fd,(void*)((char*)buff+_snd),_len,0); if ( ret <= 0 ) { if ( errno == EINTR || errno == EAGAIN ) { usleep(5); continue; } break; } _len -= ret; _snd += ret; } return _snd;}

 

10、客户端创建socket并发起连接请求

int ClientSocketConnect(){    sock_status = SOCK_CLOSED;    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);        //地址复用    int reuse = 1;    setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));    // 非阻塞    int val = fcntl(sock_fd, F_GETFL, 0);    fcntl(sock_fd, F_SETFL, val | O_NONBLOCK) ;    //建立连接    struct sockaddr_in recvAddr;    memset(&recvAddr, 0, sizeof(recvAddr));    recvAddr.sin_family = AF_INET;    recvAddr.sin_addr.s_addr = inet_addr(server_ip.c_str());    recvAddr.sin_port = htons(server_port);    struct timeval tv;    tv.tv_sec = 3 ;     tv.tv_usec = 0  ;    fd_set fd_write ;    int error = -1 ;    int len = sizeof(int);    // 连接服务器    int ret_code = connect(sock_fd, (sockaddr *)&recvAddr, sizeof(sockaddr));    if (ret_code < 0)    {        FD_ZERO(&fd_write);        FD_SET(sock_fd, &fd_write );        if(select(sock_fd+1, NULL , &fd_write, NULL, &tv) > 0)        {            if( FD_ISSET(sock_fd,&fd_write) )            {                getsockopt(_sock_fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);                if(error == 0)                {                    sock_status = SOCK_CONNECTED;                }                else                {                    sock_status = SOCK_CLOSED;                }            }        }    }    else    {        sock_status = SOCK_CONNECTED;    }    if(sock_status == SOCK_CLOSED)    {        close(_sock_fd);        sock_fd = -1 ;        return -1;    }    else    {        int buff_size = 100*1024;        setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (char*)&buff_size, sizeof(buff_size));        setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (char*)&buff_size, sizeof(buff_size));    }        //开始发送和接收数据    }

 

转载于:https://www.cnblogs.com/share-ideas/p/10886689.html

你可能感兴趣的文章
程序员常用借口指南
查看>>
关于PXE网络安装linux系统中碰到的个别问题
查看>>
awk 常用方法
查看>>
Android网络框架实现之【Retrofit+RxJava】
查看>>
Android文件的加密与解密
查看>>
SOAP webserivce 和 RESTful webservice 对比及区别
查看>>
【原】记录一句话
查看>>
Android标题栏,状态栏
查看>>
三数中值快速排序(长度小于3的数组转插入排序)
查看>>
Windows下安装Memcached for PHP
查看>>
hdu 1040 As Easy As A+B
查看>>
java笔记:SpringSecurity应用(二)
查看>>
vim命令
查看>>
php记录代码执行时间
查看>>
【C】strcpy()需谨慎使用;
查看>>
用Adobe Flash Professional CS6创建一个iOS应用程序
查看>>
简简单单几段代码让自己变成最合格的网站管理员
查看>>
Slim Text 0.0.9 发布, 代码开源!
查看>>
[置顶] 遵循Java EE标准体系的开源GIS服务平台之二:平台部署
查看>>
Session深度探索
查看>>