当前位置:   article > 正文

【Socket】Linux下UDP Socket的基本流程以及connect、bind函数的使用(C语言实现)_linux connect

linux connect


【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> // 未使用,提供设置及获取域名的函数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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方便理解
  • 1
  • 2
  • 3
  • 4

3、声明全局变量

SOCKET mySocket; // (我方)网络套接字
sockaddr_in mySockAddr; // 我方网络地址
sockaddr_in theySockAddr; // 对方网络地址
  • 1
  • 2
  • 3

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

5、connect和bind函数介绍

bind()函数一般在服务器端使用,表示绑定我方服务socket的IP和端口。

int bind(SOCKET socket, const struct sockaddr * addr, socklen_t len)
// socket套接字,addr我方物理地址,len我方物理地址长度
  • 1
  • 2

connect()函数一般在客户端使用,表示把我方服务socket连接至对方服务IP和端口,是面向连接的。主要作用在后续send与sendto函数的使用区别。使send更加方便,就不需要每次使用sendto指定目标服务了。

int connect(SOCKET socket, const struct sockaddr * addr, socklen_t len)
// socket套接字,addr对方物理地址,len对方物理地址长度
  • 1
  • 2

6、套接字绑定

绑定我方套接字和我方服务地址

int ret = bind(mySocket, (sockaddr*)&mySockAddr, sizeof(mySockAddr)); // ret返回0成功绑定
  • 1

7、(可选)连接至对方服务

把我方套接字连接至对方服务地址,连接后向对方服务发送数据使用send函数,否则使用sendto函数

int ret = connect(mySocket, (sockaddr*)&theySockAddr, sizeof(theySockAddr)); 
  • 1

8、收发数据

存储数组定义:

char recvBuf[512]; // 接收信息存储的数组地址
char sendBuf[512]; // 发送信息存储的数组地址
char tempBuf[512]; // 中间数据
  • 1
  • 2
  • 3

接收数据:

sockaddr_in otherSockAddr; // 定义对方服务网络地址,这个地址不只表示第三节说的对方网络地址,也包括其他服务的地址,这里作为存储单元使用
socklen_t sockLen = sizeof(otherSockAddr);

int recvLen = recvfrom(mySocket, recvBuf, 512, 0, (sockaddr*)&otherSockAddr, &sockLen); // 返回接收数据长度,长度>0认为有数据

printf("From: %s:%d.\nMessage: %s\n", inet_ntoa(otherSockAddr.sin_addr), ntohs(otherSockAddr.sin_port), recvBuf); // 获得对方服务的ip/port
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

发送数据:

// 可以接收了谁的数据返回给谁,也可以固定一个目标服务,以前者为例
strcpy(sendBuf, "GOT IT!");
sendto(mySocket, sendBuf, 512, 0, (sockaddr*)&otherSockAddr, sockLen); // 通过我的套接字发送给对方服务

// connect时使用:仅发送给连接的对象
send(mySocket, sendBuf, 512, 0);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

9、关闭套接字

close(mySocket);
  • 1

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: %s\n", 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

(代码没有经过测试,相信各位都是成熟的程序员,会自己改bug(๑•̀ㅂ•́)و✧)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/187179
推荐阅读
相关标签
  

闽ICP备14008679号