网络编程中的tcp实例太多了,自己也写了好几次(羞愧),今天在想一对一的TCP知道怎么写了,可是一对多的怎么办呢?服务器是如何知道要给那个发送数据呢?做开发的同学应该经常听说uid这个属性。可以为什么通过UID就知道要发送的数据是给正确的用户的呢?
不怎么忙的时候。仔细的了解了一下TCP的几个API和其中的参数。下面来看一下这几个API和参数:
- 描述:当创建socket套接字后,该套接字并没有鱼本机地址和端口等信息相连接,而bind函数将完成这些工作
包含的头文件 <sys/types.h> <sys/socket.h>
原型:
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
返回值: 0:成功 1:失败
返回的错误码
EACCES:地址受到保护,用户非超级用户。
EADDRINUSE:指定的地址已经在使用。
EBADF:sockfd参数为非法的文件描述符。
EINVAL:socket已经和地址绑定。
ENOTSOCK:参数sockfd为文件描述符
- 描述:listen函数使用主动连接套接口变为被连接套接口,是的一个进程能够接受其他请求,使之成为一个服务器进程。总之TCP服务器中LISTEN函数将进程变为一个服务器进程,将主动变成了被动
包含头文件 #include<sys/socket.h>
原型: int listen(int sockfd, int backlog)
返回值: 0:成功 1:失败
参数解析:
参数1:socket
被listen函数作用的套接字,sockfd之前由socket函数返回。在被socket函数返回的套接字fd之时,它是一个主动连接的套接字,也就是此时系统假设用户会对这个套接字调用connect函数,期待它主动与其它进程连接,然后在服务器编程中,用户希望这个套接字可以接受外来的连接请求,也就是被动等待用户来连接。由于系统默认时认为一个套接字是主动连接的,所以需要通过某种方式来告诉系统,用户进程通过系统调用listen来完成这件事。
参数2:backlog:
这个参数涉及到一些网络的细节。在进程正理一个一个连接请求的时候,可能还存在其它的连接请求。因为TCP连接是一个过程,所以可能存在一种半连接的状态,有时由于同时尝试连接的用户过多,使得服务器进程无法快速地完成连接请求。如果这个情况出现了,服务器进程希望内核如何处理呢?内核会在自己的进程空间里维护一个队列以跟踪这些完成的连接但服务器进程还没有接手处理或正在进行的连接,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限。这个backlog告诉内核使用这个数值作为上限。毫无疑问,服务器进程不能随便指定一个数值,内核有一个许可的范围。这个范围是实现相关的。很难有某种统一,一般这个值会小30以内。
- 描述:accept().接受客户端的连接,并建立一个与客户端对应的socket。
需注意:在服务器端,socket()返回的套接字用于监听(listen)和接受(accept)客户端的连接请求。这个套接字不能用于与客户端之间发送和接收数据。accept()接受一个客户端的连接请求,并返回一个新的套接字。所谓“新的”就是说这个套接字与socket()返回的用于监听和接受客户端的连接请求的套接字不是同一个套接字。与本次接受的客户端的通信是通过在这个新的套接字上发送和接收数据来完成的。
头文件: #include <sys/types.h> #include <sys/socket.h>
原型: int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen)
返回值: >0:成功 <0:失败,并返回错误码
参数解释:
socketfd:利用系统调用socket()建立的套接字描述符,通过bind()绑定到一个本地地址(一般为服务器的套接字),并且通过listen()一直在监听连接;
addr:指向struct sockaddr的指针,该结构用通讯层服务器对等套接字的地址(一般为客户端地址)填写,返回地址addr的确切格式由套接字的地址类别(比如TCP或UDP)决定;若addr为NULL,没有有效地址填写,这种情况下,addrlen也不使用,应该置为NULL;
addrlen:一个值结果参数,调用函数必须初始化为包含addr所指向结构大小的数值,函数返回时包含对等地址(一般为服务器地址)的实际数值;
- 来看一下服务器的大概流程:
1: server_sock = socket(); 2: bind(server_sock); 3: listen(server_sock); 4: client_sock = accept(server_sock); 5: close(server_sock); 6: send(client_sock, data); 7: recv(client_sock, data); 8: close(client_sock);
是不是发现第四点新建立了一个套接字!而发送和接受数据的套接字都是accpet后新建的,没错服务器就是通过这个套接字向正确的客户端发送数据的。你可能会说常见的都是通过UID发送的。没错!那是因为开发者做了一层封装,将此套接字作为Value和用户的uid作为key存到了一个名为hashmap的对应关系中,所以才会有你所了解的通过UID给用户发送数据。