这里我用socket编程实现了一个网络编程中最简单的功能,客户端向服务器发送了一个字符‘A’,服务器端接收到之后将其打印出来。
socket编程的流程非常的简单,原理也非常的直观。socket建立之后会生成一个文件描述符,当你处理好连接哪里的问题后,直接通过这个描述符就可以来来在套接字上发送和接收数据了,流程图如下图所示:
我会根据这个流程图以及代码对关键的函数进行一个简要的介绍以及剖析,目的在于对socket编程的流程及其主要部分有一个清晰的了解。
1 /*************************************************************************** 2 * * * 3 * * * Filename: client.c 4 * * * 5 * * * Description: Network Client , transfer A to server 6 * * * 7 * * * creater: tian 2015.9.9 8 * * * 9 * * * version: 1.0 10 * * * 11 * * * change: 12 * * * 13 * * ************************************************************************/ 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 19 #include <unistd.h> 20 #include <arpa/inet.h> 21 #include <netinet/in.h> 22 23 #define SERV_PORT 8000 24 25 int main(int argc, char **argv) 26 { 27 int Client_socket = 0; 28 int Socket_len = 0; 29 int result = 0; 30 char ch = 'A'; 31 struct sockaddr_in server_addr; 32 33 Client_socket = socket(AF_INET, SOCK_STREAM, 0);//create a socket 34 35 if(-1 == Client_socket) 36 { 37 perror("socket"); 38 exit(1); 39 } 40 41 memset(&server_addr, 0, sizeof(server_addr)); 42 43 server_addr.sin_family = AF_INET; 44 server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 45 server_addr.sin_port = htons(SERV_PORT); 46 47 result = connect(Client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));//c onnect the server 48 49 if(-1 == result) 50 { 51 perror("connect"); 52 exit(1); 53 } 54 55 result = write(Client_socket, &ch, 1);//send out the character 56 57 if(-1 == result) 58 { 59 perror("write"); 60 } 61 62 close(Client_socket);//close the socket 63 64 exit(0); 65 } 66
1 /*************************************************************************** 2 * * * 3 * * * Filename: server.c 4 * * * 5 * * * Description: Network server, accept A from server 6 * * * 7 * * * creater: tian 2015.9.9 8 * * * 9 * * * version: 1.0 10 * * * 11 * * * change: 12 * * * 13 * * ************************************************************************/ 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <sys/types.h> 18 #include <sys/socket.h> 19 #include <unistd.h> 20 #include <arpa/inet.h> 21 #include <netinet/in.h> 22 23 #define SERV_PORT 8000 24 25 int main(int argc, char **argv) 26 { 27 int Listen_socket = 0,New_socket = 0; 28 int result = 0; 29 int len = 0; 30 char ch; 31 struct sockaddr_in server_addr,Client_addr; 32 33 Listen_socket = socket(AF_INET, SOCK_STREAM, 0);//create a socket 34 35 if(-1 == Listen_socket) 36 { 37 perror("socket"); 38 exit(1); 39 } 40 41 memset(&server_addr, 0, sizeof(server_addr)); 42 43 server_addr.sin_family = AF_INET; 44 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 45 server_addr.sin_port = htons(SERV_PORT); 46 47 result = bind(Listen_socket, (struct sockaddr *)&server_addr, sizeof(server_addr));//Named the socket 48 49 if(-1 == result) 50 { 51 perror("bind"); 52 exit(1); 53 } 54 55 listen(Listen_socket,20);//momitor the connect fro client 56 57 len = sizeof(Client_addr); 58 New_socket = accept(Listen_socket, (struct sockaddr *)&Client_addr, &len);//receive a connect and create a new socket 59 60 if(-1 == New_socket) 61 { 62 perror("accept"); 63 exit(1); 64 } 65 66 result = read(New_socket, &ch, 1);//receive the data 67 68 if(-1 == result) 69 { 70 perror("read"); 71 exit(1); 72 } 73 74 printf("receive: %c ", ch);//print the data 75 76 close(New_socket); 77 close(Listen_socket);//close the socket 78 79 exit(0); 80 } 81
运行结果:
首先开启服务器:
./server
然后开启客户端:
./client
便可以在服务器端看到结果:
receive: A
(行号以源代码中的标号为准)
1、客户端:
(1)首先是创建一个套接字(33行):int socket(int domain, int tyepe, int protocol)函数中的前两个参数非常重要,domain参数指定协议族,我们这里选择的就是AF_INET(UNIX网络套接字)常见的还有AF_UNIX(文件系统套接字),还有一些协议族,我们这里就不再进行介绍。type指定新建立的套接字的通信特性,比较常见的有:SOCK_STREAM和SOCK_DGREAM。第一个是流套接字,另外一个便是数据报套接字。第一个通常由TCP连接来提供,而第二个由UDP连接来提供。数据报套接字对所发送的数据报的长度有所限制,其作为一个单独的网络消息被传输。而数据流套接字中对大的消息会进行分片、传输,其接手大量的数据,然后以小数据块的形式将它们写入底层的磁盘。在这里我们选用的是TCP连接,所以type被赋值为SOCK_STREAM。第三个参数不常用,设为0表示使用默认协议。
(2)建立套接字之后便开始连接服务器(47行):int connect(int socket, const struct sockaddr *address, size_t address_len),第一个参数指的便是我们前面建立的一个套接字,第二参数指的是一个网络协议里专门的网络地址。这里的server_addr就肯定指的是服务器的地址了。address参数是一个结构体,里面包含了一个网络地址一些必要的设置。
在AF_INET域中,套接字地址由结构sockaddr_in来指定。
struct sockaddr_in
{
short int sin_family;//地址类型,我们这里是AF_INET
unsigned short int sin_port;//端口号
struct in_addr sin_addr;//IP地址
};
struct in_addr
{
unsigned long int s_addr;
};
我们可以想到确定IP地址和端口号之后便可以进行进程间的网络通信了,客户端要连接的网络地址是服务器的地址。所以在43-45中我们设置了其中的成员,将建立的socket连接到这个服务器上。在其中我们要注意的是,这个程序客户端连接的是位于本机服务器,本地主机有一个标准的IP地址就是:127.0.0.1。另外要注意的就是主机字节序和网络字节序的转换。
(3)连接到服务器之后你便可以用write(55)函数向服务器发送数据了。
(4)最后要记得关闭套接字。(62)
揍是这么简单。。。。。
2、服务器:
(1) 首先仍是建立一个套接字(33)
(2) 然后便是给这个套接字进行命名(48),我的理解就是每一个套接必须绑定一个地址,不然无法进行通信工作。这里就将listen_socket与地址server_addr绑定。server_addr也是一个sockaddr_in结构体,不同的是这里表示的服务器的地址。要注意的是,44行中表示的是指定一组容许连接的IP地址,这里我们用到了INADDR_ANY,它表示将接受来自计算机任何网络接口的连接。
(3)接着便是用listen(55)建立一个接收连接的队列,作为一个服务器会接收到许多客户端的连接请求。listen(int socket, int backlog);backlog定义了队列中可以容纳的未处理连接的最大数目,在套接字队列中,等待处理的进入连接的个数最多不能超过这个数字。再往后的连接将被拒绝,这样的机制的好处在于服务器在处理一个客户端连接请求时,其它后续的客户请求也不会被拒绝,他们将会进入等待队列,等待服务器的处(lin)理(xing)。
接收到客户端的请求之后,或者可以说是客户端给服务器的电话打通之后,便要进行说话了。服务器的机制是随后利用accept生成一个新的socket为new_socket(跟Listen_socket一模一样),这个socket就负责与客户端的连接进行通话。
(4) int accept(int socket, struct sockaddr *address, size_t *address_len);(58)
accept将处理排在套接字队列中排在第一个的未处理连接,accept将创建一个新套接字来与该客户进行通信,并且返回新套接字的描述符。新套接字的类型和服务器监听套接字类型是一样的。
连接客户的地址将被放入address参数指向的sockaddr结构中,如果我们不关心客户的地址,也可以将address参数指定为空指针。
参数address_len指定客户结构的长度,如果客户地址的长度超过这个值,它将被截断。
最需要注意的一点是,如果套接字队列中没有未处理的连接,accept将阻塞(程序将暂停)直到有客户建立连接为止。
(5) 上一步完成之后,便可以与客户端欢快的通信了。他write我就read,我write他就read,好欢快啊!!!
参考:《Linux程序设计》
版权声明:本文为博主原创文章,未经博主允许不得转载。