C/C++网络编程在windows和linux中将socket设置为阻塞和非阻塞

在 socket编程中,对于socket的读写默认都是阻塞的,但有的情况我们需要将其设置为非阻塞,比如做多路复用,或者通过select实现连接超时等功能,将socket设置为非阻塞,在windows和linux中的接口有所不同,在windows中使用ioctlsocket函数,在linux中使用 fcntl函数,下面我们做一个跨平台设置阻塞的函数SetBlock。

bool SetBlock(int sock,bool isblock)
{
	int re = 0;
//通过宏区分windows和linux,如果是windows64位程序判断 _WIN64宏
#ifdef WIN32
	unsigned long ul = 0;
	if(!isblock) ul = 1;
	re = ioctlsocket(sock, FIONBIO, (unsigned long*)&ul);
#else
	//先取到现有描述符属性,保证本次更改不变动原有属性
	int flags = fcntl(sock, F_GETFL, 0);
	if (flags < 0) {
		return false;
	}
	if(isblock)
	{
		flags = flags & ~O_NONBLOCK;
	}
	else
	{
		flags = flags | O_NONBLOCK;
	}
	re = fcntl(sock, F_SETFL, flags);
#endif
	if (re != 0) return false;
	return true;
}

linux 服务端:


        cpy_reads = reads;
        timeout.tv_sec = 5;
        timeout.tv_usec = 5000;

        //监听服务端套接字和与客服端连接的服务端套接字的read事件
        if ((fd_num = select(fd_max + 1, &cpy_reads, 0, 0, &timeout)) == -1)
            break;
        if(fd_num == 0)
            continue;

        if (FD_ISSET(serv_sock, &cpy_reads))//受理客服端连接请求
        {
            adr_sz = sizeof(clnt_adr);
            clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz);
            FD_SET(clnt_sock, &reads);
            if(fd_max < clnt_sock)
                fd_max = clnt_sock;
            printf("connected client: %d \n", clnt_sock);
        }
        else//转发客服端数据
        {
            str_len = read(clnt_sock, buf, BUF_SIZE);
            if (str_len == 0)//客服端发送的退出EOF
            {
                FD_CLR(clnt_sock, &reads);
                close(clnt_sock);
                printf("closed client: %d \n", clnt_sock);
            }
            else
            {
                //接收数据为字符串时执行回声服务
                write(clnt_sock, buf, str_len);
            }
        }
    }

    close(serv_sock);
    return 0;
}


void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}    

客户端:

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

#define BUF_SIZE 1024
void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

int main(int argc, const char * argv[]) {
    int sock;
    char message[BUF_SIZE];
    int str_len, recv_len, recv_cnt;
    struct sockaddr_in serv_adr;

    if(argc != 3)
    {
        printf("Usage: %s <IP> <port> \n", argv[0]);
        exit(1);
    }

    sock = socket(PF_INET, SOCK_STREAM, 0);
    if(sock == -1)
        error_handling("socket() error");

    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
    serv_adr.sin_port = htons(atoi(argv[2]));

    if (connect(sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1)
        error_handling("connect() error");
    else
        puts("Connected ...............");

    while (1) {
        fputs("Input message(Q to quit): ", stdout);
        fgets(message, BUF_SIZE, stdin);
        if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
            break;

        str_len = write(sock, message, strlen(message));

        /*这里需要循环读取,因为TCP没有数据边界,不循环读取可能出现一个字符串一次发送
         但分多次读取而导致输出字符串不完整*/
        recv_len = 0;
        while (recv_len < str_len) {
            recv_cnt = read(sock, &message[recv_len], BUF_SIZE - 1);
            if(recv_cnt == -1)
                error_handling("read() error");
            recv_len += recv_cnt;
        }
        message[recv_len] = 0;
        printf("Message from server: %s", message);
    }

    close(sock);
    return 0;
}

windows
服务端:

#include<iostream>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#define bufsize 1024
using namespace std;
void main() {
    WSADATA wsadata;
    SOCKET serverSocket,clientSocket;
    int szClientAddr,fdnum,str_len;
    SOCKADDR_IN  serverAddr, clientAddr;
    fd_set reads, cpyReads;
    TIMEVAL timeout;
    char message[bufsize] = "\0";
 
    if(WSAStartup(MAKEWORD(2, 2), &wsadata)!=0)
        cout<<"WSAStartup() error"<<endl;
 
    serverSocket = socket(PF_INET, SOCK_STREAM, 0);
    if(serverSocket == INVALID_SOCKET)
        cout<<"socket()  error"<<endl;
 
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(9999);
 
    if (bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
        cout << "bind () error" << endl;
 
    listen(serverSocket, 5);
    cout << "服务器启动成功!" << endl;
 
    FD_ZERO(&reads);  //所有初始化为0
    FD_SET(serverSocket, &reads);  //将服务器套接字存入
 
    while (1) {
        cpyReads = reads;   
        timeout.tv_sec = 5;      //5秒
        timeout.tv_usec = 5000;   //5000毫秒
 
        //找出监听中发出请求的套接字
        if ((fdnum = select(0, &cpyReads, 0, 0, &timeout)) == SOCKET_ERROR)
            break;
        if (fdnum == 0) {
            cout << "time out!" << endl; 
            continue;
        }
        for (unsigned int i = 0; i < reads.fd_count; i++) {
            if (FD_ISSET(reads.fd_array[i], &cpyReads)) { //判断是否为发出请求的套接字
                if (reads.fd_array[i] == serverSocket) {  //是否为服务器套接字
                    szClientAddr = sizeof(clientAddr);
                    clientSocket = accept(serverSocket, (SOCKADDR*)&clientAddr, &szClientAddr);
                    if (clientSocket == INVALID_SOCKET)  cout << "accept() error" << endl;
                    FD_SET(clientSocket, &reads);
                    cout << "连接的客户端是:" << clientSocket << endl;
                }
                else {//否  就是客户端
                    str_len = recv(reads.fd_array[i], message, bufsize - 1, 0);
                    if (str_len == 0) {//根据接受数据的大小 判断是否是关闭
                        FD_CLR(reads.fd_array[i], &reads);  //清除数组中该套接字
                        closesocket(cpyReads.fd_array[i]);
                        cout << "关闭的客户端是:" << cpyReads.fd_array[i] << endl;
                    }
                    else {
                        send(reads.fd_array[i], message, str_len, 0);
                    }
                }
            }
        }    
    }
    closesocket(clientSocket);
    closesocket(serverSocket);
    WSACleanup();
}

客户端:

#include<iostream>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#define bufsize 1024
using namespace std;
void main() {
    WSADATA wsadata;
    SOCKET clientSocket;
    SOCKADDR_IN  serverAddr;
    int  recvCnt;
 
    char message[bufsize] = "\0";
    if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
        cout << "WSAStartup() error" << endl;
 
    if ((clientSocket = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
        cout << "socket()  error" << endl;
 
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serverAddr.sin_port = htons(9999);
 
    if(connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr))==SOCKET_ERROR)
        cout<<"connect() error"<<endl;
 
    while (1) {
        cout << "输入Q或q退出:";
        cin >> message;
        if (!strcmp(message, "Q") || !strcmp(message, "q")) break;
        send(clientSocket, message, strlen(message), 0);
        memset(message, 0, sizeof(message));
        recv(clientSocket, message, bufsize, 0);
        cout << "服务器结果:" << message << endl;
    }
    closesocket(clientSocket);
    WSACleanup();
}

参考博客:https://blog.csdn.net/zl908760230/article/details/70257229

参考博客:https://blog.csdn.net/hshl1214/article/details/45872243

参考博客:https://blog.csdn.net/u010223072/article/details/48133725
参考博客:https://cloud.tencent.com/developer/article/1350701

参考书籍:《TCP/IP 网络编程 --尹圣雨》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值