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