文章标题

来源:互联网 发布:华一软件 编辑:程序博客网 时间:2024/06/18 17:26

1.每一条TCP连接唯一地被通信两端的两个端点(即两个套接字)所确定。
即:TCP连接 ={socket1,socket2}={(IP1:port1),(IP2:port2)};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2.那基本的TCP客户/服务器程序的使用函数如下:
TCP服务器端函数的流程是:
socket()—->bind()—->listen()—->accept()然后一直阻塞到客户连接—->处理数据等流程—–>close()。
接下来分析下这几个函数:
socket系统调用可以创建一个socket。所谓socket就是可读,可写,可控制,可关闭的文件描述符。
函数原型:
int socket(int domain,int type,int protocol);
domain参数告诉系统使用哪个底层协议族。
type参数指定服务类型。
服务类型主要有:SOCK_STREAM服务(流服务)和SOCK_DGRAM(数据报)服务。
对TCP/IP协议族而言,其值取SOCK_STREAM表示传输层使用TCP协议,取SOCK_DGRAM表示传输层使用UDP协议。
自从Linux内核版本2.6.17起,type参数可以接受上述服务类型与下面两个重要标志相与的值:
SOCK_NONBLOCK===>表示新创建的socket设为非阻塞的。
SOCK_CLOEXEC ===>表示fork调用创建子进程时在子进程中关闭该socket。
protocol参数是在前两个参数构成的协议集合下,再选择一个具体的协议。不过几乎在所有情况下,我们都应该把它设置为0,表示使用默认协议。
函数返回值:socket系统调用成功时返回一个socket文件描述符,失败则返回-1,并设置errno。

作用:将我们之前创建的socket与socket地址绑定,给socket命名。在服务器程序中,我们通常要命名socket,因为只有命名后客户端才能知道该如何连接它。
命名socket的系统调用时bind,其定义如下:
int bind(int sockfd,const struct sockaddr* my_addr,socklen_t addrlen);
bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen参数指定该socket地址的长度。
函数返回值:
bind成功时返回0,失败则返回-1并设置errno。
最常见的两种errno是EACCES和EADDRINUSE。
EACCES===>说明被绑定的地址是受保护的地址,仅超级用户能够进行访问。比如普通用户将socket绑定到知名服务端口(端口号为0-1023)上时,bind将返回EACCES错误。
EADDRINUSE===>说明被绑定的地址正在使用中,比如将socket绑定到一个处于TIME_WAIT状态的socket地址。

[3]listen()系统调用:
socket被命名之后,还不能马上接受客户连接,我们需要创建一个监听队列以存放待处理的客户连接。
int listen(int sockfd,int backlog);
sockfd参数指定被监听的socket。backlog参数提示内核监听队列的最大长度。
listen成功时返回0,失败时返回-1并设置errno。
listen相关的队列有2个:syn queue和accept queue
syn queue队列是处于半连接状态,用于保存等待三次握手完成的SYN请求的信息;
accept queue队列是处于完全连接状态,用于保存三次握手完成并等待accept系统调用的连接的信息。
服务器通过listen调用被动接受连接。
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
sockfd参数是执行过listen调用的监听socket。
addr参数用来获取被接受连接远端socket地址,该地址长度有addrlen参数指出。
accept成功时返回一个socket,该socket唯一地标示了这个连接。服务器通过读写该socket来与客户端通信。
accept失败时返回-1。

accept只是从监听队列中取出连接,而不论连接处于何种状态,更不关心网络状况的变化。

1 0
原创粉丝点击