常用socket相关函数

本文详细介绍了套接字的初始化、套接字选项的设置,包括SO_REUSEADDR、SO_DONTLINGER等,以及绑定、监听、接受连接等关键步骤。讲解了socketpair用于全双工本地进程间通信,强调了在不同场景下如何调整套接字参数以优化网络通信性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

初始化socket

int socket(int domain, int type, int protocol);

domain指明了协议族/域,通常AF_INET (使用IPV4协议),AF_INET6 (使用IPV6协议),PF_UNIX域的套接字可以实现同一机器上的不同进程间的通信
type是套接口类型,主要SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)
protocol一般取为0,用于制定某个协议的特定类型,即type类型中的某个类型。通常某协议中只有一种特定类型,这样protocol参数仅能设置为0。

创建一对无名的、相互连接的套接字,一般用于本地进程间通信(命名管道)
无名管道只能用于父子进程之间的通信,命名管道解决了这个局限性,他能在不相关的进程之间进行数据通信,但通常他们是半双工的。

int socketpair(int domain, int type, int protocol, int m_pipefd);
// PF_UNIX用于本地通信,一般用于本地进程间通信
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_pipefd);
assert(ret != -1);

如果函数成功,则返回0,创建好的套接字分别是m_pipefd[0]和m_pipefd[1]
这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。例如,可以往sv[0]中写,从sv[1]中读;或者从sv[1]中写,从sv[0]中读;如果往一个套接字(如sv[0])中写入后,再从该套接字读时会阻塞,只能在另一个套接字中(sv[1])上读成功

设置socket

int  setsockopt(int  s, int level, int optname, void* optval, socklen_t* optlen);

1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:

int flag=1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));

2.如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历TIME_WAIT的过程:

int flag= 0;
setsockopt(s, SOL_SOCKET, SO_DONTLINGER, &flag, sizeof(flag));

3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限:

int nNetTimeout=1000; //1秒
//发送时限
setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));
//接收时限
setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));

4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send(), recv()不断的循环收发:

// 接收缓冲区
int nRecvBuf=32*1024;  //设置为32K
setsockopt(s, SOL_SOCKET, SO_RCVBUF, (const char*)&nRecvBuf, sizeof(int));
//发送缓冲区
int nSendBuf=32*1024;//设置为32K
setsockopt(s, SOL_SOCKET, SO_SNDBUF, (const char*)&nSendBuf, sizeof(int));

5.如果在发送数据的时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响程序的性能:

int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));

6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区):

int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int));

7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:

BOOL bBroadcast=TRUE;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));

8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大)

BOOL bConditionalAccept=TRUE;
setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL));

9.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(), 如何设置让程序满足具体应用的要求(即让没发完的数据发送出去后在关闭socket)

struct linger {
	u_short l_onoff;
	u_short l_linger;
};
linger m_sLinger;
m_sLinger.l_onoff=1; 
m_sLinger.l_linger=5;//(容许逗留的时间为5秒)
setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));

在这里插入图片描述

绑定和监听

// 返回值:0 成功,-1 出错
int bind(int sockfd, const struct sockaddr* myaddr, socklen_t addrlen)

当socket函数返回一个描述符时,只是存在于其协议族的空间中,并没有分配一个具体的协议地址(这里指IPv4/IPv6和端口号的组合),bind函数可以将一组固定的地址绑定到sockfd上。
sockfd是socket函数返回的描述符;
myaddr指定了想要绑定的IP和端口号
addrlen是前面struct sockaddr的长度

// 返回值:0 成功,-1 出错
int listen(int sockfd, int backlog)

在这里插入图片描述

accept连接

int accept(int s, struct sockaddr * addr, int * addrlen)

服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;如果失败就返回 INVALID_SOCKET。该函数的第一个参数指定处于监听状态的流套接字;系统利用第二个参数来返回新创建的套接字的地址结构;利用第三个参数来返回新创建的套接字的地址结构的长度。
例如:

struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
// accept连接请求,并创建一个新的套接字
int connfd = accept(m_listenfd, (struct sockaddr *)&client_address, &client_addrlength);
if (connfd < 0)
{
	LOG_ERROR("%s:errno is:%d", "accept error", errno);
	return false;
}

socket编程基础套路

	//网络编程基础步骤
    // PF_INET和AF_INET是一样的,表示IPV4地址族,AF_INET6 则是 IPv6 
    // SOCK_STREAM表示TCP连接
    m_listenfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(m_listenfd >= 0);

    //是否优雅关闭连接
    if (0 == m_OPT_LINGER)
    {
        struct linger tmp = {0, 1};
        setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
    }
    else if (1 == m_OPT_LINGER)
    {
        struct linger tmp = {1, 1};
        setsockopt(m_listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));
    }

    int ret = 0;
    struct sockaddr_in address;
    bzero(&address, sizeof(address));    // 地址填充0
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = htonl(INADDR_ANY);
    address.sin_port = htons(m_port);

    // closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket
    int flag = 1;
    setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));

    // 当socket函数返回一个描述符时,只是存在于其协议族的空间中,
    // 并没有分配一个具体的协议地址,bind函数可以将一组固定的地址绑定到sockfd上。
    ret = bind(m_listenfd, (struct sockaddr *)&address, sizeof(address));
    assert(ret >= 0);
    ret = listen(m_listenfd, 5);
    assert(ret >= 0);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值