socket 和 select

socket server端和client端过程如下:

 服务器端的步骤如下:
1. socket:      建立一个socket
2. bind:          将这个socket绑定在某个文件上(AF_UNIX)或某个端口上(AF_INET)。
3. listen:        开始监听
4. accept:      如果监听到客户端连接,则调用accept接收这个连接并同时新建一个socket来和客户进行通信
5. read/write:读取或发送数据到客户端
6. close:        通信完成后关闭socket

客户端的步骤如下:
1. socket:      建立一个socket
2. connect:   主动连接服务器端的某个文件(AF_UNIX)或某个端口(AF_INET)
3. read/write:如果服务器同意连接(accept),则读取或发送数据到服务器端
4. close:        通信完成后关闭socket

图形如下:

AF_UNIX用的sockaddr_in结构体(在netinet/in.h中定义),AF_INET用的是sockaddr_un结构体(在netinet/un.h中定义)

 struct sockaddr {
unsigned short sa_family;     	/* address family, AF_xxx */
char sa_data[14];          		/* 14 bytes of protocol address */
};
/*sa_family是地址家族,一般都是“AF_xxx”的形式。好像通常大多用的是都是AF_INET。
sa_data是14字节协议地址。
此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。
但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构
*/


//sockaddr_in(在netinet/in.h中定义):
struct sockaddr_in {
short int sin_family;             	/* Address family */
unsigned short int sin_port;       	/* Port number */
struct in_addr sin_addr;         	/* Internet address */
unsigned char sin_zero[8];      	/* Same size as struct sockaddr */
};

/*
sin_family 指代协议族,在socket编程中只能是AF_INET
sin_port 存储端口号(使用网络字节顺序)
sin_addr 存储IP地址,使用in_addr这个数据结构
sin_zero 是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
sockaddr_in 和 sockaddr 是并列的结构,指向sockaddr_in的结构体的指针也可以指向 sockadd 的结构体,并代替它
*/

struct sockaddr_un {
	sa_family_t sun_family;	/* AF_UNIX */
	char sun_path[UNIX_PATH_MAX];	/* pathname */
};
关于select

int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);

参数maxfd是需要监视的最大的文件描述符值+1;

rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合。

struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0

fd_set(它比较重要所以先介绍一下)是一组文件描述字(fd)的集合,它用一位来表示一个fd(下面会仔细介绍),对于fd_set类型通过下面四个宏来操作:

FD_ZERO(fd_set *fdset); 将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。 
FD_SET(int fd, fd_set *fdset); 用于在文件描述符集合中增加一个新的文件描述符。 
FD_CLR(int fd, fd_set *fdset); 用于在文件描述符集合中删除一个文件描述符。 
FD_ISSET(int fd, fd_set *fdset); 用于测试指定的文件描述符是否在该集合中。
好了,开始上代码。

服务器端:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>


int main (int argc, char *argv[])
{
	int server_sockfd, client_sockfd;
	int server_len, client_len;
	struct sockaddr_un server_address;      /*声明一个UNIX域套接字结构*/
	struct sockaddr_un client_address;
	char ch_send, ch_recv;
	unlink ("server_socket");       /*删除原有server_socket对象*/	
	
	server_sockfd = socket (AF_UNIX, SOCK_STREAM, 0);/*创建 socket, 通信协议为AF_UNIX, SCK_STREAM 数据方式*/	
	
	server_address.sun_family = AF_UNIX;/*配置服务器信息(通信协议)*/		
	strcpy (server_address.sun_path, "server_socket");/*配置服务器信息(socket 对象)*/
	
	server_len = sizeof (server_address);/*配置服务器信息(服务器地址长度)*/	
	
	bind (server_sockfd, (struct sockaddr *)&server_address, server_len);/*绑定 socket 对象*/
	
	listen (server_sockfd, 5);/*监听网络,队列数为5*/
	printf ("Server is waiting for client connect...\n");
		
	client_len = sizeof (client_address);
		
	int recv_num;  
	int send_num;  
	char recv_buf[100];  
	char send_buf[100]; 
		//用一个数组记录描述符的状态  
	int i, ready, max_fd;  
	int client[FD_SETSIZE];  
	int conn_fd;
	
	for (i = 0;i < FD_SETSIZE;i ++) {  
		client[i] = -1;  
	}
	
	fd_set readset;  
	max_fd = server_sockfd;
	
	//最大可用描述符的个数,一般受操作系统内核的设置影响,我的环境下这个值是1024  
	printf("max fd num %d\n",FD_SETSIZE);
	
	while (1) {
		FD_ZERO(&readset);  
		FD_SET(server_sockfd,&readset);  
		for (i = 0;i < FD_SETSIZE;i ++) {  
			if (client[i] == 1) {  
				FD_SET(i, &readset);  
			}  
		} 	
	
			//开始监听描述符,是异步的,不会阻塞  
		ready = select(max_fd+1, &readset, NULL, NULL, NULL); 
	
				//可用描述符如果是创建连接描述符,则创建一个新的连接  
		if (FD_ISSET(server_sockfd, &readset)) {  
			conn_fd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);  
			if (conn_fd < 0) {  
				perror("accept failed");  
				exit(1);  
			}  
  
			FD_SET(conn_fd, &readset);  
			FD_CLR(server_sockfd, &readset);  
  
			if (conn_fd > max_fd) {  
				max_fd = conn_fd;  
			}  
			client[conn_fd] = 1;      
		} 
		
				//检查所有的描述符,查看可读的是哪个,针对它进行IO读写  
		for (i = 0; i < FD_SETSIZE; i ++) {  
			if (FD_ISSET(i, &readset)) {  
				recv_num = recv(i, recv_buf, sizeof(recv_buf), 0);  
				if (recv_num <= 0) {  
					FD_CLR(i, &readset);  
					client[i] = -1;  
				}  
				
			recv_buf[recv_num] = '\0'; 
			printf ("The character receiver from client is %s\n", recv_buf);
			sleep (1);
				
				memset(send_buf,0,sizeof(send_buf));  
				sprintf(send_buf, "server proc got %d bytes\n", recv_num);  
				send_num = send(i, send_buf, strlen(send_buf), 0);  
				if (send_num <= 0) {  
					FD_CLR(i, &readset);  
					client[i] = -1;  
				}  
			}  
		} 
			
	}
	
		close (client_sockfd);
		unlink ("server socket");
	
}
	


客户端:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>

int main (int argc, char *argv[])
{
	struct sockaddr_un address;
	int sockfd;
	int len;
	int i, bytes;
	int result;
	char ch_recv, ch_send;


	if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 	/*创建socket,AF_UNIX通信协议,SOCK_STREAM数据方式*/
	{
		perror ("socket");
		exit (EXIT_FAILURE);
	}

	address.sun_family = AF_UNIX;
	strcpy (address.sun_path, "server_socket");
	len = sizeof (address);

	result = connect (sockfd, (struct sockaddr *)&address, len);	/*向服务器发送连接请求*/
	if (result == -1) {
		printf ("ensure the server is up\n");
		perror ("connect");
		exit (EXIT_FAILURE);
	}

	for (i = 0, ch_send = 'A'; i < 25; i++, ch_send++) 
	{
		if ((bytes = write(sockfd, &ch_send, 1)) == -1) { /*发消息给服务器*/
			perror ("write");
			exit (EXIT_FAILURE);
		}

		sleep (2);    /*休息二秒钟再发一次*/

		if ((bytes = read (sockfd, &ch_recv, 1)) == -1) { /*接收消息*/
			perror ("read");
			exit (EXIT_FAILURE);
		}

		printf ("receive from server data is %c\n", ch_recv);
	}
	close (sockfd);

	return (0);
}



运行结果:

服务器端:

Server is waiting for client connect...
max fd num 1024
The character receiver from client is A
The character receiver from client is B
The character receiver from client is C
The character receiver from client is D
The character receiver from client is E
The character receiver from client is F
The character receiver from client is G
The character receiver from client is H
The character receiver from client is I
The character receiver from client is J
The character receiver from client is K
The character receiver from client is L
The character receiver from client is M
The character receiver from client is N
The character receiver from client is O
The character receiver from client is P
The character receiver from client is Q
The character receiver from client is R
The character receiver from client is S
The character receiver from client is T
The character receiver from client is U
The character receiver from client is V
The character receiver from client is W
The character receiver from client is X
The character receiver from client is Y
The character receiver from client is Y

客户端:

receive from server data is s
receive from server data is e
receive from server data is r
receive from server data is v
receive from server data is e
receive from server data is r
receive from server data is  
receive from server data is p
receive from server data is r
receive from server data is o
receive from server data is c
receive from server data is  
receive from server data is g
receive from server data is o
receive from server data is t
receive from server data is  
receive from server data is 1
receive from server data is  
receive from server data is b
receive from server data is y
receive from server data is t
receive from server data is e
receive from server data is s
receive from server data is 

receive from server data is s

猜你喜欢

转载自blog.csdn.net/jin615567975/article/details/44978797