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