【Socket】Linux下UDP Socket的基本流程以及connect、bind函数的使用(C语言实现)
文章目录
【Socket】Linux下UDP Socket中connect、bind函数的使用(C语言实现)
一、UDP Socket简介
Socket的原意是“插座”,在计算机通信领域,socket 被翻译为“套接字”。
Socket通信主要有两个类型:TCP、UDP。
TCP通信,是一个有序的、可靠的、面向连接的通信方式。用数据流的方式传递信息。
UDP通信,是无连接的、不保证有序到达的、但具有较好的实时性、能够高速传输的通信方式。用数据报的方式传递信息。
其中,UDP Socket主要使用场景为:实时性要求高,可以接受一定的数据错误和丢失。例如在线游戏、音视频聊天等场景。
UDP Socket对于客户端和服务端的区分不明显,在需要时,客户端和服务端均可以使用connect、bind函数。
因此,本文从对我个人来说更好理解的角度定义客户端和服务端,即以我方服务和对方服务来区分两端。
二、Linux下socket的基本流程
在此介绍Linux系统下UDP Socket通信的基本流程,使用C语言实现。(Windows系统下另需要其他步骤如WSAStartup,请自行查找)
1、头文件引用
#include <stdio.h>
#include <string.h>
#include <unistd.h> // 提供通用的文件、目录、程序及进程操作的函数,如close()
#include <sys/socket.h> // 提供socket主要函数
#include <arpa/inet.h> // 提供IP地址格式转换函数;包含了<netinet/in.h>,提供sockaddr_in数据结构
//#include <netdb.h> // 未使用,提供设置及获取域名的函数
2、宏定义部分
#define MY_PORT 2024 // 我方服务PORT
#define THEY_IP "192.168.0.88" // 对方服务IP
#define THEY_PORT 2023 // 对方服务PORT
#define SOCKET int // Linux下的socket()函数返回int类型,这里定义SOCKET方便理解
3、声明全局变量
SOCKET mySocket; // (我方)网络套接字
sockaddr_in mySockAddr; // 我方网络地址
sockaddr_in theySockAddr; // 对方网络地址
4、定义和配置Socket
bool init() {
// 定义(我方)网络套接字
mySocket = socket(AF_INET, SOCK_DGRAM, 0);
// 定义我方物理地址配置
mySockAddr.sin_family = AF_INET;
mySockAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听本机所有地址
mySockAddr.sin_port = htons(MY_PORT);
// 定义对方物理地址配置
theySockAddr.sin_family = AF_INET;
theySockAddr.sin_addr.s_addr = inet_addr(THEY_IP);
theySockAddr.sin_port = htons(THEY_PORT);
return true;
}
5、connect和bind函数介绍
bind()函数一般在服务器端使用,表示绑定我方服务socket的IP和端口。
int bind(SOCKET socket, const struct sockaddr * addr, socklen_t len)
// socket套接字,addr我方物理地址,len我方物理地址长度
connect()函数一般在客户端使用,表示把我方服务socket连接至对方服务IP和端口,是面向连接的。主要作用在后续send与sendto函数的使用区别。使send更加方便,就不需要每次使用sendto指定目标服务了。
int connect(SOCKET socket, const struct sockaddr * addr, socklen_t len)
// socket套接字,addr对方物理地址,len对方物理地址长度
6、套接字绑定
绑定我方套接字和我方服务地址
int ret = bind(mySocket, (sockaddr*)&mySockAddr, sizeof(mySockAddr)); // ret返回0成功绑定
7、(可选)连接至对方服务
把我方套接字连接至对方服务地址,连接后向对方服务发送数据使用send函数,否则使用sendto函数
int ret = connect(mySocket, (sockaddr*)&theySockAddr, sizeof(theySockAddr));
8、收发数据
存储数组定义:
char recvBuf[512]; // 接收信息存储的数组地址
char sendBuf[512]; // 发送信息存储的数组地址
char tempBuf[512]; // 中间数据
接收数据:
sockaddr_in otherSockAddr; // 定义对方服务网络地址,这个地址不只表示第三节说的对方网络地址,也包括其他服务的地址,这里作为存储单元使用
socklen_t sockLen = sizeof(otherSockAddr);
int recvLen = recvfrom(mySocket, recvBuf, 512, 0, (sockaddr*)&otherSockAddr, &sockLen); // 返回接收数据长度,长度>0认为有数据
printf("From: %s:%d.nMessage: %sn", inet_ntoa(otherSockAddr.sin_addr), ntohs(otherSockAddr.sin_port), recvBuf); // 获得对方服务的ip/port
发送数据:
// 可以接收了谁的数据返回给谁,也可以固定一个目标服务,以前者为例
strcpy(sendBuf, "GOT IT!");
sendto(mySocket, sendBuf, 512, 0, (sockaddr*)&otherSockAddr, sockLen); // 通过我的套接字发送给对方服务
// connect时使用:仅发送给连接的对象
send(mySocket, sendBuf, 512, 0);
9、关闭套接字
close(mySocket);
10、完整的主函数
int main() {
// 1、初始配置
if (!init()) {
printf("INIT ERROR!n");
return -1;
}
// 2、我方服务套接字绑定
int ret = bind(mySocket, (sockaddr*)&mySockAddr, sizeof(mySockAddr));
if (ret == 0) {
printf("UDP SERVER IS START AND BIND TO %d.n", MY_PORT);
}
else {
printf("PORT %d BIND FAILED!n", MY_PORT);
return -1;
}
// (可选)连接至对方服务
//int ret = connect(mySocket, (sockaddr*)&theySockAddr, sizeof(theySockAddr));
// 3、定义存储地址
char recvBuf[512]; // 接收信息存储的数组地址
char sendBuf[512]; // 发送信息存储的数组地址
char tempBuf[512]; // 中间数据
// 4、定义接收到的其他服务地址
sockaddr_in otherSockAddr;
socklen_t sockLen = sizeof(otherSockAddr);
// 持续收发
while (true) {
// 5、接收
int recvLen = recvfrom(mySocket, recvBuf, 512, 0, (sockaddr*)&otherSockAddr, &sockLen); // 返回接收数据长度,长度>0认为有数据
printf("From: %s:%d.nMessage: %sn", inet_ntoa(otherSockAddr.sin_addr), ntohs(otherSockAddr.sin_port), recvBuf); // 打印接收到的对方服务的ip/port
if (!strcmp(recvBuf, "exit")) { // 接收到exit退出收发(这个方式退出不安全)
printf("Exit success.n");
break;
}
// 6、发送
strcpy(sendBuf, "GOT IT!");
sendto(mySocket, sendBuf, 512, 0, (sockaddr*)&otherSockAddr, sockLen);
// connect时使用:仅发送给连接的对象
//send(mySocket, sendBuf, 512, 0);
}
// 7、关闭套接字
close(mySocket);
return 0;
}
(代码没有经过测试,相信各位都是成熟的程序员,会自己改bug(๑•̀ㅂ•́)و✧)