socket(二)

TCP客户/服务器模型

1.客户端调用序列

客户端编程序列如下:

  1. 调用socket函数创建套接字
  2. 调用connect连接服务器端
  3. 调用I/O函数(read/write)与服务器端通讯
  4. 调用close关闭套接字

2.服务器端调用序列

服务端的编程序列如下:

  1. 调用socket函数创建套接字
  2. 调用bind绑定本地地址和端口
  3. 调用listen启动监听
  4. 调用accept从已连接队列中提取客户端连接
  5. 调用I/O函数(read/write)与客户端通讯
  6. 调用close函数关闭套接字

回射客户/服务器

socket函数

  包含头文件 <sys/socket.h>

  功能:创建一个套接字用于通信(socket - create an endpoint for communication)

  原型

    int socket(int domain, int type, int protocol);

  参数

    domain:指定通信协议族(protocol family)

    type:指定socket类型,流式套接字SOCK_STREAM,数据报套接字SOCK_DGRAM,原始套接字SOCK_RAW

    protocol:协议类型

  返回值:成功返回非负整数,它与文件描述符类似,我们把它称为套接字描述字,简称套接字。失败返回 -1

拓展:

  域参数指定一个通信域;这将选择用于通信的协议族。这些族在<sys/socket.h>中定义。(The domain argument specifies a communication domain; this selects the protocol family which will be used for communication. These families are defined in <sys/socket.h>.)

domain:

    目前了解的格式包括:(The currently understood formats include:)   

    Name         Purpose               Man page
    AF_UNIX,       AF_LOCAL Local communication      unix(7)
    AF_INET        IPv4 Internet protocols          ip(7)
    AF_INET6      IPv6 Internet protocols          ipv6(7)
    AF_IPX          IPX - Novell protocols
    AF_NETLINK      Kernel user interface device       netlink(7)
    AF_X25         ITU-T X.25 / ISO-8208 protocol        x25(7)
    AF_AX25       Amateur radio AX.25 protocol
    AF_ATMPVC      Access to raw ATM PVCs
    AF_APPLETALK     Appletalk                ddp(7)
    AF_PACKET      Low level packet interface          packet(7)

type:

  套接字具有指定的类型,该类型指定通信语义。目前定义的类型有:(The socket has the indicated type, which specifies the communication semantics.  Currently defined types are:)

  SOCK_STREAM     提供有序、可靠、双向、基于连接的字节流。可能支持带外数据传输机制。(Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.)

  SOCK_DGRAM      支持数据报(无连接,不可靠的固定最大长度的消息)。(Supports datagrams (connectionless, unreliable messages of a fixed maximum length).)

  SOCK_SEQPACKET   为最大长度固定的数据报提供有序、可靠、基于双向连接的数据传输路径;消费者需要在每次输入系统调用时读取整个数据包。(Provides a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed               maximum length; a consumer is required to read anentire packet with each input system call.)

  SOCK_RAW       提供原始网络协议访问。(Provides raw network protocol access.)

  SOCK_RDM       提供不保证排序的可靠数据报层。(Provides a reliable datagram layer that does not guarantee ordering.)

  SOCK_PACKET      过时的,不应用于新项目;参考手册(7)。( Obsolete and should not be used in new programs; see packet(7).)

某些套接字类型可能不能由所有协议族实现;例如,SOCK_SEQPACKET没有在AF_INET中实现。(Some socket types may not be implemented by all protocol families; for example, SOCK_SEQPACKET is not implemented for AF_INET.)

由于Linux 2.6.27,类型参数有第二个用途:除了指定套接字类型外,它还可以包括按位或下列任何一个值,以修改socket()的行为:

(Since Linux 2.6.27, the type argument serves a second purpose: in addition to specifying a socket type, it may include the bitwise OR of any of the following values, to modify the behavior of socket():)

  SOCK_NONBLOCK    在新打开的文件描述中设置O_NONBLOCK文件状态标志。使用这个标志可以节省对fcntl(2)的额外调用,从而获得相同的结果。( Set the O_NONBLOCK file status flag on the new open file description. Using this flag saves extra calls to               fcntl(2) to achieve the same result.)

  SOCK_CLOEXEC       在新的文件描述符上设置close-on-exec (FD_CLOEXEC)标志。查看open(2)中关于O_CLOEXEC标志的说明,了解为什么可能使用它的原因‐。( Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. See the description               of the O_CLOEXEC flag in open(2) for reasons why this may be use‐ful.)

bind函数

 1 #include <sys/types.h> /* See NOTES */
 2 #include <sys/socket.h>
 3 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
 5   @param sockfd : 套接字描述符
 6   @param addr : 通用套接字地址结构的地址
 7   @param addrlen : 套接字地址结构的长度
 8 
 9   @return
10   成功返回 0
11   失败返回 -1

  bind() 函数用于绑定一个固定的 ip 地址与端口号,为客户端提供可以访问的地址,相应的ip 地址与端口号在通用地址结构中存储.

  struct sockaddr 结构体为通用地址结构,主要用于存储 ip 地址与端口号

1  struct sockaddr {
2  sa_family_t sa_family;
3  char sa_data[14];
4  };
5  @sa_family : 地址族 AF_INET
6  @sa_data : ip地址与端口号
  • 由于通用地址结构 ip 地址与端口号保存在一个数组中,不便于区分,在 Internet 协议族重新设计了地址结构,这个地址结构为 struct sockaddr_in
  • ip 地址 与端口号使用单独的成员来表示
  • 同时为了兼容 bind() 函数接口,使用了数组进行填充,保持和原结构一致
1 struct sockaddr_in {
2     __kernel_sa_family_t sin_family; /* 地址族*/
3     __be16 sin_port; /* 端口号 */
4     struct in_addr sin_addr; /* ip 地址*/
5     /* 填充数组,保持与 struct sockaddr 结构大小一致 */
6     unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)];
7 };

listen、

  流 socket 通常可以分为主动和被动两种:在默认情况下,使用 socket() 创建的 socket 是主动的,一个主动的 socket 可用在connect() 调用中来建立一个到一个被动 socket 的连接.一个被动 socket 是一个通过调用 listen() 以被标记成允许接入连接的 socket.在大多数使用流 socket 的应用场景,服务器会执行被动式打开,而客户端会执行主动式

  listen() 函数的原型如下:

1 #include <sys/types.h> /* See NOTES */
2 #include <sys/socket.h>
3 int listen(int sockfd, int backlog);
4 @param sockfd : socket 文件描述符
5 @param backlog : 未决连接的数量

accept、

  accept() 系统调用在文件描述符 sockfd 引用的监听流 socket 上接受一个接入连接,如果在调用 accept() 时不存在未决的连接,那么调用就会阻塞直到有连接请求到达为止

  accpet() 函数的原型为:

1 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
2 @param sockfd : 套接字文件描述符
3 @param addr : 客户端地址结构对象指针
4 @param addrlen :地址结构长度指针
5 
6 @return
7 成功 : 返回新的文件描述符

connect、

  connect() 函数将文件描述符 sockfd 引用的主动 socket 连接到地址通过 addr 和 addrlen 指定的监听 socket 上

1 int connect(int sockfd, const struct sockaddr *addr,
2 socklen_t addrlen);
3 @param sockfd : 文件描述符
4 @param addr : 服务器地址结构对象的指针
5 @param addrlen :地址结构的长度
6 
7 @return :
8 成功 : 返回 0
9 失败 : 返回 -1

server.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <error.h>
 5 
 6 #include <sys/socket.h>
 7 #include <sys/types.h>
 8 #include <arpa/inet.h>
 9 //#include <sys/un.h>
10 
11 #define handle_error(message) 
12     do 
13     { 
14         perror(message); 
15         exit(EXIT_FAILURE); 
16     } while(0);
17 
18 int getSocketFd()
19 {
20     int socket_listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
21     if (socket_listenfd < 0)
22         handle_error("socket");
23    
24     return socket_listenfd;
25 }
26 
27 void bindSocketFd(int socket_listenfd)
28 {
29     struct sockaddr_in serverAddr;
30     memset(&serverAddr, 0, sizeof(serverAddr));
31     serverAddr.sin_family = PF_INET;
32     serverAddr.sin_port = htons(7777);
33     serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
34     /*serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
35     /*inet_ntoa("127.0.0.1", &serverAddr.sin_addr);*/
36 
37     if (bind(socket_listenfd, (struct sockaddr * )&serverAddr, sizeof(serverAddr)) < 0)
38         handle_error("bind");
39 
40     if(listen(socket_listenfd, SOMAXCONN) < 0)
41         handle_error("listen");
42 
43     struct sockaddr_in clientAddr;
44     memset(&clientAddr, 0, sizeof(clientAddr));
45     int lengthOfClientAddr = sizeof(clientAddr);
46     int connectFd = 0;
47     if((connectFd = accept(socket_listenfd, (struct sockaddr * )&clientAddr, &lengthOfClientAddr)) < 0)
48         handle_error("acept");
49 
50     char recvBuf[1024];
51     while (1)
52     {
53         memset(recvBuf, 0, sizeof(recvBuf));
54         int ret = read(connectFd, recvBuf, sizeof(recvBuf));
55 
56         fputs(recvBuf, stdout);
57         write(connectFd, recvBuf, ret);
58     }
59 
60     close(socket_listenfd);
61 
62 }
63 
64 int main(int argv, char * argc[])
65 {
66     int socket_listenfd = getSocketFd();
67 
68     bindSocketFd(socket_listenfd);
69 
70     return 0;
71 }

client.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <unistd.h>
 5 #include <error.h>
 6 
 7 #include <sys/types.h>
 8 #include <sys/socket.h>
 9 #include <arpa/inet.h>
10 #include <netinet/in.h>
11 
12 #define handle_error(message) 
13     do 
14     { 
15         perror(message); 
16         exit(EXIT_FAILURE); 
17     } while(0);
18 
19 void clientToServer()
20 {
21     int clientSockFd;
22     if((clientSockFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
23         handle_error("client socket");
24     
25     struct sockaddr_in clientAddr;
26     memset(&clientAddr, 0, sizeof(clientAddr));
27     clientAddr.sin_family = PF_INET;
28     clientAddr.sin_port = htons(7777);
29     clientAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
30 
31     if(connect(clientSockFd, (struct sockaddr * )&clientAddr, sizeof(clientAddr)) < 0)
32         handle_error("client connect");
33 
34     char sendBuf[1024] = {0};
35     char recvBuf[1024] = {0};
36 
37     while (fgets(sendBuf, sizeof(sendBuf), stdin) != NULL)
38     {
39         write(clientSockFd, sendBuf, sizeof(sendBuf));
40         read(clientSockFd, recvBuf, sizeof(recvBuf));
41         
42         fputs(recvBuf, stdout);
43         memset(sendBuf, 0, sizeof(sendBuf));
44         memset(recvBuf, 0, sizeof(recvBuf));
45     }
46 
47     close(clientSockFd);
48 }
49 
50 int main(int argv, char * argc[])
51 {
52     clientToServer();
53 
54     return 0;
55 }
原文地址:https://www.cnblogs.com/Reverse-xiaoyu/p/12820482.html