初始化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);