Linux网络通信之【Socket编程】

目录

30.socket编程

30.1 socket 简介

什么是TCP/IP、UDP?

30.2 socket API

30.2.1 socket编程接口

30.2.2 bind()函数

30.2.3 listen()函数

30.2.4 accept()函数

30.2.5 connect()函数

30.3 TCP Socket编程实例

30.4 UDP Socket编程实例

问题:试描述TCP建立和断开连接时的三次握手和四次挥手


30.socket编程

30.1 socket 简介

套接字(socket)是 Linux 下的一种进程间通信机制(socket IPC),socket 是应用层与 TCP/IP 协议通信的中间软件抽象层,它是一组接口,还是一个标准BSD socket,能在不同平台很方便移植

  • Socket

TCP/IP协议族包括运输层、网络层、链路层,而socket所在位置如图,Socket是应用层与TCP/IP协议族通信的中间软件抽象层。

什么是TCP/IP、UDP?

TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。

  • TCP/IP协议

存在于OS中,网络服务通过OS提供,在OS中增加支持TCP/IP的系统调用——Berkeley套接字,如Socket,Connect,Send,Recv等、

  • UDP(User Data Protocol,用户数据报协议)

是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。如图:

30.2 socket API

30.2.1 socket编程接口

socket()函数类似于 open()函数,它用于创建一个网络通信端点(打开一个网络通信),如果成功则返回一个网络文件描述符,通常把这个文件描述符称为 socket 描述符(socket descriptor)

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
​
int socket(int domain, int type, int protocol);
//domain: 用于指定一个通信域
//type  : 指定套接字的类型
//protocol: 设置为0,表示为给定的通信域和套接字类型选择默认类型

30.2.2 bind()函数

bind()函数用于将一个 IP 地址或端口号与一个套接字进行绑定(将套接字与地址进行关联),(注意这里说的地址包括 IP 地址和端口号),因为对于客户端来说,它与服务器进行通信,首先需要知道服务器的 IP 地址以及对应的端口号,所以通常服务器的 IP 地址以及端口号都是众所周知的

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//最后一个参数addrlen :指定了 addr 所指向的结构体对应的字节长度

参数 addr 是一个指针,指向一个 struct sockaddr 类型变量:

  • struct sockaddr

示例代码 30.2.1 struct sockaddr 结构体
struct sockaddr {
 sa_family_t sa_family;
 char sa_data[14];
}
// 第二个参数sa_data :是一个 char 类型数组,一共 14 个字节,在这 14 个字节中就包括了 IP 地址、端口号等信息

一般我们在使用的时候都会使用 struct sockaddr_in 结构体,sockaddr_in 和 sockaddr 是并列的结构(占用的空间是一样的),指向 sockaddr_in 的结构体的指针也可以指向 sockadd 的结构体,并代替它

示例代码 30.2.2 struct sockaddr_in 结构体
struct sockaddr_in {
 sa_family_t sin_family; /* 协议族 */
 in_port_t sin_port; /* 端口号 */
 struct in_addr sin_addr; /* IP 地址 */
 unsigned char sin_zero[8];
};

30.2.3 listen()函数

listen()函数只能在服务器进程中使用,让服务器进程进入监听状态,等待客户端的连接请求,listen()函数在一般在 bind()函数之后调用,在 accept()函数之前调用

int listen(int sockfd, int backlog);
//参数 backlog 用来描述 sockfd 的等待连接队列能够达到的最大值

30.2.4 accept()函数

服务器调用 listen()函数之后,就会进入到监听状态,等待客户端的连接请求,使用 accept()函数获取客户端的连接请求并建立连接

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

socket()函数返回的是服务器的套接字(以服务器为例),而accept()函数返回的套接字连接到调用 connect()的客户端,服务器通过该套接字与客户端进行数据交互

30.2.5 connect()函数

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

该函数用于客户端应用程序中,客户端调用 connect()函数将套接字 sockfd 与远程服务器进行连接

30.3 TCP Socket编程实例

  • 服务端程序

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
​
​
/* socket
 * bind
 * listen
 * accept
 * send/recv
 */
​
#define SERVER_PORT 8888
#define BACKLOG     10
​
int main(int argc, char **argv)
{
    int iSocketServer;
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;
    struct sockaddr_in tSocketClientAddr;
    int iRet;
    int iAddrLen;
​
    int iRecvLen;
    unsigned char ucRecvBuf[1000];
​
    int iClientNum = -1;
​
    signal(SIGCHLD,SIG_IGN);
    
    iSocketServer = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == iSocketServer)
    {
        printf("socket error!\n");
        return -1;
    }
​
    tSocketServerAddr.sin_family      = AF_INET;
    tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
    tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
    memset(tSocketServerAddr.sin_zero, 0, 8);
    
    iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
    if (-1 == iRet)
    {
        printf("bind error!\n");
        return -1;
    }
​
    iRet = listen(iSocketServer, BACKLOG);
    if (-1 == iRet)
    {
        printf("listen error!\n");
        return -1;
    }
​
    while (1)
    {
        iAddrLen = sizeof(struct sockaddr);
        iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
        if (-1 != iSocketClient)
        {
            iClientNum++;
            printf("Get connect from client %d : %s\n",  iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
            if (!fork())
            {
                /* 子进程的源码 */
                while (1)
                {
                    /* 接收客户端发来的数据并显示出来 */
                    iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0);
                    if (iRecvLen <= 0)
                    {
                        close(iSocketClient);
                        return -1;
                    }
                    else
                    {
                        ucRecvBuf[iRecvLen] = '\0';
                        printf("Get Msg From Client %d: %s\n", iClientNum, ucRecvBuf);
                    }
                }               
            }
        }
    }
    
    close(iSocketServer);
    return 0;
}
  • 客户端程序

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
​
/* socket
 * connect
 * send/recv
 */
​
#define SERVER_PORT 8888
​
int main(int argc, char **argv)
{
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;
    
    int iRet;
    unsigned char ucSendBuf[1000];
    int iSendLen;
​
    if (argc != 2)
    {
        printf("Usage:\n");
        printf("%s <server_ip>\n", argv[0]);
        return -1;
    }
​
    iSocketClient = socket(AF_INET, SOCK_STREAM, 0);
​
    tSocketServerAddr.sin_family      = AF_INET;
    tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
    //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
    if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
    {
        printf("invalid server_ip\n");
        return -1;
    }
    memset(tSocketServerAddr.sin_zero, 0, 8);
​
​
    iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));    
    if (-1 == iRet)
    {
        printf("connect error!\n");
        return -1;
    }
​
    while (1)
    {
        if (fgets(ucSendBuf, 999, stdin))
        {
            iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
            if (iSendLen <= 0)
            {
                close(iSocketClient);
                return -1;
            }
        }
    }
    
    return 0;
}

30.4 UDP Socket编程实例

  • 服务端程序

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket
 * bind
 * sendto/recvfrom
 */
#define SERVER_PORT 8888
​
int main(int argc, char **argv)
{
    int iSocketServer;
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;
    struct sockaddr_in tSocketClientAddr;
    int iRet;
    int iAddrLen;
    int iRecvLen;
    unsigned char ucRecvBuf[1000];
    int iClientNum = -1;
    //SOCK_DGRAM表示使用的是UDP协议
    iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == iSocketServer)
    {
        printf("socket error!\n");
        return -1;
    }
    tSocketServerAddr.sin_family      = AF_INET;
    tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
    tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
    memset(tSocketServerAddr.sin_zero, 0, 8);
    
    iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
    if (-1 == iRet)
    {
        printf("bind error!\n");
        return -1;
    }
    while (1)
    {
        iAddrLen = sizeof(struct sockaddr);
        iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
        if (iRecvLen > 0)
        {
            ucRecvBuf[iRecvLen] = '\0';
            printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
        }
    }   
    close(iSocketServer);
    return 0;
}
​
  • 客户端程序

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
​
/* socket
 * connect
 * send/recv
 */
​
#define SERVER_PORT 8888
​
int main(int argc, char **argv)
{
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;
    
    int iRet;
    unsigned char ucSendBuf[1000];
    int iSendLen;
    int iAddrLen;
​
    if (argc != 2)
    {
        printf("Usage:\n");
        printf("%s <server_ip>\n", argv[0]);
        return -1;
    }
​
    iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);
    tSocketServerAddr.sin_family      = AF_INET;
    tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
    //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
    if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
    {
        printf("invalid server_ip\n");
        return -1;
    }
    memset(tSocketServerAddr.sin_zero, 0, 8);
    while (1)
    {
        if (fgets(ucSendBuf, 999, stdin))
        {
            iAddrLen = sizeof(struct sockaddr);
            iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0,
                                  (const struct sockaddr *)&tSocketServerAddr, iAddrLen);
​
#endif
            if (iSendLen <= 0)
            {
                close(iSocketClient);
                return -1;
            }
        }
    }   
    return 0;
}

问题:试描述TCP建立和断开连接时的三次握手和四次挥手


建立连接: ​ 
A->B : SYN=1 ​ 
B->A : SYN=1 + ACK=1 ​ 
A->B : ACK=1 ​
释放连接: ​ 
A->B : FIN=1 ​ 
B->A : ACK ​ 
B->A : FIN=1 ​ 
A->B : ACK
  • 三次握手:

  1. 客户端先发送标志位SYN=1,seq=x请求与服务器建立连接2.

  2. 服务器收到客户端的TCP报文后,返回标志位SYN=1,ACK=1,seq=y,ack=x+1的报文应答客户端并同意建议连接

  3. 客户端收到服务器的TCP报文后,返回标志位ACK=1,seq=x+1,ack=y+1的报文表示接收到服务器的消息并建立连接

  • 四次挥手:

  1. 客户端向服务器发送标志位FIN=1,seq=x的报文请求断开连接2.

  2. 服务器收到后,返回标志位ACK=1,seq=y,ack=x+1的报文告诉客户端收到报文,并准备断开连接

  3. 服务器做好断开连接的准备后,给客户端发送标志位FIN=1,ACK=1,seq=u,ack=x+1的报文告诉客户端已做好准备断开连接

  4. 客户端收到报文后,返回标志位ACK=1,seq=x+1,ack=u+1的报文表示收到消息并断开连接

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值