socket函数详解 (有了新的认识)
我们先来看一下socket函数的原型:
SOCKET PASCAL FAR socket (int af, int type, int protocol);
典型的调用方式为:
unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0);
1.af是address family的缩写,实际上就是指明域domain, 这个af主要是用来区分是创建ipv4的套接字还是ipv6的套接字。 当然, 顺便说一下, 如果是在unix中, 第一个参数还可以是AF_UNIX, 表示这个socket既不是ipv4的socket, 也不是ipv6的socket, 而是非网络形式的unix域socket, 可以用来进行非网络形式的进程间通信。 在很多嵌入式系统中, 进程间的通信均是通过非网络形式的unix域套接字来完成的。不过, 我们要明白, unix域套接字的客户端和服务端进行通信时, 客户端和服务端必须位于同一台机器上, 而且效率比网络套接字更高。
2. 我以前总是以为, 这个type值决定了是tcp套接字还是udp套接字, 其实不是的。 type值决定的是流套接字还是数据报套接字或者其他。 注意流套接字不一定是tcp, 数据报套接字也不一定是udp.
3. protocol这个值通常为0, 为0的时候是什么意思呢? 意思是, 如果type是流套接字, 且protocol为0, 那么就是就是默认的流套接字---tcp套接字。 同理, 如果type是数据报套接字, 且protocol为0, 那么就是默认的数据报套接字---udp套接字。
返回值其实就是一个无符号整形,用于标识和索引套接字。可以通过返回值判断套接字是否创建成功。
bind函数详解
前面我们已经说了, 套接字也创建了, “地方”也定义了,下面就需要将socket放置在这个“地方”(TCP),将他们紧紧地捆绑在一起,用bind函数吧, 我们来看看函数原型:int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen);
第一个参数当然是待绑定的套接字啦,第二个参数是标识绑定在哪个“地方”, 第三个参数是这个“地方”的占地大小。
返回值表示绑定操作是否成功,0表示成功, -1表示不成功。函数的返回值千万不要忽视,上次就被师傅说了。
一般是这么调用的:
iRet = bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); // 注意强制转换
listen函数详解
套接字与“地方”绑定好了后,对于服务端而言(这里是TCP情况),自然就应该乖乖地去聆听,聆听客户端的需求,否则怎么会叫服务客户呢?如何去聆听呢?很简单,这个单词你早就学过了,那就是listen, 我们来看看listen函数的原型:
int PASCAL FAR listen (SOCKET s, int backlog);
第一个参数是服务端套接字,你要聆听,总得出来说个话啊,好,就指定你了;第二个参数是等待连接队列的最大长度,比方说,你将backlog定为10, 当有15个连接请求的时候,前面10个连接请求就被放置在请求队列中,后面5个请求被拒绝。千千万万要注意:这个10并不是表示客户端最大的连接数为10, 实际上可以有很多很多的客户端(实践证明也是如此)。
再看函数的返回值,成功返回0, 失败返回-1.
accept函数详解
既然客户端已经很虔诚了,很真诚了,处于倾听状态,那么该是去尝试接受客户端请求的时候了,别只顾着倾听,不去接纳别人。接纳客户端请求的函数是accept, 我们先来看看函数的原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
函数的第一个参数用来标识服务端套接字(也就是listen函数中设置为监听状态的套接字),第二个参数是用来保存客户端套接字对应的“地方”(包括客户端IP和端口信息等), 第三个参数是“地方”的占地大小。返回值对应客户端套接字标识。
实际上是这样的: accept函数指定服务端去接受客户端的连接,接收后,返回了客户端套接字的标识,且获得了客户端套接字的“地方”(包括客户端IP和端口信息等)。
accept函数非常地痴情,痴心不改:如果没有客户端套接字去请求,它便会在那里一直痴痴地等下去,直到永远(阻塞式)。
可是,我不想等了,我要睡觉了。睡觉之前,最后来看看accpt函数的用法:
unsigned int sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len);