Socket编程

本文详细介绍了Linux下的套接字通信机制,包括socket的创建、bind、listen、accept、connect等关键步骤,以及IP地址格式转换函数如inet_aton和inet_pton的应用。
摘要由CSDN通过智能技术生成

一、socket介绍

套接字(socket)是Linux下的一种进程间通信的机制。这个机制不仅可以在不同主机之间实现网络通信,也可以在同一台主机上的不同应用程序完成通信。socket通常使用客户端<----->服务端这种模式完成通信。多个客户端可以同时连接到同一个服务器,由服务器完成数据处理,再将结果返回给客户端。
socket是应用层与TCP/IP协议通信间的中间软件抽象层,它是一组接口。

二、socket编程

使用socket接口需要包含的头文件:

//Linux
#include <sys/types.h>
#include <sys/socket.h>
//Windows
#pragma comment(lib,"ws2_32.lib")
#include <sys/types.h>
#include <WinSock2.h>

1、创建一个socket-----socket()

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

socket():用于创建一个网络通信端点(相当于打开了一道门,供数据流出和流入)。

  • domain:用于指定一个通信域(通常选择AF_INET)
协议族名字说明
AF_UNIX,AF_LOCALLocal communication [unix(7)]
AF_INETIPv4 Internet protocols [ip(7)]
AF_INET6IPv6 Internet protocols [ipv6(77)
AF_IPXIPX - Novell protocols
AF_NETLINKKernel user interface device [netlink(7)]
。。。。。。。。。。
  • type:指定套接字的类型
类型名字作用
SOCK_STREAM提供有序的,可靠的,双向的,基于连接通道的字节流,能保证数据完整的传送给对方,用于TCP协议;可以支持带外数据传输机制
SOCK_DGRAM固定长度,无连接,不可靠的报文传递,用于UDP协议
SOCK_SEQPACKET固定长度,有序的,可靠的,面向连接的报文传递
SOCK_RAW表示原始套接字,它允许应用程序访问网络层的原始数据包,此套接字类型使用较少
SOCK_RDM提供不保证排序的可靠数据报层
SOCK_PACKET已经过时
  • protocol:通常设置为0,表示为给定的通信域和套接字协议类型选择默认协议。

样例:

#include <iostream>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")

using namespace std;

int main()
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	int socketfd = socket(AF_INET,SOCK_STREAM,0);
	if (socketfd < 0)
	{
		cout << "failed to create socket" << endl;
		return -1;
	}

	cout << "success to create socket" << endl;
	return 0;
}

在这里插入图片描述

2、bind()

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

此函数用来将一个IP地址或端口号与一个socket绑定在一起(即套接字和地址/端口 互相关联)。一般情况下,会将一个服务器的套接字绑定到一个众所周知的IP地址和端口号,即一个固定的服务器(客户端应用程序提前就直到的IP地址和端口号)。因为对于客户端与服务器通信,客户端需要直到目的地址的IP地址和端口号。

  • sockfd:要绑定的套接字
  • addr:一个指针,指向struct sockaddr
struct sockaddr {
 sa_family_t sa_family;
 char sa_data[14];
}

sa_data:通用的socket地址结构体,char类型数组,一共14个字节,包括IP地址、端口号等信息。(用户无法进行对其数组赋值)对开发用户不好。

一般我们使用struct sockaddr_in结构体(sockaddr_in和sockaddr是并列的结构体,两者的占用空间一样,所以指向sockaddr_in的指针也可以指向sockaddr结构体)。

struct sockaddr_in {
 sa_family_t sin_family; /* 协议族 */
 in_port_t sin_port; /* 端口号 */
 struct in_addr sin_addr; /* IP 地址 */
 unsigned char sin_zero[8];
};
  • addrlen:指定了addr所指向的结构体对于的字节长度

样例:

#include <iostream>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")

using namespace std;

int main()
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	int socketfd = socket(AF_INET,SOCK_STREAM,0);
	if (socketfd < 0)
	{
		return -1;
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(111);

	int ret = bind(socketfd, (const sockaddr*)&addr, sizeof(addr));
	if (ret < 0)
	{
		cout << "bind failed" << endl;
		return -1;
	}
	cout << "success bind" << endl;
	return 0;
}

在这里插入图片描述
注意
这里的htonshtonl作用是为了避免大小端的问题。
bind()函数并不是总需要调用的,如果用户进程并不需要与一个具体IP地址和端口号互联的话。

3、listen()

int listen(int sockfd, int backlog);

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

  • sockfd:server套接字
  • backlog:sockfd的等待连接队列能够达到的最大值。一个服务器会存在多个客户端同时连接服务器,这些请求连接会放入队列中让服务器按照先后顺序依次处理,而这个参数就是告诉内核来控制队列的上限。当客户端请求连接队列满时,会受到一个表示连接失败的错误,本次连接会直接丢弃掉不做任何处理。

4、accpet()

服务器调用此函数之后,就会获取客户端的连接请求并建立起连接。

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

5、connect()

此函数用于客户端中,客户端调用connect()将socket与服务器进行连接。

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

TCP:发生在TCP连接的握手过程中,最终建立一个TCP连接。
UDP:只是在sockfd中记录服务器IP地址和端口号,并不会发送数据。

6、发送数据和接受数据

  • read()
    读取指定字节大小的数据放入到指定的缓冲区中。

  • recv()

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

flags:通常设置为0

flags描述
MSG_CMSG_CLOEXEC为UNIX域套接字上接受的文件描述符设置执行时关闭标志
MSG_DONTWAIT启动非阻塞操作
MSG_ERRQUEUE接受错误信息作为辅助数据
MSG_OOB如果协议支持,获取带外数据
MSG_PEEK返回数据包内容而不真正的取走数据包
MSG_TRUNC即使数据包被拦截,也返回数据包的长度
MSG_WAITALL等待直到所有的数据可用(只用于SOCK_STREAM
  • write()
  • send()
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • closesocket():关闭套接字

三、IP地址格式转换函数

通常的IP地址形式:xxx.xxx.xxx.xxx ,这是一种字符串的形式,而计算器需要处理的是二进制的数据,因此需要将IP地址由字符串转换成二进制。
头文件:** Ws2tcpip.h**

1、inet_aton、inet_addr、inet_ntoa

  • inet_aton:将网络字节序转换为IPv4地址。
  • inet_ntoa:将IPv4 地址转换为网络字节序。
  • inet_addr:将点分十进制的 IPv4 地址字符串转换为网络字节序。

2、inet_pton、inet_ntop

inet_pton是Internet Protocol to Presentation Numeric的缩写
可以将点分文本的IP地址转换为二进制网络字节序”的IP地址,而且inet_pton和inet_ntop这2个函数能够处理ipv4和ipv6。

int inet_pton(int af, const char *src, void *dst);
  • af:必须是AF_INET(ipv4)或者AF_INET6(ipv6)
  • src:转化的地址
  • dst:对应struct in_addr/struct in6_addr 结构体

inet_ntop是Internet Numeric To Presentation的缩写。

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

样例:

#include <iostream>
#include <WinSock2.h>
#include <Ws2tcpip.h>

#pragma comment(lib,"ws2_32.lib")

using namespace std;

int main()
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	struct in_addr addr;
	inet_pton(AF_INET,"192.168.0.1",&addr);
	cout << addr.S_un.S_addr << endl;

	char buf[40];
	inet_ntop(AF_INET, &addr, buf, sizeof(buf));
	cout << buf << endl;

	return 0;
}

在这里插入图片描述

四、一些其它函数

int setsockopt (int sockfd, int level, int optname, const void *optval, socklen_t optlen);



(1) int sockfd: 很简单,socket句柄
(2) int level: 选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次
(3) int optname: 需设置的选项
(4) const void *optval: 指针,指向存放选项值的缓冲区
(5) socklen_t optlen: optval缓冲区的长度

optname定义如下:
#define SO_DEBUG 1            -- 打开或关闭调试信息
#define SO_REUSEADDR 2        -- 打开或关闭地址复用功能
#define SO_TYPE 3             -- 
#define SO_ERROR 4
#define SO_DONTROUTE 5
#define SO_BROADCAST 6
#define SO_SNDBUF 7           -- 设置发送缓冲区的大小
#define SO_RCVBUF 8           -- 设置接收缓冲区的大小
#define SO_KEEPALIVE 9        -- 套接字保活
#define SO_OOBINLINE 10
#define SO_NO_CHECK 11
#define SO_PRIORITY 12        -- 设置在套接字发送的所有包的协议定义优先权
#define SO_LINGER 13
#define SO_BSDCOMPAT 14
#define SO_REUSEPORT 15
#define SO_PASSCRED 16
#define SO_PEERCRED 17
#define SO_RCVLOWAT 18
#define SO_SNDLOWAT 19
#define SO_RCVTIMEO 20       -- 设置接收超时时间
#define SO_SNDTIMEO 21       -- 设置发送超时时间
#define SO_ACCEPTCONN 30
#define SO_SNDBUFFORCE 32
#define SO_RCVBUFFORCE 33
#define SO_PROTOCOL 38
#define SO_DOMAIN 39

ntohs

ntohs 是一个函数,它用于将网络字节序(Network Byte Order)中的 16 位无符号整数(unsigned short)转换为主机字节序(Host Byte Order)。

send和sendto

int send(int sockfd, const void *buf, size_t len, int flags);   

send 函数通常用于已连接的套接字上发送数据,它不需要指定目标地址,因为在调用 send 函数之前,套接字已经与远程主机建立了连接。这个函数通常用于 TCP 套接字。

int sendto(int s, const void *buf, int len, unsigned int flags, 
        const struct sockaddr *to, int tolen);

sendto 函数通常用于在无连接的套接字上发送数据,它可以向指定的目标地址发送数据,并且可以指定一些其他参数,比如目标地址的长度等。这个函数通常用于 UDP 套接字。

总的来说,sendto 适用于 UDP 套接字,而 send 适用于 TCP 套接字。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Rain_ZZX

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

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

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

打赏作者

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

抵扣说明:

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

余额充值