UNIX网络编程卷1:套接字-chapter5

UNIX网络编程卷1:套接字-chapter5_第1张图片

一、公有头文件

unp.h

#include		/* basic system data types */
#include		/* basic socket definitions */
#include		/* timeval{} for select() */
#include			/* timespec{} for pselect() */
#include		/* sockaddr_in{} and other Internet defns */
#include		/* inet(3) functions */
#include	

#include	
#include	
#include	
#include	

#define	SA	struct sockaddr
#define	LISTENQ	1024
#define	MAXLINE 4096	/* max text line length */
#define	BUFFSIZE 8192	/* buffer size for reads and writes */
#define	SERV_PORT 9877	
int  Socket(int, int, int);
void Bind(int, const SA *, socklen_t);
void Listen(int, int);
int Accept(int, SA *, socklen_t *);
pid_t Fork(void);
void Connect(int, const SA *, socklen_t);
void Close(int);
void Writen(int fd, void *ptr, size_t nbytes);
void str_echo(int sockfd);
const char*  Inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
void Inet_pton(int family, const char *strptr, void *addrptr);

void	 Fclose(FILE *);
FILE	*Fdopen(int, const char *);
char	*Fgets(char *, int, FILE *);
FILE	*Fopen(const char *, const char *);
void	 Fputs(const char *, FILE *);
ssize_t	 Readline(int, void *, size_t);
void	 str_echo(int);
void	 str_cli(FILE *, int);

实现 wrap.c

#include "unp.h"


int
Socket(int family,int type,int protocol){
 int n;
 if((n=socket(family,type,protocol))< 0){
    printf("socket error");
    return n;
 }
  return n;
}

void
Bind(int fd, const struct sockaddr *sa, socklen_t salen){
  if (bind(fd, sa, salen) < 0){
	printf("bind error");
    }
}

void
Listen(int socketfd,int backlog){
  if(listen(socketfd,backlog) <0){
      printf("listen error");
  }
}

int 
Accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen){
	int n;
	if((n=accept(sockfd,addr,addrlen))<0){
	 printf("accept error");
	}
	return n;
}

pid_t
Fork(void)
{
	pid_t	pid;

	if ( (pid = fork()) == -1)
		printf("fork error");
	return pid;
}

void
Connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen){
   if(connect(sockfd,addr,addrlen)<0){
      printf("connect error");
   }
}

void
Close(int sockfd){
  if(close(sockfd) <0)
    printf("close error");
}

const char *
Inet_ntop(int family, const void *addrptr, char *strptr, size_t len)
{
	const char	*ptr;

	if (strptr == NULL)		/* check for old code */
		printf("NULL 3rd argument to inet_ntop");
	if ( (ptr = inet_ntop(family, addrptr, strptr, len)) == NULL)
		printf("inet_ntop error");		/* sets errno */
	return(ptr);
}

void
Inet_pton(int family, const char *strptr, void *addrptr)
{
	int		n;

	if ( (n = inet_pton(family, strptr, addrptr)) < 0)
		printf("inet_pton error for %s", strptr);	/* errno set */
	else if (n == 0)
		printf("inet_pton error for %s", strptr);	/* errno not set */

	/* nothing to return */
}

void
Fclose(FILE *fp)
{
        if (fclose(fp) != 0)
                printf("fclose error");
}

FILE *
Fdopen(int fd, const char *type)
{
        FILE    *fp;

        if ( (fp = fdopen(fd, type)) == NULL)
                printf("fdopen error");

        return(fp);
}

char *
Fgets(char *ptr, int n, FILE *stream)
{
        char    *rptr;

        if ( (rptr = fgets(ptr, n, stream)) == NULL && ferror(stream))
                printf("fgets error");

        return (rptr);
}

FILE *
Fopen(const char *filename, const char *mode)
{
        FILE    *fp;

        if ( (fp = fopen(filename, mode)) == NULL)
                printf("fopen error");

        return(fp);
}

void
Fputs(const char *ptr, FILE *stream)
{
        if (fputs(ptr, stream) == EOF)
                printf("fputs error");
}

ssize_t                                         /* Write "n" bytes to a descriptor. */
writen(int fd, const void *vptr, size_t n)
{
        size_t          nleft;
        ssize_t         nwritten;
        const char      *ptr;

        ptr = vptr;
        nleft = n;
        while (nleft > 0) {
                if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
                        if (nwritten < 0 && errno == EINTR)
                                nwritten = 0;           /* and call write() again */
                        else
                                return(-1);                     /* error */
                }

                nleft -= nwritten;
                ptr   += nwritten;
        }
        return(n);
}
/* end writen */

void
Writen(int fd, void *ptr, size_t nbytes)
{
        if (writen(fd, ptr, nbytes) != nbytes)
                printf("writen error");
}

二、服务端

BaseSocketServer.c

#include	"../unp.h"

int
main(int argc, char *args[])
{
	int                 listenfd, connfd;
	pid_t	            childpid;
	socklen_t	    clilen;
	struct sockaddr_in  cliaddr, servaddr;
        int port = atoi(args[1]);
    //创建socket
	listenfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family      = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port        = htons(port);
    //绑定端口
	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    //监听端口
	Listen(listenfd, LISTENQ);

	for ( ; ; ) {
		clilen = sizeof(cliaddr);
		//接受客户端的连接
		connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
                printf("accept ip:%s,port:%d\n",inet_ntoa(cliaddr.sin_addr),htons(cliaddr.sin_port));
        //fork子进程,处理该客户端交互
		if ( (childpid = Fork()) == 0) {	/* child process */
			Close(listenfd);	/* close listening socket */
			str_echo(connfd);	/* process the request */
			exit(0);
		}
		Close(connfd);			/* parent closes connected socket */
	}
}

str_echo的实现

#include	"../unp.h"

void
str_echo(int sockfd)
{
	ssize_t		n;
	char		buf[MAXLINE];

again:
	while ( (n = read(sockfd, buf, MAXLINE)) > 0)
		Writen(sockfd, buf, n);

	if (n < 0 && errno == EINTR)
		goto again;
	else if (n < 0)
		printf("str_echo: read error");
}

三、客户端

BaseSocketClient.c

#include	"../unp.h"

int
main(int argc, char *args[])
{
        int		        sockfd;
	struct sockaddr_in	servaddr;

	if (argc <3)
	   printf("argc error\n");
        int port = atoi(args[2]);
    //创建socket
	sockfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(port);
	Inet_pton(AF_INET, args[1], &servaddr.sin_addr);
    //连接服务端
	Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

	str_cli(stdin, sockfd);		/* do it all */

	exit(0);
}

str_cli的实现

#include	"../unp.h"

void
str_cli(FILE *fp, int sockfd)
{
	char	sendline[MAXLINE], recvline[MAXLINE];

	while (Fgets(sendline, MAXLINE, fp) != NULL) {

		Writen(sockfd, sendline, strlen(sendline));

		if (Readline(sockfd, recvline, MAXLINE) == 0)
			printf("str_cli: server terminated prematurely");

		Fputs(recvline, stdout);
	}
}

Readline的实现

/* include readline */
#include	"../unp.h"

static int	read_cnt;
static char	*read_ptr;
static char	read_buf[MAXLINE];

static ssize_t
my_read(int fd, char *ptr)
{

	if (read_cnt <= 0) {
again:
		if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
			if (errno == EINTR)
				goto again;
			return(-1);
		} else if (read_cnt == 0)
			return(0);
		read_ptr = read_buf;
	}

	read_cnt--;
	*ptr = *read_ptr++;
	return(1);
}

ssize_t
readline(int fd, void *vptr, size_t maxlen)
{
	ssize_t	n, rc;
	char	c, *ptr;

	ptr = vptr;
	for (n = 1; n < maxlen; n++) {
		if ( (rc = my_read(fd, &c)) == 1) {
			*ptr++ = c;
			if (c == '\n')
				break;	/* newline is stored, like fgets() */
		} else if (rc == 0) {
			*ptr = 0;
			return(n - 1);	/* EOF, n - 1 bytes were read */
		} else
			return(-1);		/* error, errno set by read() */
	}

	*ptr = 0;	/* null terminate like fgets() */
	return(n);
}

ssize_t
readlinebuf(void **vptrptr)
{
	if (read_cnt)
		*vptrptr = read_ptr;
	return(read_cnt);
}
/* end readline */

ssize_t
Readline(int fd, void *ptr, size_t maxlen)
{
	ssize_t		n;

	if ( (n = readline(fd, ptr, maxlen)) < 0)
		printf("readline error");
	return(n);
}

四、gcc编译服务端

gcc BaseSocketServer.c str_echo.c ../wrap.c -o server

UNIX网络编程卷1:套接字-chapter5_第2张图片

  • ./server 8899
    绑定并且监听8899端口

五、gcc编译客户端

gcc BaseSocketClient.c readline.c str_cli.c ../wrap.c -o client

在这里插入图片描述

  • ./client xxxx.xxxx.xxx 8899
    客户端需要连接的ip地址以及端口数据,8899就是服务端监听的端口

六、使用CMake编译

如果我们直接使用gcc对程序进行编译的话,源文件少的话,还能接受,如果很多的话,就头大了!就采用CMake跨平台的交叉编译功能,来简化我们的编译。Cmake官方文档

6-1、服务端CMakeList.txt

cmake_minimum_required(VERSION 3.26)

PROJECT(BASIC)
INCLUDE_DIRECTORIES(../unp.h)
SET(SRC_LIST BaseSocketServer.c str_echo.c ../wrap.c)
ADD_EXECUTABLE(server ${SRC_LIST})
  • cmake_minimum_required
    设置CMake最小版本
  • PROJECT
    设置项目名称
  • INCLUDE_DIRECTORIES
    设置头文件
  • SET
    设置变量值
  • ADD_EXECUTABLE
    用于使用指定的源文件向项目(project)添加可执行文件

6-2、客户端CMakeLists.txt

cmake_minimum_required(VERSION 3.26)

PROJECT(BASIC_CLIENT)
INCLUDE_DIRECTORIES(../unp.h)
SET(SRC_LIST BaseSocketClient.c readline.c str_cli.c ../wrap.c)
ADD_EXECUTABLE(client ${SRC_LIST})

6-3、在CMakeLists.txt所在的目录下执行cmake指令

UNIX网络编程卷1:套接字-chapter5_第3张图片

  • cmake
    cmake指令必须指定编译的路径 cmake .代表当前目录下
  • 多余文件
    使用该方式,会在源文件夹下产生很多多余的文件,影响整体项目的可读性,为了解决这样的问题,我们可以在这个源文件夹下建立一个build文件,专门用来保存编译后的文件
    UNIX网络编程卷1:套接字-chapter5_第4张图片
    UNIX网络编程卷1:套接字-chapter5_第5张图片
  • cd build/
    进入build文件夹
  • cmake …
    因为CMakeLists.txt文件在当前文件夹的上级目录下,所以使用…指向上级目录
  • cmake生成的文件
    生成的是Makefile相关的文件
  • make
    使用make命令编译源码
  • server
    该文件就是编译后生成的可执行文件
    UNIX网络编程卷1:套接字-chapter5_第6张图片

七、服务端监听中断信号

signal.c

#include "../unp.h"

Sigfunc *
signal(int signo, Sigfunc *func)
{
	struct sigaction	act, oact;

	act.sa_handler = func;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	if (signo == SIGALRM) {
#ifdef	SA_INTERRUPT
	   act.sa_flags |= SA_INTERRUPT;	/* SunOS 4.x */
#endif
	} else {
#ifdef	SA_RESTART
	  act.sa_flags |= SA_RESTART;		/* SVR4, 44BSD */
#endif
	}
	if (sigaction(signo, &act, &oact) < 0)
		return(SIG_ERR);
	return(oact.sa_handler);
}
/* end signal */

Sigfunc *
Signal(int signo, Sigfunc *func)	/* for our signal() function */
{
	Sigfunc	*sigfunc;

	if ( (sigfunc = signal(signo, func)) == SIG_ERR)
	   printf("signal error");
	return(sigfunc);
}

sigchldwait.c

#include        "../unp.h"
  
void
sig_chld(int signo)
{
        pid_t   pid;
        int             stat;

        while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
            printf("child %d terminated\n", pid);
        return;
}

服务端调整


#include        "../unp.h"
  
int
main(int argc, char *args[])
{
        int                 listenfd, connfd;
        pid_t               childpid;
        socklen_t           clilen;
        struct sockaddr_in  cliaddr, servaddr;
        void                sig_chld(int);


        int port = atoi(args[1]);
        listenfd = Socket(AF_INET, SOCK_STREAM, 0);

        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family      = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port        = htons(port);

        Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

        Listen(listenfd, LISTENQ);
        //必须在fork第一个子进程之前完成,并且只做一次
        Signal(SIGCHLD, sig_chld);      /* must call waitpid() */

        for ( ; ; ) {
                clilen = sizeof(cliaddr);
                if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
                        if (errno == EINTR)
                                continue;               /* back to for() */
                        else
                                printf("accept error");
                }

                printf("accept ip:%s,port:%d\n",inet_ntoa(cliaddr.sin_addr),htons(cliaddr.sin_port));
                if ( (childpid = Fork()) == 0) {        /* child process */
                        Close(listenfd);        /* close listening socket */
                        str_echo(connfd);       /* process the request */
                        exit(0);
                }
                Close(connfd);                  /* parent closes connected socket */
        }
}

你可能感兴趣的:(C/C++,unix,网络,服务器)