《UNP》第4到第5章講解了基本的TCP套接字編程。先介紹了各類套接字的地址結構,然後詳述了各個API。
先介紹了一個迭代服務器,但是迭代服務器性能有限,所以提供了一個併發服務器。但是併發服務器需要用信號機制處理結束的子進程,並且會帶來中斷慢系統調用的問題。
再升級一下,可以預先派生子進程,可以用線程替代進程,可以把服務器改造爲守護進程等等。
#ifndef UTILS_H
#define UTILS_H
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
//typedef void waitSon(int);
//註冊信號處理程序,需要提供信號和函數指針。
void (*waitSon(int signo, void (*func)(int)))(int);
//建立一個TCP連接,成功返回套接字描述符;失敗返回-1.
int tcp_connect(const char *addr, const char *port);
//成功則返回一個監聽套接字;否則返回-1.
int tcp_listen(const char *port, int backlog);
//建立一個TCP連接,成功返回套接字描述符;失敗返回-1.
int tcp_connect(const char *addr, const char *port)
{
int sockfd;
struct sockaddr_in svraddr;
bzero(&svraddr, sizeof(svraddr));
svraddr.sin_family = AF_INET;
svraddr.sin_port = htons(atoi(port));
inet_pton(AF_INET, addr, &svraddr.sin_addr);
if( (sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
write(2, "socket error\n", 13);
return -1;
}
if(connect(sockfd, (struct sockaddr*) &svraddr, sizeof(svraddr)) == 0)
return sockfd;
else
{
write(2, "connect error\n", 14);
return -1;
}
}
//成功則返回一個監聽套接字;否則返回-1.
int tcp_listen(const char *port, int backlog)
{
int listenfd;
struct sockaddr_in svraddr;
bzero(&svraddr, sizeof(svraddr));
svraddr.sin_family = AF_INET;
svraddr.sin_port = htons(atoi(port));
svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
if( (listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
write(2, "socket error\n", 13);
return -1;
}
if( bind(listenfd, (struct sockaddr*)&svraddr, sizeof(svraddr)) < 0)
{
write(2, "bind error\n", 11);
return -1;
}
if(listen(listenfd, backlog) < 0)
{
write(2, "listen error\n", 13);
return -1;
}
return listenfd;
}
//註冊信號處理程序,需要提供信號和函數指針。
void (*waitSon(int signo, void (*func)(int)))(int)
{
struct sigaction act,oact;//oact = oldact
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_flags |= SA_RESTART;
if( sigaction(signo, &act, &oact) < 0 )
return SIG_ERR;
else
return oact.sa_handler;
}
#endif