Linux网络编程 | Socket

一、Socket编程

1.1 基本概念

在Linux环境下,用于表示进程间网络通信的特殊文件类型(IP和port);

  • 本质:为内核借助缓冲区形成的伪文件
  • 即是文件,则可用文件描述符来引用;进而,在Linux中封装成与读写文件操作一致的接口;
  • 若要建立连接,则两个进程必须各自有一个socket标识【一对一】;
  • 在网络通信中,套接字一定是成对出现的。一端的发送缓冲区对应对端的接收缓冲区。我们使用同一个文件描述符索发送缓冲区和接收缓冲区。

网络编程接口
在这里插入图片描述

1.2 socket常用函数
1.2.1 socket
/**
* # include <sys/types.h>
* # include <sys/socket.h>
 * int socket(int domain, int type, int protocol)
 * -------
 * func:创建通信端点(服务端)并返回一个描述符;
 * param domain:指定通信域;选择用于通信的协议族;
 	   AF_UNIX, AF_LOCAL   本地通信            
       AF_INET             IPv4 
       AF_INET6            IPv6
       AF_IPX              IPX
       AF_NETLINK          Kernel user interface device     
       AF_X25              ITU-T X.25 / ISO-8208 protocol  
       AF_AX25             Amateur radio AX.25 protocol
       AF_ATMPVC           Access to raw ATM PVCs
       AF_APPLETALK        Appletalk                        
       AF_PACKET           Low level packet interface       
 * param type:指定通信语义;
	   SOCK_STREAM    提供有序的、可靠的、双向的、基于连接的字节流。 可能支持带外数据传输机制;
       SOCK_DGRAM     支持数据报(固定最大长度的无连接、不可靠消息);
     SOCK_SEQPACKET   为最大固定长度的atagram提供一个顺序的、可靠的、基于双向连接的数据传输路径; 消费
     				  者需要在每次输入系统调用时读取整个数据包;
       SOCK_RAW        提供原始网络协议访问;
       SOCK_RDM        提供不保证顺序的可靠数据报层; 
     SOCK_NONBLOCK     在新打开的文件描述中设置O_NONBLOCK文件状态标志。 使用这个标志可以节省对fcntl(2)
     					的额外调用来实现相同的结果;
 * param protocol:当只有一个协议支持指定的套接字类型时,为0;其他需指定;
 * return:成功返回新套接字的文件描述符,出错返回-1;
 * */
1.2.2 bind
/**
 * int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen)     
 * -------
 * func:当使用socket创建套接字时,它存在于一个名称空间(地址族)中,但没有分配给它地址。 Bind()将addr
 * 		指定的地址分配给套接字;
 * param sockfd:返回的文件描述符;
 * param addr:传入sockaddr_in结构体且需要进行强制转换为(sockaddr);
			serv_addr.sin_family = AF_INET;			// 通信域
     		serv_addr.sin_port = htons(SERV_PORT);	// 端口
     		serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);	// IP
 * param addrlen:传入addr的长度;
 * return:成功返回0,失败返回-1;
 * */
1.2.3 listen
/**
 * int listen(int sockfd, int backlog)
 * -------
 * func:将套接字标记为被动套接字,即用于accept接收传入的连接请求的套接字;
 * param sockfd:返回的文件描述符;
 * param backlog:定义了sockfd挂起连接的队列可能增长的最大长度。 如果一个连接请求到达时队列已经满了,
 * 					客户端可能会收到一个指示ECONNREFUSED的错误,或者,如果底层协议支持重传,这个请求
 * 					可能会被忽略,这样之后的连接重试就会被放弃;
 * return:成功返回0,失败返回-1;
 * */
1.2.4 accept
/**
 * int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
 * -------
 * func:侦听套接字sockfd的挂起连接队列中提取第一个连接请求,创建一个新的连接套接字,并返回一个指向该套
 * 		接字的新文件描述符;
 * param sockfd:返回的文件描述符;
 * param addr:指向sockaddr结构的指针,为传出参数;
 * param addrlen:为传入传出参数;
 * return:成功返回文件描述符,失败返回-1;
 * */
1.2.5 ntohl()、htonl()、ntohs()、htons()
n代表net网络,h代表host主机,l代表long长整型,s代表short短整型
* ntohl():将一个无符号长整形数从网络字节顺序转换为主机字节顺序
* htonl():将一个无符号长整形数从主机字节顺序转换为网络字节顺序
* ntohs():将一个无符号短整形数从网络字节顺序转换为主机字节顺序
* htons():将一个无符号短整形数从主机字节顺序转换为网络字节顺序
1.2.6 connect
/**
 * int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen)
 * -------
 * func:将套接字连接到由addr指定的地址;
 * param sockfd:返回的文件描述符;
 * param addr:指向sockaddr结构的指针;若sockfd的类型是SOCK_DGRAM,则addr是默认情况下发送数据报的地
 				址,也是接收数据报的唯一地址; 若为SOCK_STREAM或SOCK_SEQPACKET,此调用将尝试与绑定到
 				addr指定地址的套接字建立连接;
 * param addrlen:传入结构体的大小;
 * return:成功返回0,失败返回-1;
 * */
1.2.7 inet_ntop
/**
 * const char *inet_ntop(int af, const void *src,
                             char *dst, socklen_t size)
 * -------
 * func:将ip的网络字节序转换为字符串;
 * param af:网络协议;
 * param src:网络字节序的IP;
 * param dst:接收字符串IP;
 * param size:dst的大小;
 * return:成功返回dst的非空指针,失败返回NULL;
 * */
1.3 简易服务端程序

模拟客户端给服务器发送字符串,服务端将字母转换成大写并返回给客户端;

client

/*----------------------------------------------------------------------
	> File Name: clientBase.cpp
	> Author: Jxiepc
	> Mail: Jxiepc
	> Created Time: Sat 19 Feb 2022 11:55:25 AM CST
----------------------------------------------------------------------*/

#include <iostream>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666

void client() {
    int cfd;
    struct sockaddr_in serv_addr;
    socklen_t ser_addr_len;

    cfd = socket(AF_INET, SOCK_STREAM, 0);

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);

    connect(cfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

    while(1) {
        char buf[128];
        fgets(buf, 128, stdin); 

        write(cfd, buf, strlen(buf));
        int n = read(cfd, buf, strlen(buf));
        std::cout << buf;
    }
}

int main(int argc, char* argv[])
{
    client();

    return 0;
}

server

/*----------------------------------------------------------------------
	> File Name: serverBase.cpp
	> Author: Jxiepc
	> Mail: Jxiepc
	> Created Time: Wed 09 Feb 2022 10:26:07 AM CST
----------------------------------------------------------------------*/

#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <ctype.h>
#include <arpa/inet.h>

#define SERV_IP "127.0.0.1"
#define SERV_PORT 6666

void server() {
    int lfd, cfd;
    struct sockaddr_in serv_addr;
    struct sockaddr_in clie_addr;
    socklen_t clie_addr_len;
    char buf[1024];
    int i;

    lfd = socket(AF_INET, SOCK_STREAM, 0);  // 建立连接

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(SERV_PORT);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));  // 绑定
    listen(lfd, 128);                                             // 监听
    clie_addr_len = sizeof(clie_addr);                              
    cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);                // 接收客户端

    char clie_ip[128];

    while(1) {
        std::cout << inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_ip, sizeof(clie_ip))
            << ":" << ntohs(clie_addr.sin_port) << std::endl;
        int len = read(cfd, buf, 1024);                               // 读取客户端的数据
        for(int i=0; i<len; ++i)
            buf[i] = toupper(buf[i]);
        write(cfd, buf, len);                                         // 向客户端发送数据
    }
    close(lfd);
    close(cfd);

}

int main(int argc, char* argv[])
{
    server();
    return 0;
}

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jxiepc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值