listen()监听套接字上的连接
1.int listen(int sockfd, int backlog) ;
2.被动监听的socket
3.需要在此前调用bind()函数,否则由系统指定一个随机的端口
4.成功返回0,失败返回-1。
连接队列
1.一个新的Client的连接请求先被放在连接队列中,等待Server程序调用accept函数接受连接请求
2.backlog指的就是接收队列的长度,亦即Server程序调用accept函数之前最大允许的连接请求数,多余的连接请求将被拒绝
accept system call
accept()函数将响应连接请求,建立连接
1.int accept(int sockfd,struct sockaddr * addr,int * addrlen);
2.accept缺省是阻塞函数,阻塞直到有连接请求
3.sockfd:被动(监听)的socket描述符
4.如果成功,返回一个新的socket描述符(connected socket descriptor)来描述该连接。这个连接用来与特定的Client交换信息,原来的socket描述符继续保持非连接监听。如果失败则返回-1
5.addr将在函数调用后被填入连接对方的地址信息,如对方的IP、端口等。
connect system call
客户端使用connect()函数连接到主机
int connect(int sockfd, struct sockaddr * servaddr, int addrlen) ;
主动的socket
servaddr是事先填写好的结构,Server的IP和端口都在该数据结构中指定。
通常connect将阻塞直到服务器响应为止。
成功返回0,失败返回-1。
send/recv
面向连接的socket数据通讯
int send(int sockfd, const void * msg, size_t len, int flag ) ;
int recv(int sockfd, void * buf, size_t len, int flag ) ;
数据发送/接收函数,成功返回实际发送/接收的字节数,失败返回-1
与write/read比较
ssize_t write(int fd, const void * buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);
flag:
1.send:MSG_OOB, MSG_DONTROUTE,MSG_DONTWAIT,MSG_NOSIGNAL, ...
2.recv:MSG_OOB, MSG_PEEK,MSG_WAITALL,MSG_NOSIGNAL,…
OOB(out-of-band),一般用于紧急特殊数据。
sendto/recvfrom
面向数据报的socket通讯sendto/recvfrom
int sendto(int sockfd, const void *msg, size_t len, int flags, const
struct sockaddr *to, socketlen_t tolen) ;
int recvfrom(int sockfd, void *buf, size_t len, int flags, struct
sockaddr *from, socklen _t *fromlen);
close&shutdown
close关闭套接字
1.int close(int sockfd);
shutdown按指定方式关闭套接字
1.int shutdown(int sockfd, int how);
2.how:SHUT_RD,SHUT_WR,SHUT_RDWR
shutdown直接对TCP连接进行操作,close只是对套接字描述符操作。
getsockopt&setsockopt
检查与修改套接字选项
int getsockopt(int socket, int level, int optname,
void *optval, socklent *optlen-ptr)
int setsockopt(int socket, int level, int optname,
void *optval, socklent optlen)
level为操作级别,当为普通socket级时,设为SOL_ SOCKET,否则设为操作控制选项对应的协议号,如TCP选项为IPPROTO_TCP,IP选项为IPPROTO_IP。
Host name and address conversions
Files:
1./etc/resolv.conf, /etc/hosts
Functions
#include
struct hostent *gethostbyname(const char *name);
struct hostent *gethostbyaddr(const char *addr, size_t len, int format);
struct hostent
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list * /
int h_addrtype; /* host address type, AF_INET/ AF _INET6 */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
};
Service name and port number conversions
File:
1./etc/services
Functions:
#include
struct servent *getservbyname(const char *name, const char *proto);
struct servent *getservbyport(int port, const char *proto) ;
struct servent
struct servent {
char *s_name ; /* official service name */
char **s_aliases ; /* alias list */
int s_port; /* port number */
char *s_proto; /* protocol to use */
};
Multi-process server
Non-block mode
阻塞与非阻塞方式的比较
1.errno - EWOULDBLOCK
非阻塞的实现
int flags;
if((flags=fcntl(sock_fd, F_GETFL ,0))< 0)
err_sys();
flags|=O_NONBLOCK;
if(fcntl(sock_fd, F_SETFL, flags)<0)
err_sys();
Signal-driven I/O
用于接收紧急数据
1.带外数据
2.SIGURG,SIGIO
实现
1.fcntl(conn_fd, F_SETOWN, getpid());
2.sigaction
I/O multiplexing
基本思想:
先构造一张有关描述符的表,然后调用一个函数(如select),该函数到这些描述符中的一个已准备好进行I/O 时才返回,返回时告诉进程哪个描述符已准备好进行I/O.
select:synchronous I/O multiplexing.
#include
int select(int n, fd _set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);
FD_ZERO(fd_set * set);
FD_SET(int fd, fd_set *set);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
xinetd 启动服务的过程
xinetd启动时读取/etc/xinetd目录中的文件(早期版本为/etc/inetd文件),根据其中的内容给所有允许启动的服务创建一个指定类型的套接口,并将套接口放入select()中的描述符集合中。
对每个套接口绑定bind(),所用的端口号和其它参数来自/etc/xinetd目录下每个服务的配置文件。
如果是TCP套接口就调用函数listen(),等待用户连接。如果是UDP套接口,就不需调用此函数。
所有套接口建立后,调用函数select()检查哪些套接口是准备好的。
若select()返回TCP套接口,就调用accept()接收这个连接。如果为UDP,就不需调用此函数。
xinetd调用fork()创建子进程,由子进程处理连接请求。
1.子进程关闭所有其它描述符,只剩下套接口描述符。这个套接口描述符对于TCP是accept()返回的套接口,对于UDP为最初建立的套接口。然后子进程连续三次dup()函数,将套接口描述符复制到0、1和2,它们分别对应标准输入、标准输出和标准错误输出,并关闭套接口描述符。
2. 子进程查看/etc/xinetd下文件中的用户,如果不是root用户,就用调用命令setuid和setgid将用户ID和组ID改成文件中指定的用户。
对于TCP套接口,与用户交流结束后父进程需要关闭已连接套接口。父进程重新处于select()状态,等待下一个可读的套接口。
最后调用配置文件中指定的外部服务程序,外部程序启动后就可与用户进行信息传递了。