Linux网络编程:Socket编程接口(1)TCP下的socket操作

本文详细介绍了网络编程中字节序转换、IP地址和主机名转换的关键函数,如ntohs、htonl、inet_aton、inet_ntoa等。同时,讲解了TCP套接字操作,包括客户端的socket创建、connect连接、write发送、read接收和close关闭,以及服务器端的socket创建、bind绑定、listen监听、accept接受和关闭。通过实例展示了客户端和服务器之间的交互过程。
摘要由CSDN通过智能技术生成

1. 接口

1.1 转换操作

转换操作主要分为三类:字节序转换操作、IP地址转换操作和主机名转换操作

字节序转换操作

网络序转主机序

函数含义作用
ntohs()network to host short把unsigned short类型从网络序转换到主机序
ntohl()network to host long把unsigned long类型从网络序转换到主机序

主机序转网络序

函数含义作用
htons()host to network short把unsigned short类型从主机序转换到网络序
htonl()host to network long把unsigned long类型从主机序转换到网络序

头文件: #include <arpa/inet.h>

网络序和主机序的转换:

#include <iostream>
#include <arpa/inet.h>
#include <cstring>
#include <iomanip>
using namespace std;

void Hex(int n){
        char bytes[2];
        memcpy(bytes,&n,2);    // 把n的地址复制进来,两个字节
        cout << hex << setw(2) << setfill('0') << (int)bytes[0] << setw(2) << (int) bytes[1] << endl;
        // 16进制,用0把两个字节补满
}

int main(){
        uint16_t n;
        cin >> n;
        Hex(n);
        uint16_t m = htons(n);    // 主机序n转换为网络序m
        Hex(m);
        uint16_t t = ntohs(m);    // 网络序m转换为主机序n
        Hex(t);
}

结果为:

100
6400
0064
6400

无符号的16位是两个字节

IP地址转换操作

函数功能特点
int inet_aton(const char *string, struct in_addr*addr)点分十进制数串转网络字节序长整型IPv4专用
in_addr_t inet_addr(const char* string)点分十进制数串转网络字节序长整型IPv4专用
char* inet_ntoa(struct in_addr addr)网络字节序长整型转点分十进制数串IPv4专用
int inet_pton(int af, const char *src, void *dst)点分十进制数串转网络字节序长整型IPv4/IPv6通用(推荐)
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)网络字节序长整型转点分十进制数串IPv4/IPv6通用(推荐)

结构体:

结构体功能特性
struct sockaddr套接字地址结构IPv4/IPv6通用
struct sockaddr_inIPv4套接字地址结构IPv4专用
struct in_addrIPv4地址结构IPv4专用
in_addr_tIPv4地址类型IPv4专用
struct sockaddr_in6IPv6套接字地址结构IPv6专用

点分十进制数串和网络字节序长整型转换:

#include <iostream>
#include <arpa/inet.h>
#include <cstring>
#include <iomanip>
using namespace std;

int main(){
        string s;
        cin >> s;
        in_addr addr;        // IPv4地址类型结构体
        inet_aton(s.c_str(),&addr);         // 点分十进制数串转网络字节序长整型
        cout << addr.s_addr << endl;      // s_addr为成员变量,可以直接用

        cout << inet_ntoa(addr) << endl;     // 网络字节序长整型转点分十进制数串

        cout << inet_addr(s.c_str()) << endl;    // 点分十进制数串转网络字节序长整型(简单)
}

结果为:

192.168.0.1
16820416
192.168.0.1
16820416

主机名转换操作

函数功能
struct hostent *gethostbyname(const char *hostname)主机名转地址
struct hostent *gethostbyaddr(const char * addr, int len, int type)地址转主机名
struct hostent *gethostbyaddr(const char * addr, int len, int type)地址转主机名

2. socket操作(TCP)

在这里插入图片描述

2.1 客户端

socket创建

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

参数含义说明
domain协议域AF_INET:IPv4;AF_INET6:IPv6;AF_LOCAL:Unix域
type类型SOCK_STREAM:流式套接字;SOCK_DGRAM:数据报套接字;SOCK_RAW:原始套接字
protocol协议0:自动根据type匹配协议;IPPROTO_TCP / IPPROTO_UDP

返回值:

返回值含义
-1失败
>0socket描述符
        int fd = socket(AF_INET,SOCK_STREAM,0);
        if(-1 == fd){
                perror("open socket error");
                return 1;
        }

connect连接

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

参数含义
sockfd客户端的socket描述字
addr服务器的socket地址
addrlen服务器的socket地址的长度

返回值:

返回值含义
0成功
-1失败
        sockaddr_in addr;                // IPv4套接字地址结构体
        addr.sin_family = AF_INET;                // 放入协议
        inet_aton("127.0.0.1",&addr.sin_addr);            // 点分十进制数串转网络字节序长整型
        addr.sin_port = htons(8080);                 // 主机序转网络序,然后传入端口
        int res = connect(fd,(sockaddr*)&addr,sizeof(addr));
        if(-1 == res){
                perror("connect error");
                return 1;
        }

write发送

ssize_t write(int fd, const void *buf, size_t len);

参数含义
fd文件描述符
buf写入数据
len写入数据的长度

返回值:

返回值含义
>0实际所写的字节数
<0出错
		string s;
        cin >> s;
        write(fd,s.c_str(),s.size()+1);

read接收

ssize_t read(int fd, void *buf, size_t len);

参数含义
fd文件描述符
buf读取数据
len读取数据的长度

返回值:

返回值含义
0读到文件的结束
>0实际所读的字节数
<0出错
        char buffer[256] = {0};
        read(fd,buffer,256);
        cout << buffer << endl;

close关闭

int close(int sockfd)

参数含义
sockfdsocket套接字
        close(fd);

2.2 服务器

socket创建

与客户端相同

        int fd = socket(AF_INET,SOCK_STREAM,0);
        if(-1 == fd){
                perror("open socket error");
                return 1;
        }

bind绑定

int bind(int socket, const struct sockaddr* address, socklen_t address_len)

参数含义
socket套接字描述符
address地址和端口号
address_lenaddress 缓冲区的长度

返回值:

返回值含义
0成功
SOCKET_ERROR失败
        sockaddr_in addr;                // IPv4套接字地址结构体
        addr.sin_family = AF_INET;                // 放入协议
        inet_aton("127.0.0.1",&addr.sin_addr);            // 点分十进制数串转网络字节序长整型
        addr.sin_port = htons(8080);                 // 主机序转网络序,然后传入端口
        int res = bind(fd,(sockaddr*)&addr,sizeof(addr));
        if(-1 == res){
                perror("bind error");
                return 1;
        }

listen监听

int listen(int sockfd, int backlog)

参数含义
sockfd监听的socket描述符
backlog排队的最大连接个数

返回值:

返回值含义
0成功
-1失败
        res = listen(fd,4);
        if(-1 == res){
                perror("listen error");
                return 1;
        }

accept接受

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

参数含义
sockfd服务器的socket描述符,监听socket描述符
addr客户端的socket地址,不需要就给NULL
addrlen客户端的socket地址的长度,不需要就给NULL

返回值:

返回值含义
非-1连接描述符
-1失败
        int connfd = accept(fd,NULL,NULL);
        if(-1 == connfd){
                perror("accept error");
                return 1;
        }

read接收

这里接收的是用于通信的描述符connfd

        char buffer[256] = {0};
        read(connfd,buffer,256);
        cout << buffer << endl;

write发送

这里发送的是用于通信的描述符connfd

        string s;
        cin >> s;
        write(connfd,s.c_str(),s.size()+1);

close关闭

不仅需要关闭用于监听的描述符fd,还需要关闭用于通信的描述符connfd

        close(connfd);
        close(fd);

2.3 客户端和服务器的程序

客户端:

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>    // AF_INET  sockaddr_in
#include <unistd.h>      // write() read() close()
using namespace std;

int main(){
        int fd = socket(AF_INET,SOCK_STREAM,0);
        if(-1 == fd){
                perror("open socket error");
                return 1;
        }

        sockaddr_in addr;                // IPv4套接字地址结构体
        addr.sin_family = AF_INET;                // 放入协议
        inet_aton("127.0.0.1",&addr.sin_addr);            // 点分十进制数串转网络字节序长整型
        addr.sin_port = htons(8080);                 // 主机序转网络序,然后传入端口
        int res = connect(fd,(sockaddr*)&addr,sizeof(addr));
        if(-1 == res){
                perror("connect error");
                return 1;
        }

        string s;
        cin >> s;
        write(fd,s.c_str(),s.size()+1);

        char buffer[256] = {0};
        read(fd,buffer,256);
        cout << buffer << endl;

        close(fd);
}

服务器:

#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>    // AF_INET  sockaddr_in
#include <unistd.h>      // write() read() close()
using namespace std;

int main(){
        int fd = socket(AF_INET,SOCK_STREAM,0);
        if(-1 == fd){
                perror("open socket error");
                return 1;
        }

        sockaddr_in addr;                // IPv4套接字地址结构体
        addr.sin_family = AF_INET;                // 放入协议
        inet_aton("127.0.0.1",&addr.sin_addr);            // 点分十进制数串转网络字节序长整型
        addr.sin_port = htons(8080);                 // 主机序转网络序,然后传入端口
        int res = bind(fd,(sockaddr*)&addr,sizeof(addr));
        if(-1 == res){
                perror("bind error");
                return 1;
        }

        res = listen(fd,4);
        if(-1 == res){
                perror("listen error");
                return 1;
        }

        int connfd = accept(fd,NULL,NULL);
        if(-1 == connfd){
                perror("accept error");
                return 1;
        }

        char buffer[256] = {0};
        read(connfd,buffer,256);
        cout << buffer << endl;

        string s;
        cin >> s;
        write(connfd,s.c_str(),s.size()+1);

        close(connfd);
        close(fd);
}

操作步驟:

  1. 在一边启动服务器,在另一端启动客户端
  2. 服务器等待客户端发送消息
  3. 客户端发送消息,服务器接收到
  4. 服务器发送消息,客户端接收到,两边退出

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值