TCP Server处理多Client请求的方法—非阻塞accept与select

参看基于TCP/UDP的socket代码,同一时间Server只能处理一个Client请求:在使用当前连接的socket和client进行交互的时候,不能够accept新的连接请求。为了使Server能够处理多个Client请求,常见的方法:

多进程/线程方法、non-blocking socket(单进程并发)、non-blocking和select结合使用。三种方法各有优缺点,下面进行详细分析和说明。

一、多进程/线程方法

这种方法,每个子进程/线程单独处理一个client连接。以使用进程为例,在每个accept成功之后,使用fork创建一个子进程专门处理该client的connection,父进程(server)本身可以继续accept其他新的client的连接请求。示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

#include <signal.h>
#include <sys/wait.h>

#define DEFAULT_PORT    1984    //默认端口
#define BUFFER_SIZE     1024    //buffer大小

void sigCatcher(int n) {
	//printf("a child process dies\n");
	while(waitpid(-1, NULL, WNOHANG) > 0);
}

int clientProcess(int new_sock);

int main(int argc, char *argv[]) {
	unsigned short int port;

	//get port, use default if not set
	if (argc == 2) {
		port = atoi(argv[1]);
	} else if (argc < 2) {
		port = DEFAULT_PORT;
	} else {
		fprintf(stderr, "USAGE: %s [port]\n", argv[0]);
		return 1;
	}

	//create socket
	int sock;
	if ( (sock = socket(PF_INET, SOCK_STREAM, 0)) == -1 ) {
		perror("socket failed, ");
		return 1;
	}
	printf("socket done\n");

	//create socket address and initialize
	struct sockaddr_in bind_addr;
	memset(&bind_addr, 0, sizeof(bind_addr));
	bind_addr.sin_family = AF_INET;
	bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);  //设置接受任意地址
	bind_addr.sin_port = htons(port);//将host byte order转换为network byte order

	//bind (bind socket to the created socket address)
	if ( bind(sock, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) == -1 ) {
		perror("bind failed, ");
		return 1;
	}
	printf("bind done\n");

	//listen
	if ( listen(sock, 5) == -1) {
		perror("listen failed.");
		return 1;
	}
	printf("listen done\n");

	//handler to clear zombie process
	signal(SIGCHLD, sigCatcher);

	//loop and respond to client
	int new_sock;
	int pid;
	while (1) {
		//wait for a connection, then accept it
		if ( (new_sock = accept(sock, NULL, NULL)) == -1 ) {
			perror("accept failed.");
			return 1;
		}
		printf("accept done\n");

		pid = fork();
		if (pid < 0) {
			perror("fork failed");
			return 1;
		} else if (pid == 0) {
			//这里是子进程
			close(sock);            //子进程中不需要server的sock
			clientProcess(new_sock);    //使用新的new_sock和client进行交互
			close(new_sock);        //关闭client的连接
			exit(EXIT_SUCCESS);     //子进程退出
		} else {
			//这里是父进程
			close(new_sock); //由于new_sock已经交给子进程处理,这里可以关闭了
		}
	}
	return 0;
}

int clientProcess(int new_sock) {
	int recv_size;
	char buffer[BUFFER_SIZE];

	memset(buffer, 0, BUFFER_SIZE);
	if ( (recv_size = recv(new_sock, buffer, sizeof(buffer), 0)) == -1) {
		perror("recv failed");
		return 1;
	}
	printf("%s\n", buffer);

	char *response = "This is the response";
	if ( send(new_sock, response, strlen(response) + 1, 0) == -1 ) {
		perror("send failed");
		return 1;
	}
	return 0;
}

代码为了处理zombie process(僵尸进程)问题:当server进程运行时间较长,且产生越来越多的子进程,当这些子进程运行结束都会成为zombie process,占据系统的process table。解决方法是在父进程(server进程)中显式地处理子进程结束之后发出的SIGCHLD信号:调用wait/waitpid清理子进程的zombie信息。

测试:运行server程序,然后同时运行2个client(telnet localhost 1984),可看到该server能够很好地处理2个client。

多进程方法的优点:

每个独立进程处理一个独立的client,对server进程来说只需要accept新的连接,对每个子进程来说只需要处理自己的client即可。

多进程方法的缺点:

子进程的创建需要独立的父进程资源副本,开销较大,对高并发的请求不太适合;且一个进程仅处理一个client不能有效发挥作用。另外有些情况下还需要进程间进行通信以协调各进程要完成的任务。

二、non-blocking socket(单进程并发)方法

blocking socket VS non-blocking socket

默认情况下socket是blocking的,即函数accept(), recv/recvfrom, send/sendto,connect等,需等待函数执行结束之后才能够返回(此时操作系统切换到其他进程执行)。accpet()等待到有client连接请求并接受成功之后,recv/recvfrom需要读取完client发送的数据之后才能够返回。

可设置socket为non-blocking模式,即调用函数立即返回,而不是必须等待满足一定条件才返回。参看http://www.scottklement.com/rpg/socktut/nonblocking.html

non-blocking: by default, sockets are blocking – this means that they stop the function from returning until all data has been transfered. With multiple connections which may or may not be transmitting data to a server, this would not be very good as connections may have to wait to transmit their data.

设置socket为非阻塞non-blocking

使用socket()创建的socket(file descriptor),默认是阻塞的(blocking);使用函数fcntl()(file control)可设置创建的socket为非阻塞的non-blocking。

#include <unistd.h>
#include <fcntl.h>

sock = socket(PF_INET, SOCK_STREAM, 0);

int flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);

这样使用原本blocking的各种函数,可以立即获得返回结果。通过判断返回的errno了解状态:

accept():

在non-blocking模式下,如果返回值为-1,且errno == EAGAIN或errno == EWOULDBLOCK表示no connections没有新连接请求;

recv()/recvfrom():

在non-blocking模式下,如果返回值为-1,且errno == EAGAIN表示没有可接受的数据或正在接受尚未完成;

send()/sendto():

在non-blocking模式下,如果返回值为-1,且errno == EAGAIN或errno == EWOULDBLOCK表示没有可发送数据或数据发送正在进行没有完成。

read/write:

在non-blocking模式下,如果返回-1,且errno == EAGAIN表示没有可读写数据或可读写正在进行尚未完成。

connect():

在non-bloking模式下,如果返回-1,且errno = EINPROGRESS表示正在连接。

使用如上方法,可以创建一个non-blocking的server的程序,类似如下代码:

int main(int argc, char *argv[]) {
	int sock;
	if ( (sock = socket(PF_INET, SOCK_STREAM, 0)) == -1 ) {
		perror("socket failed");
		return 1;
	}

	//set socket to be non-blocking
	int flags = fcntl(sock, F_GETFL, 0);
	fcntl(sock, F_SETFL, flags | O_NONBLOCK);

	//create socket address to bind
	struct sockaddr_in bind_addr
	...

	//bind
	bind(...)
	...

	//listen
	listen(...)
	...

	//loop 
	int new_sock;
	while (1) {
		new_sock = accept(sock, NULL, NULL);
		if (new_sock == -1 && errno == EAGAIN) {
			fprintf(stderr, "no client connections yet\n");
			continue;
		} else if (new_sock == -1) {
			perror("accept failed");
			return 1;
		}

		//read and write
		...

	}   

	...
}

纯non-blocking程序缺点:

如果运行如上程序会发现调用accept可以理解返回,但这样会耗费大量的CPU time,实际中并不会这样使用。实际中将non-blocking和select结合使用。

三、non-blocking和select结合使用的方法

select通过轮询,监视指定file descriptor(包括socket)的变化,知道:哪些ready for reading, 哪些ready for writing,哪些发生了错误等。select和non-blocking结合使用可很好地实现socket的多client同步通信。

select函数:

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int maxfd, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);

参数说明:

maxfd:所有set中最大的file descriptor + 1
readfds:指定要侦听ready to read的file descriptor,可以为NULL
writefds:指定要侦听ready to write的file descriptor,可以为NULL
errorfds:指定要侦听errors的file descriptor,可以为NULL
timeout:指定侦听到期的时间长度,如果该struct timeval的各个域都为0,则相当于完全的non-blocking模式;如果该参数为NULL,相当于block模式;

select返回:

select返回total number of bits set in readfds, writefds and errorfds,当timeout的时候返回0,发生错误返回-1。

注:select会更新readfds(保存ready to read的file descriptor), writefds(保存read to write的fd), errorfds(保存error的fd),且更新timeout为距离超时时刻的剩余时间。

另外,fd_set类型需要使用如下4个宏进行赋值:

FD_ZERO(fd_set *set);       //Clear all entries from the set.
FD_SET(int fd, fd_set *set);    //Add fd to the set.
FD_CLR(int fd, fd_set *set);    //Remove fd from the set.
FD_ISSET(int fd, fd_set *set);  //Return true if fd is in the set.

因此通过如下代码可以将要侦听的file descriptor/socket添加到响应的fd_set中,例如:

fd_set readfds;
FD_ZERO(&readfds);

int sock;
sock = socket(PF_INET, SOCK_STREAM, 0);

FD_SET(sock, &readfds);     //将新创建的socket添加到readfds中
FD_SET(stdin, &readfds);    //将stdin添加到readfds中

struct timeval类型:

struct timeval {
	int tv_sec;     //seconds
	int tv_usec;    //microseconds,注意这里是微秒不是毫秒,1秒 = 1000, 000微秒
};

struct timeval {
int tv_sec; //seconds
int tv_usec; //microseconds,注意这里是微秒不是毫秒,1秒 = 1000, 000微秒
};

struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;

使用select和non-blocking实现server处理多client实例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>

#define DEFAULT_PORT    1984    //默认端口
#define BUFF_SIZE       1024    //buffer大小
#define SELECT_TIMEOUT  5       //select的timeout seconds

//函数:设置sock为non-blocking mode
void setSockNonBlock(int sock) {
	int flags;
	flags = fcntl(sock, F_GETFL, 0);
	if (flags < 0) {
		perror("fcntl(F_GETFL) failed");
		exit(EXIT_FAILURE);
	}
	if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
		perror("fcntl(F_SETFL) failed");
		exit(EXIT_FAILURE);
	}
}
//函数:更新maxfd
int updateMaxfd(fd_set fds, int maxfd) {
	int i;
	int new_maxfd = 0;
	for (i = 0; i <= maxfd; i++) {
		if (FD_ISSET(i, &fds) && i > new_maxfd) {
			new_maxfd = i;
		}
	}
	return new_maxfd;
}

int main(int argc, char *argv[]) {
	unsigned short int port;

	//获取自定义端口
	if (argc == 2) {
		port = atoi(argv[1]);
	} else if (argc < 2) {
		port = DEFAULT_PORT;
	} else {
		fprintf(stderr, "USAGE: %s [port]\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	//创建socket
	int sock;
	if ( (sock = socket(PF_INET, SOCK_STREAM, 0)) == -1 ) {
		perror("socket failed, ");
		exit(EXIT_FAILURE);
	}
	printf("socket done\n");

	//in case of 'address already in use' error message
	int yes = 1;
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))) {
		perror("setsockopt failed");
		exit(EXIT_FAILURE);
	}

	//设置sock为non-blocking
	setSockNonBlock(sock);

	//创建要bind的socket address
	struct sockaddr_in bind_addr;
	memset(&bind_addr, 0, sizeof(bind_addr));
	bind_addr.sin_family = AF_INET;
	bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);  //设置接受任意地址
	bind_addr.sin_port = htons(port);//将host byte order转换为network byte order

	//bind sock到创建的socket address上
	if ( bind(sock, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) == -1 ) {
		perror("bind failed, ");
		exit(EXIT_FAILURE);
	}
	printf("bind done\n");

	//listen
	if ( listen(sock, 5) == -1) {
		perror("listen failed.");
		exit(EXIT_FAILURE);
	}
	printf("listen done\n");

	//创建并初始化select需要的参数(这里仅监视read),并把sock添加到fd_set中
	fd_set readfds;
	fd_set readfds_bak; //backup for readfds(由于每次select之后会更新readfds,因此需要backup)
	struct timeval timeout;
	int maxfd;
	maxfd = sock;
	FD_ZERO(&readfds);
	FD_ZERO(&readfds_bak);
	FD_SET(sock, &readfds_bak);

	//循环接受client请求
	int new_sock;
	struct sockaddr_in client_addr;
	socklen_t client_addr_len;
	char client_ip_str[INET_ADDRSTRLEN];
	int res;
	int i;
	char buffer[BUFF_SIZE];
	int recv_size;

	while (1) {

		//注意select之后readfds和timeout的值都会被修改,因此每次都进行重置
		readfds = readfds_bak;
		maxfd = updateMaxfd(readfds, maxfd);        //更新maxfd
		timeout.tv_sec = SELECT_TIMEOUT;
		timeout.tv_usec = 0;
		printf("selecting maxfd=%d\n", maxfd);

		//select(这里没有设置writefds和errorfds,如有需要可以设置)
		res = select(maxfd + 1, &readfds, NULL, NULL, &timeout);
		if (res == -1) {
			perror("select failed");
			exit(EXIT_FAILURE);
		} else if (res == 0) {
			fprintf(stderr, "no socket ready for read within %d secs\n", SELECT_TIMEOUT);
			continue;
		}

		//检查每个socket,并进行读(如果是sock则accept)
		for (i = 0; i <= maxfd; i++) {
			if (!FD_ISSET(i, &readfds)) {
				continue;
			}
			//可读的socket
			if ( i == sock) {
				//当前是server的socket,不进行读写而是accept新连接
				client_addr_len = sizeof(client_addr);
				new_sock = accept(sock, (struct sockaddr *) &client_addr, &client_addr_len);
				if (new_sock == -1) {
					perror("accept failed");
					exit(EXIT_FAILURE);
				}
				if (!inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip_str, sizeof(client_ip_str))) {
					perror("inet_ntop failed");
					exit(EXIT_FAILURE);
				}
				printf("accept a client from: %s\n", client_ip_str);
				//设置new_sock为non-blocking
				setSockNonBlock(new_sock);
				//把new_sock添加到select的侦听中
				if (new_sock > maxfd) {
					maxfd = new_sock;
				}
				FD_SET(new_sock, &readfds_bak);
			} else {
				//当前是client连接的socket,可以写(read from client)
				memset(buffer, 0, sizeof(buffer));
				if ( (recv_size = recv(i, buffer, sizeof(buffer), 0)) == -1 ) {
					perror("recv failed");
					exit(EXIT_FAILURE);
				}
				printf("recved from new_sock=%d : %s(%d length string)\n", i, buffer, recv_size);
				//立即将收到的内容写回去,并关闭连接
				if ( send(i, buffer, recv_size, 0) == -1 ) {
					perror("send failed");
					exit(EXIT_FAILURE);
				}
				printf("send to new_sock=%d done\n", i);
				if ( close(i) == -1 ) {
					perror("close failed");
					exit(EXIT_FAILURE);
				}
				printf("close new_sock=%d done\n", i);
				//将当前的socket从select的侦听中移除
				FD_CLR(i, &readfds_bak);
			}
		}
	}

	return 0;
}

编译并运行如上程序,然后尝试使用多个telnet localhost 1984连接该server。可以发现各个connection很好地独立工作。因此,使用select可实现一个进程尽最大所能地处理尽可能多的client。

windows实现非阻塞:


bool set_sock_nonblock(int sock, bool isblock) {

	unsigned long ul = 0;
	if (!isblock) ul = 1;
	int re = ioctlsocket(sock, FIONBIO, (unsigned long*)&ul);
	if (re != 0) 
		return false;
	return true;
}
  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
第一章 概论 .................................................................................................................. 1 1.1 网络的历史................................................................. ...................................... 1 1.2 OSI 模型........................................................................................................... 3 1.3 Internet 体系模型.............................................................................................. 4 1.4 客户/服务器模型............................................................................................... 5 1.4 UNIX 的历史 ................................................................................................... 7 1.4.1 Unix 诞生前的故事 ................................................................................. 7 1.4.2 UNIX 的诞生.......................................................................................... 8 1.4.3 1979 – UNIX 第七版 ............................................................................. 10 1.4.4 UNIX 仅仅是历史吗?............................................................................. 11 1.5 Linux 的发展.................................................................................................. 11 1.5.1 Linux 的发展历史 .................................................................................. 12 1.5.2 什么叫 GNU? ...................................................................................... 12 1.5.3 Linux 的特色 ........................................................................................ 13 1.5.4 硬件需求............................................................................................... 14 1.5.5 Linux 可用的软件 ................................................................................. 14 1.5.6 为什么选择 Linux ? ............................................................................ 15 1.6 Linux 和 Unix 的发展 .................................................................................... 15 第二章 UNIX/Linux 模型...............................................................................................17 2.1 UNIX/Linux 基本结构.......................................................................................17 2.2 输入和输出......................................................................................................19 2.2.1 UNIX/Linux 文件系统简介 ......................................................................19 2.2.2 流和标准 I/O 库......................................................................................20 2.3 进程 ................................................................................................................21 第三章 进程控制 ..........................................................................................................22 3.1 进程的建立与运行 ...........................................................................................22 3.1.1 进程的概念 ............................................................................................22 3.1.2 进程的建立 ............................................................................................22 3.1.3 进程的运行 ............................................................................................24 3.1.4 数据和文件描述符的继承 .......................................................................29 3.2 进程的控制操作...............................................................................................31 3.2.1 进程的终止 ............................................................................................31 3.2.2 进程的同步 ............................................................................................32 3.2.3 进程终止的特殊情况 ..............................................................................33 3.2.4 进程控制的实例 .....................................................................................33 3.3 进程的属性......................................................................................................38 3.3.1 进程标识符 ............................................................................................38 3.3.2 进程的组标识符 .....................................................................................39 3.3.3 进程环境................................................................................................40 3.3.4 进程的当前目录 .....................................................................................42 3.3.5 进程的有效标识符..................................................................................43 3.3.6 进程的资源 ............................................................................................44 3.3.7 进程的优先级.........................................................................................45 3.4 守护进程 .........................................................................................................46 3.4.1 简介.......................................................................................................46 3.4.2 守护进程的启动 ............................................................................................46 3.4.3 守护进程的错误输出 ..............................................................................46 3.4.4 守护进程的建立 .....................................................................................48 3.5 本章小结 .........................................................................................................49 第四章 进程间通信.......................................................................................................50 4.1 进程间通信的一些基本概念 .............................................................................50 4.2 信号 ................................................................................................................50 4.2.1 信号的处理 ............................................................................................52 4.2.2 信号与系统调用的关系...........................................................................54 4.2.3 信号的复位 ............................................................................................55 4.2.4 在进程间发送信号..................................................................................56 4.2.5 系统调用 alarm()和 pause()......................................................................58 4.2.6 系统调用 setjmp()和 longjmp().................................................................62 4.3 管道 ................................................................................................................63 4.3.1 用 C 来建立、使用管道 ..........................................................................65 4.3.2 需要注意的问题 .....................................................................................72 4.4 有名管道 .........................................................................................................72 4.4.1 有名管道的创建 .....................................................................................72 4.4.2 有名管道的 I/O 使用...............................................................................73 4.4.3 未提到的关于有名管道的一些注意 .........................................................75 4.5 文件和记录锁定...............................................................................................75 4.5.1 实例程序及其说明..................................................................................75 4.5.2 锁定中的几个概念..................................................................................78 4.5.3 System V 的咨询锁定..............................................................................78 4.5.4 BSD 的咨询式锁定 .................................................................................79 4.5.5 前面两种锁定方式的比较 .......................................................................81 4.5.6 Linux 的其它上锁技术 ............................................................................81 4.6 System V IPC ...................................................................................................84 4.6.1 ipcs 命令 ................................................................................................85 4.6.2 ipcrm 命令..............................................................................................86 4.7 消息队列(Message Queues)...........................................................................86 4.7.1 有关的数据结构 .....................................................................................86 4.7.2 有关的函数 ............................................................................................89 4.7.3 消息队列实例——msgtool,一个交互式的消息队列使用工具 ..................94 4.8 信号量(Semaphores) .........................................................................................97 4.8.1 有关的数据结构 .....................................................................................98 4.8.2 有关的函数 ............................................................................................99 4.8.3 信号量的实例——semtool,交互式的信号量使用工具........................... 103 4.9 共享内存(Shared Memory) .............................................................................. 109 4.9.1 有关的数据结构 ................................................................................... 109 4.9.2 有关的函数 .......................................................................................... 110 4.9.3 共享内存应用举例——shmtool,交互式的共享内存使用工具................... 112 4.9.4 共享内存与信号量的结合使用 .............................................................. 114 第五章 通信协议简介 ................................................................................................. 120 5.1 引言 .............................................................................................................. 120 5.2 XNS(Xerox Network Systems)概述.............................................................. 120 5.2.1 XNS 分层结构...................................................................................... 120 IPX/SPX 协议概述........................................................................................ 122 5.3 5.3.1 网际包交换(IPX) ............................................................................. 122 5.3.2 排序包交换(SPX)............................................................................. 124 5.4 Net BIOS 概述................................................................................................ 124 5.5 Apple Talk 概述 .............................................................................................. 125 5.6 TCP/IP 概述................................................................................................... 126 5.6.1 TCP/IP 结构模型 .................................................................................. 126 5.6.2 Internet 协议(IP)............................................................................... 127 5.6.3 传输控制协议(TCP) ......................................................................... 132 5.6.4 用户数据报文协议................................................................................ 134 5.7 小结 .............................................................................................................. 135 第六章 Berkeley 套接字 ............................................................................................. 136 6.1 引言 ............................................................................................................. 136 6.2 概述 ............................................................................................................. 136 6.2.1 Socket 的历史...................................................................................... 136 6.2.2 Socket 的功能...................................................................................... 136 6.2.3 套接字的三种类型............................................................................... 138 6.3 Linux 支配的网络协议................................................................................... 141 6.3.1 什么是 TCP/IP? ................................................................................... 141 6.4 套接字地址................................................................................................... 142 6.4.1 什么是 Socket? .................................................................................. 142 6.4.2 Socket 描述符...................................................................................... 142 6.4.3 一个套接字是怎样在网络上传输数据的?............................................ 143 6.5 套接字的一些基本知识 ................................................................................. 144 6.5.1 基本结构............................................................................................. 144 6.5.2 基本转换函数...................................................................................... 145 6.6 基本套接字调用............................................................................................ 147 6.6.1 socket() 函数....................................................................................... 147 6.6.2 bind() 函数 ......................................................................................... 148 6.6.3 connect()函数 ...................................................................................... 150 6.6.4 listen() 函数........................................................................................ 151 6.6.5 accept()函数 ........................................................................................ 152 6.6.6 send()、recv()函数 ............................................................................... 154 6.6.7 sendto() 和 recvfrom() 函数 ................................................................. 155 6.6.8 close()和 shutdown()函数...................................................................... 156 6.6.9 setsockopt() 和 getsockopt() 函数 ......................................................... 157 6.6.10 getpeername()函数.............................................................................. 157 6.6.11 gethostname()函数.............................................................................. 158 6.7 DNS 的操作.................................................................................................. 158 6.7.1 理解 DNS............................................................................................ 158 6.7.2 和 DNS 有关的函数和结构 .................................................................. 158 6.7.3 DNS 例程............................................................................................ 159 6.8 套接字的 Client/Server 结构实现的例子.......................................................... 160 6.8.1 简单的流服务器 .................................................................................. 161 6.8.2 简单的流式套接字客户端程序 ............................................................. 163 6.8.3 数据报套接字例程(DatagramSockets)............................................... 165 6.9 保留端口 ...................................................................................................... 169 6.9.1 简介.................................................................................................... 169 6.9.2 保留端口............................................................................................. 170 6.10 五种 I/O 模式................................................................................................. 179 6.10.1 阻塞 I/O 模式 .................................................................................... 179 6.10.2 非阻塞模式 I/O.................................................................................. 180 6.10.3 I/O 多路复用 ..................................................................................... 181 6.10.4 信号驱动 I/O 模式 ............................................................................. 182 6.10.5 异步 I/O 模式 .................................................................................... 185 6.10.6 几种 I/O 模式的比较.......................................................................... 186 6.10.7 fcntl()函数 ......................................................................................... 186 6.10.8 套接字选择项 select()函数.................................................................. 187 6.11 带外数据..................................................................................................... 190 6.11.1 TCP 的带外数据 ................................................................................ 190 6.11.2 OOB 传输套接字例程(服务器代码 Server.c) ................................... 193 6.11.3 OOB 传输套接字例程(客户端代码 Client.c).................................... 196 6.11.4 编译例子 ........................................................................................... 199 6.12 使用 Inetd(Internet 超级服务器) ............................................................... 199 6.12.1 简介.................................................................................................. 199 6.12.2 一个简单的 inetd 使用的服务器程序 hello inet service.......................... 199 6.12.3 /etc/services 和 /etc/inetd.conf 文件 ..................................................... 200 6.12.4 一个复杂一些的 inetd 服务器程序 ...................................................... 201 6.12.5 一个更加复杂的 inetd 服务器程序 ...................................................... 203 6.12.6 程序必须遵守的安全性准则............................................................... 205 6.12.7 小结.................................................................................................. 205 6.13 本章总结 .................................................................................................... 205 第七章 网络安全性..................................................................................................... 206 7.1 网络安全简介 ................................................................................................ 206 7.1.1 网络安全的重要性................................................................................ 206 7.1.2 信息系统安全的脆弱性......................................................................... 207 7.2 Linux 网络不安全的因素 ................................................................................ 209 7.3 Linux 程序员安全........................................................................................... 211 7.3.1 系统子程序 .......................................................................................... 212 7.3.2 标准 C 函数库....................................................................................... 214 7.3.3 书写安全的 C 程序................................................................................ 216 7.3.4 SUID/SGID 程序指导准则...................................................................... 217 7.3.5 root 程序的设计.................................................................................... 218 7.4 小结 .............................................................................................................. 219 第八章 Ping 例程 ....................................................................................................... 220 8.1 Ping 命令简介 ................................................................................................ 220 8.2 Ping 的基本原理............................................................................................. 220 8.3 小结 .............................................................................................................. 221 第九章 tftp 例程......................................................................................................... 222 9.1 tftp 协议简介.................................................................................................. 222 9.2 tftp 的使用 ..................................................................................................... 222 9.3 tftp 的原理 ..................................................................................................... 223 9.3 tftp 的基本结构 .............................................................................................. 223 9.4 小节 .............................................................................................................. 225 第十章 远程命令执行 ................................................................................................. 226 10.1 引言 ............................................................................................................ 226 10.2 rcmd 函数和 rshd 服务器............................................................................... 227 10.3 rexec 函数和 rexecd 服务器........................................................................... 233 第十一章 远程注册..................................................................................................... 235 11.1 简介............................................................................................................. 235 11.2 终端行律和伪终端........................................................................................ 235 11.3 终端方式字和控制终端................................................................................. 239 11.4 rlogin 概述.................................................................................................... 242 11.5 窗口环境...................................................................................................... 242 11.6 流控制与伪终端方式字................................................................................. 243 11.7 rlogin 客户程序............................................................................................. 245 11.8 rlogin 服务器 ................................................................................................ 246 第十二章 远程过程调用.............................................................................................. 249 12.1 引言 ............................................................................................................ 249 12.2 远程过程调用模型 ....................................................................................... 249 12.3 传统过程调用和远程过程调用的比较 ........................................................... 250 12.4 远程过程调用的定义.................................................................................... 252 12.5 远程过程调用的有关问题............................................................................. 252 12.5.1 远程过程调用传送协议....................................................................... 253 12.5.2 Sun RPC ........................................................................................... 254 12.5.3 Xerox Courier .................................................................................... 254 12.5.4 Apollo RPC........................................................................................ 255 12.6 stub 过程简介............................................................................................... 256 12.7 rpcgen 简介 .................................................................................................. 256 12.8 分布式程序生成的例子 ................................................................................ 257 12.8.1 我们如何能够构造出一个分布式应用程序........................................... 257 12.9 小结 ............................................................................................................ 283 第十三章 远程磁带的访问 .......................................................................................... 284 13.1 简介 ............................................................................................................ 284 13.2 Linux 磁带驱动器的处理 .............................................................................. 285 13.3 rmt 协议....................................................................................................... 285 13.4 rmt 服务器设计分析 ..................................................................................... 286 第十四章 WWW 上 HTTP 协议.................................................................................. 290 14.1 引言............................................................................................................ 290 14.2 HTTP 客户请求........................................................................................... 290 14.2.1 客户端 .............................................................................................. 290 14.2.2 服务器端........................................................................................... 290 14.2.3 Web 请求简介.................................................................................... 291 14.2.4 HTTP – HyperText Transfer Protocol 超文本传输协议 ........................... 295 14.3 Web 编程 .................................................................................................... 297 14.4 小结 ........................................................................................................... 301 附录 A 有关网络通信的服务和网络库函数................................................................... 302 附录 B Vi 使用简介..................................................................................................... 319 B.1 Vi 基本观念................................................................................................... 319 B.1.1 进入与离开.......................................................................................... 319 B.1.2 Vi 输入模式 ......................................................................................... 319 B.2 Vi 基本编辑................................................................................................... 320 B.2.1 删除与修改.......................................................................................... 320 B.3 Vi 进阶应用................................................................................................... 320 B.3.1 移动光标 ............................................................................................. 320 B.3.2 进阶编辑命令 ...................................................................................... 322 B.3.3 文件命令 ............................................................................................. 322 附录 C Linux 下 C 语言使用与调试简介 ...................................................................... 324 C.1 C 语言编程 ................................................................................................... 324 C.2 什么是 C? ..................................................................................................... 324 C.3 GNU C 编译器............................................................................................... 324 C.3.1 使用 GCC ............................................................................................ 324 C.3.2 GCC 选项 ............................................................................................ 325 C.3.3 优化选项 ............................................................................................. 325 C.3.4 调试和剖析选项................................................................................... 325 C.3.5 用 gdb 调试 GCC 程序.......................................................................... 326 C.4 另外的 C 编程工具 ........................................................................................ 330 C.4.1 Xxgdb.................................................................................................. 330 C.4.2 Calls .................................................................................................... 331 C.4.3 cproto .................................................................................................. 332 C.4.4 Indent .................................................................................................. 333 C.4.5 Gprof................................................................................................... 334 C.4.6 f2c 和 p2c ............................................................................................ 335 附录 D Ping 源码 ........................................................................................................ 336 附录 E TFTP 服务器程序源码 ..................................................................................... 362
实验(No. 4)题目:简单的客户/服务器程序设计与实现 实验目的及要求: 1、熟悉Microsoft Visual Studio 2008编程环境。 2、了解TCP与UDP协议,以及它们之间的区别。 3、了解客户/服务器模型原理。 4、熟悉Socket编程原理,掌握简单的套接字编程。 实验设备: 硬件:PC机(两台以上)、网卡、已经设定好的以太网环境 软件:Microsoft Visual Studio 2008 实验内容及步骤: 1、编写用TCP协议实现的Client端和Server端程序并调试通过。 程序分两部分:客户程序和服务器程序。 工作过程是: 服务器首先启动,它创建套接字之后等待客户的连接;客户启动后创建套接字,然后 和服务器建立连接;建立连接后,客户接收键盘输入,然后将数据发送到服务器,服 务器收到到数据后,将接收到的字符在屏幕上显示出来。或者服务器接收键盘输入, 然后将数据发送到客户机,客户机收到数据后,将接收到的字符在屏幕上显示出来。 程序流程如下: 2、编写用UDP协议实现的Client端和Server端程序并调试通过(做完第一个实验的基础 上做该实验)。 3、编写用TCP协议实现Client端与Server端的一段对话程序。Server端根据用户的输入 来提示Client端下一步将要进行操作。 所用函数及结构体参考: 1、创建套接字——socket() 功能:使用前创建一个新的套接字 格式:SOCKET PASCAL FAR socket(int af, int type, int procotol); 参数:af:代表网络地址族,目前只有一种取值是有效的,即AF_INET,代表internet地 址族; Type:代表网络协议类型,SOCK_DGRAM代表UDP协议,SOCK_STREAM代表TCP协议; Protocol:指定网络地址族的特殊协议,目前无用,赋值0即可。 返回值为SOCKET,若返回INVALID_SOCKET则失败。 2、指定本地地址——bind() 功能:将套接字地址与所创建的套接字号联系起来。 格式:int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR * name, int namelen); 参数:s: 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。 其它:没有错误,bind()返回0,否则SOCKET_ERROR 地址结构说明: struct sockaddr_in { short sin_family;//AF_INET u_short sin_port;//16位端口号,网络字节顺序 struct in_addr sin_addr;//32位IP地址,网络字节顺序 char sin_zero[8];//保留 } 3、建立套接字连接——connect()和accept() 功能:共同完成连接工作 格式:int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR * name, int namelen); SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR * name, int FAR * addrlen); 参数:s: 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。 4、监听连接——listen() 功能:用于面向连接服务器,表明它愿意接收连接。 格式:int PASCAL FAR listen(SOCKET s, int backlog); 5、数据传输——send()与recv() 功能:数据的发送与接收 格式:int PASCAL FAR send(SOCKET s, const char FAR* buf, int len, int flags); int PASCAL FAR recv(SOCKET s, const char FAR * buf, int len, int flags); 参数:buf:指向存有传输数据的缓冲区的指针。 6、多路复用——select() 功能:用来检测一个或多个套接字状态。 格式:int PASCAL FAR select(int nfds, fd_set FAR* readfds, fd_set FAR* writefds, fd_set FAR * exceptfds, const struct timeval FAR* timeout); 参数:readfds:指向要做读检测的指针 writefds:指向要做写检测的指针 exceptfds:指向要检测是否出错的指针 timeout:最大等待时间 7、

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值