Linux网络编程——socket、bind、listen、accpet、connect、read和write


基础理论

1、TCP/UDP/端口号

管道、消息队列、共享内存、信号、信号量五种进程间通信属于单机通信。网络编程是由地址和数据组成的。地址是IP地址和端口号组成。数据传输需要遵循协议(HTTP、TCP、UDP)。通过端口号对客户端访问,有FTP、HTTP、SOCKET多种服务。
Linux的socket套接字所用协议最多的是TCP/UDP
TCP:面向连接(举例:A给B打电话,先拨号再接通);
UDP:面向报文(A给B发信息,不关心是否接通成功);
二者比较:TCP应用于数据传输要求精确的场景;UDP应用于大数据传输;
端口号作用:一台拥有IP地址的主机可以提供许多服务。比如:web、FTP、SMTP(电子邮件传输)服务;实际上,通过”IP地址+端口号“来区分不同服务。

举例:
服务器、TCP/UDP、IP地址、端口号的关系
我是xxx(服务器)
是说汉语(TCP/UDP)
我的楼号(IP地址)是xxxx
我的房间号(端口号)是xxxx
我在等着大家来访,敲门(监听)

客户端
获取服务器IP,再获取服务器端口号连接。

2、字节序

字节序是指多字节在计算机存储或者网络传输各字节的存储顺序。
little endian(小端字节序):将低序字节放在低地址(起始地址)。
Big endian(大端字节序): 将高序字节放在低地址(起始地址)。

举例:
假设需要传输的数据为0x01020304(32位)(前面为高序字节,后面为低序字节)
假设内存地址是 4000 & 4001 & 4002 & 4003
下表为低序 小端字节序

假设内存地址 二进制 十进制
4003 0000 0001 01
4002 0000 0010 02
4001 0000 0011 03
4000 0000 0100 04

下表为高序 大端字节序

假设内存地址 二进制 十进制
4003 0000 0100 04
4002 0000 0011 03
4001 0000 0010 02
4000 0000 0001 01

PS:X86序列CPU均为小端字节序;网络字节序均为大端字节序。

获取网络字节序和主机字节序

//头文件
#include <netinet/in.h> 
//函数
uint16_t htons(uint16_t host16bitvalue);    //返回网络字节序的值
uint32_t htonl(uint32_t host32bitvalue);    //返回网络字节序的值
uint16_t ntohs(uint16_t net16bitvalue);     //返回主机字节序的值 
uint32_t ntohl(uint32_t net32bitvalue);     //返回主机字节序的值 
//h代表host,n代表net,s代表short(两个字节),l代表long(4个字节)。
//通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。
//有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取.

提示:以下是本篇文章正文内容,下面案例可供参考

一、socket服务器与客户端的开发步骤

开发步骤:
1、创捷套接字。
2、为套接字添加信息(IP地址和端口号)。
3、监听网络连接。
4、监听到有客服端接入,接受一个连接。
5、数据交互。
6、关闭套接字,断开连接。

示意图:
![(https://img-blog.csdnimg.cn/2b823d0da6364266bf17e67193d83236.png)

二、具体使用步骤

1.socket(创建连接协议)

函数如下

//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int socket(int domain, int type, int protocol);
//返回
//success 返回新的套接字的文件描述符
//error 返回 -1

参数
domain:指向所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)
AF_INET 代表 IPV4因特网域
AF_INET6 代表 IPV6因特网域
AF_UNIX 代表 UNIX域
AF_ROUTE 代表 路由套接字
AF_KEY 代表 密钥套接字
AF_UNSPEC 代表 未指定

type:指定socket类型
SOCK_STREAM 代表 TCP
SOCK_DGEAM 代表 UDP
SOCK_RAW 代表 IP与ICMP(对底层协议访问)

protocol:通常取为”0“,”0“对于与type类型的对应默认协议。
IPPROTO_TCP 代表 TCP协议
IPPROTO_UDP 代表 UDP协议
IPPROTO_SCIP 代表 SCIP协议
IPPROTO_TIPC 代表 TIPC协议

2.bind(地址准备好)

功能
用于绑定IP地址和端口号到socketfd。
函数如下

//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
//返回
//success 返回 0
//error 返回 -1

参数
sockfd: socket 返回的文件描述符
addr:struct sockaddr的地址,用于设定要绑定服务端的IP和端口号
addrlen:是addr结构的长度,可以设置成sizeof(struct sockaddr)。
其中:

struct sockaddr {
   
   sa_family_t sa_family;  //协议族
   char        sa_data[14];		//IP+端口号
}

上述结构体参数
sa_family 用于指定AF_***表示使用什么协议族的IP。
sa_data 存放IP和端口号。
直接向sa_data中写入IP和端口号有点麻烦,内核提供struct sockaddr_in结构体进行写入,通过**/usr/include/linux/in.h可以看到结构体原型。使用该结构体时需要包含<netinet/in.h>头文件**,且sockaddr_in结构体是专门为tcp/ip协议族使用。
同等替换为:

struct sockaddr_in{
   
		sa_family t	sin_family;//协议族
		in_port_t	sin_port;//端口号
		struct in_addr sin_addr;//IP地址结构体
		unsigned char sin_zero[8];//填充没有实际意义只是为跟sockadd结构在内存中对序这样两者才能相互转换
}

PS:寻找bind的优化结构步骤和命令
cd /user/include/
grep “struct sockaddr_in{” * -nir
vi linux/in.h +184
其中:n是行号 r是递归

bind步骤中需要用到地址转换API

//头文件
#include<arpa/inet.h>
//函数:把字符串形式的“192.168.1.123”转为网络能识别的格式 。
int inet_aton(const char* straddr,struct in_addr *addrp); 
//函数:把网络格式的ip地址转为字符串形式。
char* inet_ntoa(struct in_addr inaddr); 

3.listen(监听)

功能
设置能处理的最大连接数,listen()并未开始接受连线,只是设置sockect的listen模式,listen函数只用于服务器端,服务器进程不知道要与谁连接,因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接。
主要就两个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排邦队的最大连接数。

//头文件
#include<sys/types.h>
#include<sys/socket.h>
//函数
int listen(int sockfd, int backlog);
//返回
//success 返回 0
//error 返回 -1

sockfd:是socket系统调用返回的服务器端socket描述符。
backlog:指定在请求队列中允许的最大请求数。

4.accept(连接)

功能
accept函数由TCP服务器调用,用于从已完成连接队列队头返回下个已亮成连接。如果已完成连接队列为空,那么进程被投入睡眠

函数如下

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
很高兴能够回答你的问题。以下是一个简单的即时通信系统的C++网络编程示例,基于Linux平台使用socket库进行开发。 客户端代码: ```c++ #include <iostream> #include <cstring> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> using namespace std; int main() { // 创建socket int client_socket = socket(AF_INET, SOCK_STREAM, 0); // 配置服务器地址 sockaddr_in server_address{}; server_address.sin_family = AF_INET; server_address.sin_port = htons(8080); server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); // 连接服务器 int connect_result = connect(client_socket, (sockaddr *) &server_address, sizeof(server_address)); if (connect_result == -1) { cout << "连接服务器失败!" << endl; return -1; } // 发送消息 char message[1024] = {0}; while (true) { cin.getline(message, sizeof(message)); if (strlen(message) == 0) { continue; } send(client_socket, message, strlen(message), 0); memset(message, 0, sizeof(message)); } // 关闭socket close(client_socket); return 0; } ``` 服务器端代码: ```c++ #include <iostream> #include <cstring> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> using namespace std; int main() { // 创建socket int server_socket = socket(AF_INET, SOCK_STREAM, 0); // 配置服务器地址 sockaddr_in server_address{}; server_address.sin_family = AF_INET; server_address.sin_port = htons(8080); server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); // 绑定socket和地址 bind(server_socket, (sockaddr *) &server_address, sizeof(server_address)); // 监听客户端连接 listen(server_socket, 10); // 接受客户端连接 sockaddr_in client_address{}; socklen_t client_address_len = sizeof(client_address); int client_socket = accept(server_socket, (sockaddr *) &client_address, &client_address_len); // 接收消息 char message[1024] = {0}; while (true) { int recv_result = recv(client_socket, message, sizeof(message), 0); if (recv_result <= 0) { break; } cout << "收到消息:" << message << endl; memset(message, 0, sizeof(message)); } // 关闭socket close(client_socket); close(server_socket); return 0; } ``` 在上面的代码中,客户端和服务器端都使用了socket库中的函数来完成网络通信。客户端使用`socket`函数创建socket,使用`connect`函数连接服务器,并使用`send`函数发送消息。服务器端使用`socket`函数创建socket,使用`bind`函数将socket服务器地址绑定,使用`listen`函数监听客户端连接,并使用`accept`函数接受客户端连接,最后使用`recv`函数接收客户端发送的消息。 当然,这只是一个简单的示例。在实际开发中,你需要考虑更多的细节,例如如何处理多个客户端连接、如何处理异常情况等等。 希望这个示例能够帮助你了解Linux下C++ socket网络编程的基本流程和方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值