第四章:基本TCP套接口编程

来源:互联网 发布:上海神机妙算软件定额 编辑:程序博客网 时间:2024/05/17 07:44

基本TCP套接口编程

并发服务器:这是unix的一项技术,当有大量客户端到同一服务器的连接的时候,服务器给每一个客户端的连接都fork一个子进程,已达到并发的目的。

int socket(int sa_family, int type, intprotol)

sa_family 是协议族type是套接字类型,protol是协议(这个一般都是0)

      

协议族

解释

AF_INET

Ipv4协议族

AF_INET6

Ipv6协议族

AF_LOCAL

UNIX 域协议族

AF_ROUTE

路由套接口

AF_KEY

密钥套接口


协议族

类型

解释

SOCK_STREAM

字节流套接口

SOCK_DGRAM

数据报套接口

SOCK_RAW

原始套接口

套接口类型


socket在成功返回的时候返回一个很小的非负整数,和文件描述符一样,简称套接口

2 客户通过connect和服务器建立连接

int connect(int sock_fd, const strcutsockaddr* addr, socklen_t *len);

客户在调用connect之前不用进行bind操作,内核会选用一个ip和临时端口号来绑定到这个套接字(也可以进行bind)

关于connect的返回值:

ETIMEOUT:当connect调用的时候会进行三次握手,之前的文章已经说过了,如果客户端没有收到服务器对SYN的ACK数据报,就会返回ETIMEOUT(客户端在返回这个错误之前会重发SYN数据报)

ECONNREFUSED:当connect调用的时候,发送SYN同步数据报建立连接,服务器收到该数据报的时候,但是在connect上的地址和端口上没有监听服务器进程,服务器就返回ECONNREFUSED错误

EHOSTUNREACH:当客户端发送的SYN数据报在中间路由器引发不可达的信息,该中间路由器会返回给客户端一个ICMP数据报,客户端就会收到EHOSTUNREACH这个错误码

如果connect调用失败,不能再用该套接口,必须进行close操作(原来如此)

bind函数

int bind(int sock_fd, const strcut sockaddr * addr, socklen_t *len);

一般TCP 服务器都会调用bind 以此来确定监听套接字的 ip地址和port,如果服务器不指定的话,就会使用某个ip(多ip的情况下)和一个临时端口号,但是这样客户端就不知道服务器的具体地址和端口号,这样的话,当客户端进行connect的时候,就不知道具体服务器的地址了,就建立不了连接了。

如果端口号是0 的话,就使用临时端口号

Ipv4的通配地址是INADDR_ANY这个会让内核决定使用哪一个ip(多ip的路由器或主机)

如果端口号使用的是0 ,内核为该套接字使用临时端口号,之后如果想或者这个临时端口号的话,就调用getsockname来返回协议地址

listen函数

int listen(int sock_fd, int waitqueue)

内核要维护两个队列:

未完成连接队列:当服务器收到客户端的SYN同步数据报的时候,就会在内核的为完成队列中建立一个条目,这样的套接字处于SYN_REVD状态

已完成连接队列:当服务器对SYN数据报发出确认,同时客户端收到服务器发送的对syn数据报进行确认的数据报之后,客户端会发送给服务器ack的ack数据报,这个时候,就会在已完成队列中建立一个条目,同时取消未完成队列的相应的条目信息

当调用accept的时候,是在已完成队列中的拿走一个条目,如果这个已完成队列是空的那么accept就会进入睡眠状态

当这两个队列是满的话,就会直接丢掉这个SYN数据报或者ack数据报,等待客户端进行重发。这时候服务器处理以完成队列和未完成队列,这个时候,就能是队列不那么拥挤,就有可能对重发的syn数据报进行处理。

accept函数:

int accept(int listen_fd , strcut sockaddr* addr, socklen_t *len);

listen_fd 是监听套接字

addr是客户端地址

len是你告诉内核想要获取的长度

如果对客户端的ip和port不感兴趣的话,可以把这两个值设置为NULL

并发服务器:

并发最简单的就是为每一个客户端都fork子进程,让子进程来完成每一个客户端的请求

close操作

int close(int fd)

这个操作并不是每次都发送FIN数据报(finish),一般发送的时候是这个套接字的引用计数已经是0了,一般当调用fork之前,客户端和服务器进行完了三次握手,完成了内核建立新的发送socket的时候,这之后,调用fork函数,子进程对对父进程的资源进行拷贝,当然会有 socket套接字(相当于问价描述符)这个时候,就相当于打开了两次,访问计数为2 ,当父进程建立完子进程的时候,就直接对发送套接字进行close是这个发送套接字的访问计数减一,当子进程完成数据的发送接收的时候,就进行close操作,这个时候,才进行发送FIN字节的操作

如果我们确实想对客户端发送FIN(不考虑访问计数的问题)就对套接字调用shutdown操作

int shutdown(int s, int how);

The shutdown() call causes all or part of a full-duplex connection on thesocket associated with s to be shut down. If how is SHUT_RD, further receptions will be disallowed.  If how is SHUT_WR, further  trans-missions  will be  disallowed.  If how is SHUT_RDWR, further receptions andtransmissions will be disallowed.

获得套接口相关联的地址信息:

int getsockname(int sock_fd, strcutsockaddr * addr, socklen_t * len )

这个函数是或者套接口的本地地址信息

int getpeername(int sock_fd, structsockaddr* addr, socklen_t* len)

这个是获得与sock_fd连接上的客户端的地址

这里的这个sock_fd 参数是调用的已经建立连接的socket,connect 或accept之后的套接字




在睡梦中看书,有点不爽。不过stevens的看的还是蛮爽的

原创粉丝点击