close()
client
server
UPD通讯
2. API
详细API请参考:https://pubs.opengroup.org/onlinepubs/007908799/xns/syssocket.h.html
- 创建套接字的函数是socket()
int socket(int domain, int type, int protocol);
/\*
- 其中 “int domain”参数表示套接字要使用的协议簇,协议簇的在“linux/socket.h”里有详细定义,常用的协议簇:
AF\_UNIX(本机通信)
AF\_INET(TCP/IP – IPv4)
AF\_INET6(TCP/IP – IPv6)
- 其中 “type”参数指的是套接字类型,常用的类型有:
SOCK\_STREAM(TCP流)
SOCK\_DGRAM(UDP数据报)
SOCK\_RAW(原始套接字)
- 最后一个 “protocol”一般设置为“0”,也就是当确定套接字使用的协议簇和类型时,这个参数的值就为0,但是有时候创建原始套接字时,并不知道要使用的协议簇和类型,也就是domain参数未知情况下,这时protocol这个参数就起作用了,它可以确定协议的种类。
socket是一个函数,那么它也有返回值,当套接字创建成功时,返回套接字,失败返回“-1”,错误代码则写入“errno”中。
\*/
// 实例
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/socket.h>
int sock_fd_tcp;
int sock_fd_udp;
sock_fd_tcp = socket(AF_INET, SOCK_STREAM, 0); // 创建tcp通讯的套接字
sock_fd_udp = socket(AF_INET, SOCK_DGRAM, 0); // 创建udp通讯的套接字
if(sock_fd_tcp < 0) {
perror("TCP SOCKET ERROR!\n");
exit(-1);
}
if(sock_fd_udp < 0) {
perror("UDP SOCKET ERROR!\n");
exit(-1);
}
- 地址与端口设置的结构体 sockaddr_in
#include <netinet/in.h>
struct sockaddr\_in{
unsigned short sin_family;
unsigned short int sin_port;
struct in\_addr sin_addr;
unsigned char sin_zero[8];
};
struct in\_addr{
unsigned long s_addr;
};
/\*
sin\_family表示地址类型,对于基于TCP/IP传输协议的通信,该值只能是AF\_INET;
sin\_prot表示端口号,例如:21 或者 80 或者 27015,总之在0 ~ 65535之间;
sin\_addr表示32位的IP地址,例如:192.168.1.5 或 202.96.134.133;
sin\_zero表示填充字节,一般情况下该值为0;
Socket数据的赋值实例:
\*/
// 实例
struct sockaddr\_in addr;
memset(&addr, 0, sizeof(addr)); // 将结构体清零 主要是sin\_zero表示填充字节为零
addr.sin_family = AF_INET; //(TCP/IP – IPv4)
addr.sin_port = htons(port_out); // 绑定端口号 htons将一个无符号短整型数值转换为网络字节序
addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定IP htonl就是把ip字节顺序转化为网络字节顺序
// INADDR\_ANY 泛指机器所有的IP因为有些电脑不只有一个网卡
- 把名字和套接字相关联 bind()
int bind( int sockfd, const struct sockaddr \* addr, socklen_t \* addrlen);
/\*
当用socket()函数创建套接字以后,套接字在名称空间(网络地址族)中存在,但没有任何地址给它赋值。bind()把用addr指定的地址赋值给用文件描述符代表的套接字sockfd。addrlen指定了以addr所指向的地址结构体的字节长度。一般来说,该操作称为“给套接字命名”。
通常,在一个SOCK\_STREAM套接字接收连接之前,必须通过bind()函数用本地地址为套接字命名。
\*/
// 实例
#include <sys/types.h>
#include <sys/socket.h>
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr\_in addr;
socklen_t addr_len = sizeof(addr);
if (bind(sockfd, (struct sockaddr\*)&addr, addr_len) == -1){
printf("Failed to bind socket on port %d\n", port_out);
close(sockfd);
return false;
}
- 接受消息的函数recvfrom()
int recvfrom(int sockfd, void \* buf, size_t len, int flags, struct sockaddr \* src_addr, socklen_t \* addrlen);
/\*
recvfrom: 用于接收数据
- sockfd:用于接收UDP数据的套接字;
- buf:保存接收数据的缓冲区地址;
- len:可接收的最大字节数(不能超过buf缓冲区的大小);
- flags:可选项参数,若没有可传递0;
- src\_addr:存有发送端地址信息的sockaddr结构体变量的地址;
- addrlen:保存参数 src\_addr的结构体变量长度的变量地址值。
\*/
- 发送消息的函数sendto()
int sendto(int sockfd, const void \* buf, size_t len, int flags, const struct sockaddr \* dest_addr, socklen_t addrlen);
/\*
sendto:用于发送数据
- sockfd:用于传输UDP数据的套接字;
- buf:保存待传输数据的缓冲区地址;
- len:带传输数据的长度(以字节计);
- flags:可选项参数,若没有可传递0;
- dest\_addr:存有目标地址信息的 sockaddr 结构体变量的地址;
- addrlen:传递给参数 dest\_addr的地址值结构体变量的长度。
\*/
注意:UDP套接字不会保持连接状态,每次传输数据都要添加目标地址信息,这相当于在邮寄包裹前填写收件人地址。
3.测试程序
- server
#include<sys/select.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <iostream>
int main(){
//同一台电脑测试,需要两个端口
int port_in = 12321;
int port_out = 12322;
int sockfd;
// 创建socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1==sockfd){
return false;
puts("Failed to create socket");
}
// 设置地址与端口
struct sockaddr\_in addr;
socklen_t addr_len = sizeof(addr);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // Use IPV4
addr.sin_port = htons(port_out); //
addr.sin_addr.s_addr = htonl(INADDR_ANY);
// Time out
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 200000; // 200 ms
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char\*)&tv, sizeof(struct timeval));
// Bind 端口,用来接受之前设定的地址与端口发来的信息,作为接受一方必须bind端口,并且端口号与发送方一致
if (bind(sockfd, (struct sockaddr\*)&addr, addr_len) == -1){
printf("Failed to bind socket on port %d\n", port_out);
close(sockfd);
return false;
}
char buffer[128];
memset(buffer, 0, 128);
int counter = 0;
while(1){
struct sockaddr\_in src;
socklen_t src_len = sizeof(src);
memset(&src, 0, sizeof(src));
// 阻塞住接受消息
int sz = recvfrom(sockfd, buffer, 128, 0, (sockaddr\*)&src, &src_len);
if (sz > 0){
buffer[sz] = 0;
printf("Get Message %d: %s\n", counter++, buffer);
}
else{
puts("timeout");
}
}
close(sockfd);
return 0;
}
- client
#include<sys/select.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <iostream>
int main(){
int port_in = 12321;
int port_out = 12322;
int sockfd;
// 创建socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(-1==sockfd){
return false;
puts("Failed to create socket");
}
// 设置地址与端口
struct sockaddr\_in addr;
socklen_t addr_len=sizeof(addr);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // Use IPV4
addr.sin_port = htons(port_in); //
addr.sin_addr.s_addr = htonl(INADDR_ANY);
// Time out
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 200000; // 200 ms
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char\*)&tv, sizeof(struct timeval));
// 绑定获取数据的端口,作为发送方,不绑定也行
if (bind(sockfd, (struct sockaddr\*)&addr, addr_len) == -1){
printf("Failed to bind socket on port %d\n", port_in);
close(sockfd);
return false;
}
int counter = 0;
while(1){
addr.sin_family = AF_INET;
addr.sin_port = htons(port_out);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
sendto(sockfd, "hello world", 11, 0, (sockaddr\*)&addr, addr_len);
printf("Sended %d\n", ++counter);
sleep(1);
}
close(sockfd);
return 0;
![img](https://img-blog.csdnimg.cn/img_convert/4e9644dc68dce8709a38e6f4a3c09360.png)
![img](https://img-blog.csdnimg.cn/img_convert/45ff66a0378a9549ea8cf61758c12221.png)
![img](https://img-blog.csdnimg.cn/img_convert/7bf657945c695d31ae8155bc13a92f4c.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!**
详情docs.qq.com/doc/DSmdCdUNwcEJDTXFK
printf("Sended %d\n", ++counter);
sleep(1);
}
close(sockfd);
return 0;
[外链图片转存中...(img-LaOKnz3y-1724767572218)]
[外链图片转存中...(img-Plu7xO6P-1724767572218)]
[外链图片转存中...(img-Apmn0LJ9-1724767572219)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上运维知识点,真正体系化!**
详情docs.qq.com/doc/DSmdCdUNwcEJDTXFK