linux网络编程-TCP套接字通信实验,多路复用

代码分析 client.c

  程序分析client.c

//client.c
//
//  TCP 套接字编程典型模型
//
//		客户端							服务器端
//	创建套接字(socket)				创建套接字(socket)
//									绑定服务器地址和端口(bind)
//									监听端口(listen)
//	连接服务器(connect)      ---->	接受客户端连接(accept)
//	客户端发送请求(send)     ---->	接收客户端请求(recv)
//	客户端接收响应(recv)     <----	回送响应(send)
//	关闭套接字(close)        <--->	关闭套接字(close)
//man 7 socket
//accept() , bind() , connect() ,  getsockname()  ,  getsockopt()  ,
//listen()  , recv() , recvfrom() , recvmsg() , send() , sendmsg() ,
//sendto() , setsockopt() , shutdown() , socket() , socketpair() 
//
//socket - create an endpoint for communication
//#include <sys/socket.h>
//int socket(int domain, int type, int protocol);
//RETURN VALUE
//Upon successful completion, socket() shall return  a  non-negative
//integer,  the  socket  file  descriptor.  Otherwise, a value of -1
//shall be returned and errno set to indicate the error.
//
//bind - bind a name to a socket
//#include <sys/socket.h>
//int bind(int socket, const struct sockaddr *address,
//        socklen_t address_len);
//RETURN VALUE
//Upon  successful  completion, bind() shall return 0; otherwise, -1
//shall be returned and errno set to indicate the error.
//
//listen  -  listen  for  socket  connections  and limit the queue of
//incoming connections
//#include <sys/socket.h>
//int listen(int socket, int backlog);
//RETURN VALUE
//Upon successful completions, listen() shall return 0; otherwise, -1
//shall be returned and errno set to indicate the error.
//
//connect - connect a socket
//#include <sys/socket.h>
//int connect(int socket, const struct sockaddr *address,
//            socklen_t address_len);
//RETURN VALUE
//Upon successful completion, connect() shall return 0; otherwise, -1
//shall be returned and errno set to indicate the error.
//
//accept - accept a new connection on a socket
//#include <sys/socket.h>
//int accept(int socket, struct sockaddr *restrict address,
//          socklen_t *restrict address_len);
//RETURN VALUE
//Upon  successful completion, accept() shall return the non-negative
//file descriptor of the accepted  socket.  Otherwise,  -1  shall  be
//returned and errno set to indicate the error.
//
//send, sendto, sentmsg  - send a message on a socket
//#include <sys/socket.h>
//ssize_t  send(int  socket,  const  void *buffer, size_t length, int
//flags);
//ssize_t sendto(int socket, const void *message, size_t length,
//    int flags, const struct sockaddr *dest_addr,socklen_t dest_len);
//ssize_t  sendmsg(int  socket,  const  struct  msghdr  *message, int
//flags);
//RETURN VALUE
//Upon successful completion, shall return the number of bytes
//sent. Otherwise, -1 shall be returned and errno set to indicate the
//error.
//
//recv, recvfrom, recvmsg - receive a message from a socket
//#include <sys/types.h>
//#include <sys/socket.h>
//ssize_t recv(int sockfd, void *buf, size_t len, int flags);
//
//ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
//                struct sockaddr *src_addr, socklen_t *addrlen);
//
//ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
//RETURN VALUE
//These  calls return the number of bytes received, or -1 if an error
//occurred. The  return value will be 0 when the peer has performed an
//orderly shutdown.
//
//struct iovec {                    /* Scatter/gather array items */
//    void  *iov_base;              /* Starting address */
//    size_t iov_len;               /* Number of bytes to transfer */
//};
//
//struct msghdr {
//    void         *msg_name;       /* optional address */
//    socklen_t     msg_namelen;    /* size of address */
//    struct iovec *msg_iov;        /* scatter/gather array */
//    size_t        msg_iovlen;     /* # elements in msg_iov */
//    void         *msg_control;    /* ancillary data, see below */
//    size_t        msg_controllen; /* ancillary data buffer len */
//    int           msg_flags;      /* flags on received message */
//};
//
//socketpair - create a pair of connected sockets
//#include <sys/socket.h>
//int socketpair(int domain, int type, int protocol,
//                            int socket_vector[2]);
//RETURN VALUE
//Upon  successful  completion,  this function shall return 0; 
//otherwise, -1 shall be returned and errno set to indicate the error.
//
//setsockopt - set the socket options
//#include <sys/socket.h>
//int setsockopt(int socket, int level, int option_name,
//               const void *option_value, socklen_t option_len);
//RETURN VALUE
//Upon successful completion, setsockopt() shall return 0. 
//Otherwise, -1 shall be returned and errno set to indicate the error.
//
//getsockopt - get the socket options
//#include <sys/socket.h>
//int getsockopt(int socket, int level, int option_name,
//    void *restrict option_value, socklen_t *restrict option_len);
//RETURN VALUE
//Upon successful completion, getsockopt() shall return 0; 
//otherwise, -1 shall be returned and errno set to indicate the error.
//
//getsockname - get the socket name
//#include <sys/socket.h>
//int getsockname(int socket, struct sockaddr *restrict address,
//                socklen_t *restrict address_len);
//RETURN VALUE
//Upon successful completion, 0 shall be returned, the address argu‐
//ment shall point to the address of the socket, and the address_len
//argument  shall point to the length of the address.  Otherwise, -1
//shall be returned and errno set to indicate the error.
//
//shutdown - shut down socket send and receive operations
//#include <sys/socket.h>
//int shutdown(int socket, int how);
//how    Specifies the type of shutdown. The values are as follows:
//SHUT_RD		Disables further receive operations.
//SHUT_WR		Disables further send operations.
//SHUT_RDWR		Disables further send and receive operations.
//RETURN VALUE
//Upon  successful completion, shutdown() shall return 0; otherwise,
//-1 shall be returned and errno set to indicate the error.
//
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

#define PORT	2345
#define BUF_SIZE	1024
#define MAX_QUE_NU	5

int main(int argc, char *argv[])
{
	struct sockaddr_in *sin_addrptr;
	struct sockaddr serv_sa;
	struct addrinfo *serv_aiptr, hints, *res_aiptr ;
	int sockfd, res;
	char buf[BUF_SIZE];
	char serv_name[80];

	memset(serv_name, 0, sizeof(serv_name));
	memset(buf, 0, sizeof(buf));
	strcpy(serv_name, "127.0.0.1");
	strcpy(buf, "你好服务器!");

	if (argc > 1)
	{
		memset(serv_name, 0, sizeof(serv_name));
		strcpy(serv_name, argv[1]);
	}
	if (argc > 2)
	{
		memset(buf, 0, sizeof(buf));
		strcpy(buf, argv[2]);
	}

	//服务器地址解析函数getaddrinfo
	//int getaddrinfo(const char *node, const char *service,           
	//                const struct addrinfo *hints,
	//                struct addrinfo **res);
	//
	memset(&hints, 0, sizeof(hints));
	hints.ai_flags = AI_PASSIVE;
	hints.ai_family = AF_INET;
	
	if((res = getaddrinfo(serv_name, NULL, &hints, &res_aiptr)) !=0)
	{
		fprintf(stderr, "Error getaddrinfo %s\n",gai_strerror(res));
		exit(-1);
	}
	for(serv_aiptr = res_aiptr;serv_aiptr != NULL; serv_aiptr++)
	{
		if(serv_aiptr->ai_family == AF_INET)
		{
			serv_sa = *(serv_aiptr->ai_addr);
			sin_addrptr = (struct sockaddr_in *) &serv_sa;
			break;
		}
	}
	if (serv_aiptr == NULL)
	{
		freeaddrinfo(res_aiptr);
		printf("不能发现服务器");
		exit(-1);
	}
	freeaddrinfo(res_aiptr);

	//建立socket连接
	//int socket(int domain, int type, int protocol);
	//以默认协议,在INET域建立数据流套接字
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("Error socket");
		exit(-1);
	}
	printf("套接字 ID = %d\n", sockfd);
	
	//设置服务器sockaddr_in 结构体相关参数
	sin_addrptr->sin_port = htons(PORT);

	//调用 connect 函数主动发起对服务器端的连接
	//int connect(int socket, const struct sockaddr *address,
	//            socklen_t address_len);
	if((connect(sockfd, &serv_sa, sizeof(serv_sa))) == -1)
	{
		perror("Error connect");
		exit(-1);
	}
	
	//发送消息到服务器端
	//ssize_t  send(int  socket,  const  void *buffer, size_t length, int
	if ((send(sockfd, buf, strlen(buf), 0)) == -1)
	{
		perror("Error send");
		exit(-1);
	}

	//接收服务器端发送的消息
	//ssize_t recv(int sockfd, void *buf, size_t len, int flags);
	memset(buf, 0, sizeof(buf));
	if ((res = recv(sockfd, buf, sizeof(buf), 0)) == -1)
	{
		perror("Error recv");
		exit(-1);
	}
	printf("接收到服务器信息:%s(%d个字节)\n", buf, res);
	close(sockfd);
	exit(0);
}

代码分析 server.c

  程序分析server.c

//server.c
//使用select函数进行多路复用,允许多个客户端同事请求服务器
//
//  TCP 套接字编程典型模型
//
//		客户端							服务器端
//	创建套接字(socket)				创建套接字(socket)
//									绑定服务器地址和端口(bind)
//									监听端口(listen)
//	连接服务器(connect)     ---->	接受客户端连接(accept)
//	客户端发送请求(send)    ---->	接收客户端请求(recv)
//	客户端接收响应(recv)    <----	回送响应(send)
//	关闭套接字(close)       <--->	关闭套接字(close)
//man 7 socket
//accept() , bind() , connect() ,  getsockname()  ,  getsockopt()  ,
//listen()  , recv() , recvfrom() , recvmsg() , send() , sendmsg() ,
//sendto() , setsockopt() , shutdown() , socket() , socketpair() 
//
//socket - create an endpoint for communication
//#include <sys/socket.h>
//int socket(int domain, int type, int protocol);
//RETURN VALUE
//Upon successful completion, socket() shall return  a  non-negative
//integer,  the  socket  file  descriptor.  Otherwise, a value of -1
//shall be returned and errno set to indicate the error.
//
//bind - bind a name to a socket
//#include <sys/socket.h>
//int bind(int socket, const struct sockaddr *address,
//        socklen_t address_len);
//RETURN VALUE
//Upon  successful  completion, bind() shall return 0; otherwise, -1
//shall be returned and errno set to indicate the error.
//
//listen  -  listen  for  socket  connections  and limit the queue of
//incoming connections
//#include <sys/socket.h>
//int listen(int socket, int backlog);
//RETURN VALUE
//Upon successful completions, listen() shall return 0; otherwise, -1
//shall be returned and errno set to indicate the error.
//
//connect - connect a socket
//#include <sys/socket.h>
//int connect(int socket, const struct sockaddr *address,
//            socklen_t address_len);
//RETURN VALUE
//Upon successful completion, connect() shall return 0; otherwise, -1
//shall be returned and errno set to indicate the error.
//
//accept - accept a new connection on a socket
//#include <sys/socket.h>
//int accept(int socket, struct sockaddr *restrict address,
//          socklen_t *restrict address_len);
//RETURN VALUE
//Upon  successful completion, accept() shall return the non-negative
//file descriptor of the accepted  socket.  Otherwise,  -1  shall  be
//returned and errno set to indicate the error.
//
//send, sendto, sentmsg  - send a message on a socket
//#include <sys/socket.h>
//ssize_t  send(int  socket,  const  void *buffer, size_t length, int
//flags);
//ssize_t sendto(int socket, const void *message, size_t length,
//    int flags, const struct sockaddr *dest_addr,socklen_t dest_len);
//ssize_t  sendmsg(int  socket,  const  struct  msghdr  *message, int
//flags);
//RETURN VALUE
//Upon successful completion, shall return the number of bytes
//sent. Otherwise, -1 shall be returned and errno set to indicate the
//error.
//
//recv, recvfrom, recvmsg - receive a message from a socket
//#include <sys/types.h>
//#include <sys/socket.h>
//ssize_t recv(int sockfd, void *buf, size_t len, int flags);
//
//ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
//                struct sockaddr *src_addr, socklen_t *addrlen);
//
//ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
//RETURN VALUE
//These  calls return the number of bytes received, or -1 if an error
//occurred. The  return value will be 0 when the peer has performed an
//orderly shutdown.
//
//struct iovec {                    /* Scatter/gather array items */
//    void  *iov_base;              /* Starting address */
//    size_t iov_len;               /* Number of bytes to transfer */
//};
//
//struct msghdr {
//    void         *msg_name;       /* optional address */
//    socklen_t     msg_namelen;    /* size of address */
//    struct iovec *msg_iov;        /* scatter/gather array */
//    size_t        msg_iovlen;     /* # elements in msg_iov */
//    void         *msg_control;    /* ancillary data, see below */
//    size_t        msg_controllen; /* ancillary data buffer len */
//    int           msg_flags;      /* flags on received message */
//};
//
//socketpair - create a pair of connected sockets
//#include <sys/socket.h>
//int socketpair(int domain, int type, int protocol,
//                            int socket_vector[2]);
//RETURN VALUE
//Upon  successful  completion,  this function shall return 0; 
//otherwise, -1 shall be returned and errno set to indicate the error.
//
//setsockopt - set the socket options
//#include <sys/socket.h>
//int setsockopt(int socket, int level, int option_name,
//               const void *option_value, socklen_t option_len);
//RETURN VALUE
//Upon successful completion, setsockopt() shall return 0. 
//Otherwise, -1 shall be returned and errno set to indicate the error.
//
//getsockopt - get the socket options
//#include <sys/socket.h>
//int getsockopt(int socket, int level, int option_name,
//    void *restrict option_value, socklen_t *restrict option_len);
//RETURN VALUE
//Upon successful completion, getsockopt() shall return 0; 
//otherwise, -1 shall be returned and errno set to indicate the error.
//
//getsockname - get the socket name
//#include <sys/socket.h>
//int getsockname(int socket, struct sockaddr *restrict address,
//                socklen_t *restrict address_len);
//RETURN VALUE
//Upon successful completion, 0 shall be returned, the address argu‐
//ment shall point to the address of the socket, and the address_len
//argument  shall point to the length of the address.  Otherwise, -1
//shall be returned and errno set to indicate the error.
//
//shutdown - shut down socket send and receive operations
//#include <sys/socket.h>
//int shutdown(int socket, int how);
//how    Specifies the type of shutdown. The values are as follows:
//SHUT_RD		Disables further receive operations.
//SHUT_WR		Disables further send operations.
//SHUT_RDWR		Disables further send and receive operations.
//RETURN VALUE
//Upon  successful completion, shutdown() shall return 0; otherwise,
//-1 shall be returned and errno set to indicate the error.
//
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>

#define PORT	2345
#define BUF_SIZE	1024
#define MAX_QUE_NU	5
//#define MAX_SOCK_FD	FD_SETSIZE

int main(int argc, char *argv[])
{
	struct sockaddr_in serv_addr, clie_addr;
	int sin_size, res;
	int sockfd, cliefd, fd;
	fd_set inset, tmp_inset;
	char buf[BUF_SIZE];
	//char str[INET_ADDRSTRLEN];
	struct timeval tv;
	int maxfd;

	if (argc > 1)
	{
		tv.tv_sec = atoi(argv[1]);
	}
	else
	{
		tv.tv_sec = 60;
	}

	//建立socket连接
	//int socket(int domain, int type, int protocol);
	//以默认协议,在INET域建立数据流套接字
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("Error scoket");
		exit(-1);
	}
	printf("套接字 ID = %d", sockfd);

	//设置sockaddr_in结构中的相关参数
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(PORT);
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	bzero(&(serv_addr.sin_zero), 8);

	int i = 1;	//允许重复使用本地地址与套接字进行绑定
	//int setsockopt(int socket, int level, int option_name,
	//               const void *option_value, socklen_t option_len);
	if ((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i))) == -1)
	{
		perror("Error setsockopt");
		exit(-1);
	}

	//绑定端口及服务器地址 bind函数
	//int bind(int socket, const struct sockaddr *address,
	//        socklen_t address_len);
			if((bind(sockfd, (struct sockaddr *)&serv_addr,\
						sizeof(struct sockaddr))) == -1)
		{
			perror("Error bind");
			exit(-1);
		}
		printf("绑定成功!\n");

		//用 listen函数创建未处理请求队列
		//int listen(int socket, int backlog);
		if((listen(sockfd, MAX_QUE_NU)) == -1)
		{
			perror("Error listen");
			exit(-1);
		}
		printf("监听端口中......\n");
		//初始化select函数监听的文件集合
		maxfd = sockfd;
		FD_ZERO(&inset);
		FD_SET(sockfd, &inset);
		while(1)
		{
			tmp_inset = inset;
			//调用select函数
			//int select(int nfds, fd_set *readfds, fd_set *writefds,
			//          fd_set *exceptfds, struct timeval *timeout);
			res = select(maxfd + 1, &tmp_inset, NULL, NULL, &tv);
			if (res == -1)		
			{
				perror("Error select");
				exit(-1);
			}
			else if (res == 0)	//	超时
			{
				break;
			}
			
			//轮询select事件
			for (fd = 0; fd < maxfd + 1; fd++)
			{
				if(FD_ISSET(fd, &tmp_inset))
				{
					if (fd == sockfd)
					{
						//调用accept函数接受客户端的连接
						//int accept(int socket, struct sockaddr *restrict address,
						//          socklen_t *restrict address_len);
						sin_size = sizeof(struct sockaddr);
						if((cliefd = accept(sockfd, (struct sockaddr *)&clie_addr,\
										(socklen_t *)&sin_size)) == -1)
						{
							perror("Error accept");
							exit(-1);
						}
						
						//接受连接请求,并将客户端sockfd加入select检测列表
						
						maxfd = cliefd > maxfd ? cliefd : maxfd;

						FD_SET(cliefd, &inset);
						printf("来自%d(socket)的新的连接\n", cliefd);
					}
					else
					{

						//inet_ntop(AF_INET, &clie_addr.sin_addr, str, INET_ADDRSTRLEN);
						//调用recv 接受客户端请求
						//ssize_t recv(int sockfd, void *buf, size_t len, int flags);
						memset(buf, 0, sizeof(buf));
						if((res = recv(fd, buf, sizeof(buf), 0)) < 1)
						{
							//perror("Error recv");
							//exit(-1);
							close(fd);
							FD_CLR(fd, &inset);
							printf("客户端%d(socket)已退出\n", fd);
						}
						else
						{
							printf("接收到来自(%d(socket))的消息:%s(%d个字节)\n", fd, buf, res);
							
							//调用 send 函数回复一条消息
							//ssize_t  send(int  socket,  const  void *buffer, size_t length,
							//				int flags);
							//
							memset(buf, 0, sizeof(buf));
							sprintf(buf, "%d(socket)欢迎登录!", fd);

							if((res = send(fd, buf, strlen(buf), 0)) == -1)
							{
								perror("Error send");
								exit(-1);
							}
						}	//end if res=recv
					}	// end if fd
				}	//end if FD_ISSET
			}	// end for fd
		} // end while
		close(sockfd);
		exit(0);
}

执行结果

  客户端可以带参也可以缺省,用默认值。
执行结果