int m_socket;
// Open a socket
m_socket = socket(AF_INET, SOCK_STREAM, 0);
第一个参数domain说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等).
AF_UNIX只能够用于单一的Unix系统进程间通信, 而AF_INET是针对Internet的, 因而可以允许在远程主机之间通信.
VxWorks套接字仅支持Internet域地址族, 不支持UNIX域地址族. 因此在需要domain参数的函数中, 使用AF_INET作为函数参数值.
第二个参数type说明我们网络程序所采用的通讯协议(SOCK_STREAM, SOCK_DGRAM等). SOCK_STREAM表明我们用的是TCP协议, 这样会提供按顺序的,可靠,双向,面向连接的比特流. SOCK_DGRAM 表明我们用的是UDP协议, 这样只会提供定长的,不可靠,无连接的通信.
此外,还有SOCK_RAW代表是原始协议套接字.
第三个参数protocol, 由于我们指定了type, 所以这个地方我们一般只要用0来代替就可以了.
socket为网络通讯做基本的准备, 成功打开则返回一个套接字描述符, 如果失败则返回ERROR.套接字描述符是一个标准的I/O系统文件描述符(fd, file descriptor), 可以被close(), read(), write()和ioctl()函数使用.
// Make the socket sending alive messages when connected
int flag = 1;
setsockopt(m_socket, SOL_SOCKET, SO_KEEPALIVE, (char*)&flag, sizeof(flag));
// increase receive buffer size
flag = SOCKET_BUF_SIZE;
setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)&flag, sizeof(flag));
// increase send buffer size
setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char*)&flag, sizeof(flag));
// try without the nagle algorithm
flag = 1;
setsockopt (m_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof (flag));
// Prevent "bind error 48"!
flag = 1;
setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(flag));
SOCK_STREAM型套接字支持SO_KEEPALIVE选项,SOCK_DGRAM型套接字不支持。缺省没有启用保活机制。基本套接口选项SO_KEEPALIVE用于检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方发一个保持存活探测分节(keep alive probe)。这是一个对方必须响应的TCP分节. 它会导致以下三种情况:对方接收一切正常:以期望的ACK响应。2小时后,TCP将发出另一个探测分节。对方已崩溃且已重新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接口本身则被关闭。对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图得到一个响应。在发出第一个探测分节11分钟15秒后若仍无响应就放弃。套接口的等待处理错误被置为ETIMEOUT,套接口本身则被关闭。如ICMP错误是"host unreachable(主机不可达)",说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为EHOSTUNREACH。
IPPROTO_TCP: TCP套接口
尽管有许多TCP选项可供程序员操作,而我们却最关注如何处置其中的两个选项,它们是TCP_NODELAY和 TCP_CORK,这两个选项都对网络连接的行为具有重要的作用。
TCP_NODELAY和TCP_CORK基本上控制了包的"Nagle化",Nagle化在这里的含义是采用Nagle算法把较小的包组装为更大的帧。现在让我们假设某个应用程序发出了一个请求,希望发送小块数据。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。如果我们马上发送数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。例如,当我们正在发送一个较短的请求并且等候较大的响应时,相关过载与传输的数据总量相比就会比较低,而且,如果请求立即发出那么响应时间也会快一些。以上操作可以通过设置套接字的TCP_NODELAY选项来完成,这样就禁用了Nagle算法。
SO_REUSEADDR选项的设置将套接口设置成重新使用旧的地址(IP地址加端口号)而不等待.
如果一个socket绑定了某个端口,该socket正常关闭或程序退出后,在一段时间内该端口依然保持被绑定的状态,其他程序(或者重新启动的原程序)无法绑定该端口。这由 TCP 套接字状态TIME_WAIT引起。该状态在套接字关闭后约保留 2 到 4 分钟。在TIME_WAIT状态退出之后,套接字被删除,该地址才能被重新绑定而不出问题。等待TIME_WAIT结束可能是令人恼火的一件事,特别是如果您正在开发一个套接字服务器,就需要停止服务器来做一些改动,然后重启。幸运的是,有方法可以避开TIME_WAIT状态。可以给套接字应用SO_REUSEADDR套接字选项,以便端口可以马上重用。
// Fill in the address descriptor
struct sockaddr_in addr; // 服务器的地址
bzero((char*)&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(Port); // 默认端口
addr.sin_addr.s_addr = inet_addr(IpAddress);
// Bind the socket to the address
bind(m_socket, (struct sockaddr*)&addr, sizeof(addr));
// Listen for a client to connect (max n simultaneously)
listen(m_socket, n);
在使用TCP协议时,一般服务端任务先使用socket调用得到一个描述符,然后使用bind调用将一个名字与socket描述符连接起来,对于Internet域就是将Internet地址绑定到socket.
之后,服务端使用listen调用指出最大的被拒绝连接次数。然后就可以使用accept调用等待客户端发起连接。
bind函数用于给套接字分配名字。该函数分配一个网络地址(也称为"名字")给指定的套接字,以便其他处理可以连接它或给它发送数据。当使用socket()创建一个套接字时,它属于一个地址族但并没有分配名字。
listen函数允许连接套接字,并指定最大的被拒绝连接次数。当用listen()允许连接后,通过accept()函数接受连接。
一般服务端可能在accept返回后创建一个新的任务进行与客户的通信,或者直接创建一个新的任务来调用accept,原来的任务则继续等待另一个连接。
评论
发表评论