Linux C TCP/UDP 编程

一、常用函数接口
(1)通用结构体:

struct sockaddr {
sa_family_t sa_family;	/* unsigned short , 指定了通信协议家族 */
char sa_data[14];
};

(2)IPV4网络通信地址结构体:

struct sockaddr_in {
		pa_family_t sin_family;
		port_t sin_port;
		struct in_addr {
			unsigned int s_addr;
		}sin_addr;
	};

(3)UNIX本地通信地址结构体:

struct sockaddr_un {
		sa_family_t sun_family;
		char sun_path[108];
	};

(4)socket
函数名

int socket (int domain, int type, int protocl)

功能: 打开一个套接字,并返回一个相关的文件描述符。
返回值:成功, 一个新的文件描述符; 失败, -1;
参数表
int domain, 通信域:
AF_INET | AF_INET6 | AF_UNIX | AF_PACKET
int type, 套接字类型:
SOCK_STREAM 流式套接字
SOCK_DGRAM 数据报套接字
SOCK_RAW 原始套接字

int protocl, 协议(在原始套接字中根据需要选择使用, 流式或数据报套接字中为 0).

(5)bind
函数名

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

功能: 为一个打开的套接字,绑定一个地址(IP+PORT)。
返回值: 成功, 0; 失败, -1;
参数表
int sockfd, 通过socket打开并返回的文件描述符
const struct sockaddr * addr, 通用结构体地址,具体根据通信域选择相关的地址格式。
socklen_t addrlen, 实际地址结构体的大小。

(6)sendto
函数名

 int sendto(int sockfd, void *buff, size_t size, int flag, const struct sockaddr *addr, socklen_t addrlen);

功能: 向一个指定的目标主机发送数据包。
返回值: 成功, 实际发送的字节数; 失败, -1;
参数表
int sockfd, 通过socket打开并返回的文件描述符。
void *bufff, 即将发送的数据。
size_t size, 数据大小。
int flag, 发送标志位(阻塞、非阻塞等, 如果无特殊要求则设置为0)。
const struct sockaddr * addr, 目标主机的地址结构体(IP+PORT)。
socklen_t addrlen, 目标主机地址结构体大小。

(7)recvfrom
函数名

int recvfrom(int sockfd, void *buff, size_t size, int flag, struct sockaddr *addr, socklen_t *addrlen);

功能: 接受一个数据包,并带回数据包的来源地址。
返回值: 成功, 实际接收的字节数; 失败, -1; 对方关闭, 0;
参数表
int sockfd, 通过socket打开并返回的文件描述符。
void *bufff, 接受数据的缓冲区。
size_t size, 缓冲区大小。
int flag, 接收标志位(阻塞、非阻塞等, 如果无特殊要求则设置为0)。
struct sockaddr * addr, 用来保存数据源主机的地址的结构体(IP+PORT)。
socklen_t *addrlen, 期望接收的数据源主机的地址结构体大小(在使用前由用户自己初始化);一旦该函数调用结束,该变量会被修改为实际大小。

(8)listen
函数名:

int listen(int sockfd, int backlog);

功能: 将一个经过bind的套接字置为被动状态,以便将来使用accept接受新的请求。
返回值:成功, 0; 失败, -1;
参数表
int sockfd, 通过socket创建,并通过bind后的套接字描述符。
int backlog, 握手请求队列的最大值。

(9)accept
函数名:

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

功能: 接受一个握手请求,并返回一个新的文件描述符。
返回值: 成功, 新的文件描述符(用来收发数据的通道); 失败, -1;
参数表:
int sockfd, 经过socket创建,bind后并且listen后的文件描述符。
struct sockaddr *addr, 用来带回握手请求方的地址结构体。
socklen_t addrlen, 期望接收的对方的地址大小,该值会被修改为实际大小,调用者需提前初始化。

(10)select
函数名:

 int select(int nfds, fd_set *rdfds, fd_set *wrfds, fd_set *exceptfds, struct timeval * timval);

功能: 在多路IO复用模型中,负责检测多个文件描述符资源。但没有资源可操作时,进程阻塞; 有资源操作时返回可操作的资源个数。
返回值: 成功, 资源个数; 失败, -1; 超时, 0;
参数表:
int nfds, 检测的最大描述符值+1.
fd_set *rdfds, 希望监测的读资源描述符集合。
fd_set *wrfds, 希望监测的写资源描述符集合。
fd_set *wrfds, 希望监测的其他资源描述符集合。
ftruct timeval *timval, 超时监测,设定秒+微秒。

select相关函数:
FD_SET(int fd, fd_set * fds): 将描述加入集合中。
FD_CLR(int fd, fd_set *fds): 将指定描述从集合中移除。
FD_ZERO(fd_set *fds): 清空一个描述符集合。
FD_ISSET(Iint fd, fd_set *fds): 测试一个描述符是否在集合中(测试该描述符资源是否可操作);

(11)其他函数接口:

int send(int sockfd, const void *buff, size_t size, int flag);

int recv(int sockfd, void *buff, size_t size, int flag);

getpeername(int connfd, struct sockaddr *addr, socklen_t *addrlen);

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

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

short htons(short );

int htonl(int);

short ntohs(short);

int ntohl(int);

二、TCP编程框架及代码
TCP编程框架:
客户端:

	socket();
	connect();
	send()/write();
	recv()/read();
	close();
	
服务器端:
	
	socket();
	bind();
	listen();
	accept();
	recv()/read();
	send()/write();
	close();

编程实例:
服务器端:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define ERROR(_msg_) do{	\
	perror(_msg_);	\
	exit(EXIT_FAILURE);		\
}while(0)

#define ERROR2(_msg_)	error(EXIT_FAILURE, errno, _msg_)

#define SERVER_PORT	50001//端口号,注意不要选择已被占用端口号
#define SERVER_ADDR	"192.168.1.13"//服务器端IP地址


#define BUFF_SIZE	1024

int main(int argc, const char *argv[])
{
	int sockfd, connfd;
	struct sockaddr_in local_addr,
					   peer_addr;
	socklen_t addrlen;
	char buff[BUFF_SIZE];
	
	/*建立Socket连接*/
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
		ERROR("socket");

    /*设置socketaddr_in 结构体中相关参数*/
	local_addr.sin_family = AF_INET;
	local_addr.sin_port = htons(SERVER_PORT);
	local_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
	addrlen = sizeof(local_addr);

	/*绑定函数bind()*/
	if (-1 == bind(sockfd, (struct sockaddr *)&local_addr, addrlen))
		ERROR("bind");
		
	/*调用listen()函数,设置监听模式*/
	if (-1 == listen(sockfd ,10))
		ERROR("listen");

	/*调用accept()函数,等待客户端的连接*/
	connfd = accept(sockfd, NULL, NULL);
	if (-1 == connfd)
		ERROR("accept");
	
	/*调用recv函数接收客户端发送的数据*/
	if (-1 == recv(connfd, buff, BUFF_SIZE, 0))
		ERROR("recv");
	puts(buff);
	
	strcpy(buff, "1234567890");
	
	/*调用send函数向客户端发送数据*/
	if (-1 == send(connfd, buff, BUFF_SIZE, 0))
		ERROR("send");

	close(sockfd);
	close(connfd);

	return 0;
}


客户端:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define ERROR(_msg_) do{	\
	perror(_msg_);	\
	exit(EXIT_FAILURE);		\
}while(0)

#define ERROR2(_msg_)	error(EXIT_FAILURE, errno, _msg_)

#define CLIENT_PORT	50001//端口号,注意不要选择已被占用端口号
#define CLIENT_ADDR	"192.168.1.13"客户端ip地址


#define BUFF_SIZE	1024

int main(int argc, const char *argv[])
{
	int sockfd;
	struct sockaddr_in local_addr,
					   peer_addr;
	socklen_t addrlen;
	char buff[BUFF_SIZE];
	
	/*建立Socket连接*/
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
		ERROR("socket");

	 /*设置socketaddr_in 结构体中相关参数*/
	peer_addr.sin_family = AF_INET;
	peer_addr.sin_port = htons(SERVER_PORT);
	peer_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
	addrlen = sizeof(peer_addr);
	
	/*调用connect()函数向服务器端建立TCP连接*/
	if (-1 == connect(sockfd, (struct sockaddr *)&peer_addr, addrlen))
		ERROR("connect");

	strcpy(buff, "helloworld");
	
	/*发送消息给客户端*/
	if (-1 == send(sockfd, buff, BUFF_SIZE, 0))
		ERROR("send");

	/*接收服务器所发送消息*/
	if (-1 == recv(sockfd, buff, BUFF_SIZE, 0))
		ERROR("recv");
	puts(buff);
	//read();
	//
	close(sockfd);

	return 0;
}


通信验证(以下为本机通信):
在这里插入图片描述

三、UDP编程框架及代码
UDP编程接口:

客户端:
	socket():	
	sendto():	
	recvfrom();
	close();

服务器端:
	socket();
	bind():	
	recvfrom();
	sendto();
	close();

编程实例:
客户端:

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <error.h>

#define error_exit(_errmsg_)	error(EXIT_FAILURE, errno, _errmsg_)

#define BUFF_SIZE	1024

#define CLT_PORT		5000//端口号
#define CLTADDR		"169.254.8.225"//客户端ip

int main()
{
	int sockfd;
	struct sockaddr_in seraddr;
	char buff[BUFF_SIZE];
	int ret;
	int count = 0;
	
	/*设置socketaddr_in 结构体中相关参数*/
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(SER_PORT);
	seraddr.sin_addr.s_addr = inet_addr(SER_ADDR);
	
	/*建立Socket连接*/
	if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0)))
		error_exit("socket");

	while (1) {
		sprintf(buff, "helloworld%d", count ++);
		
		/*发送消息给服务器*/
		if (-1 == sendto(sockfd, buff, strlen(buff)+1, 0,
					(struct sockaddr *)&seraddr, sizeof(seraddr)))
			error_exit("sendto");

		memset(buff, 0, BUFF_SIZE);
		
		/*接收服务器发送的消息*/
		if (-1 == (ret = recvfrom(sockfd, buff, BUFF_SIZE, 0, NULL, NULL)))
			error_exit("recvfrom");
		printf("[%d]%s\n", ret, buff);
	}
	close(sockfd);

	return 0;
}


服务器端

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <error.h>

#define error_exit(_errmsg_)	error(EXIT_FAILURE, errno, _errmsg_)
#define BUFF_SIZE	1024

#define SER_PORT		50000
#define SER_ADDR		"169.254.8.225"

int main()
{
	int sockfd;
	struct sockaddr_in seraddr,
					   cltaddr;
	char buff[BUFF_SIZE];
	int ret;
	socklen_t addrlen = sizeof(cltaddr);
	
	/*设置socketaddr_in 结构体中相关参数*/
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(SER_PORT);
	seraddr.sin_addr.s_addr = inet_addr(SER_ADDR);
	
	/*建立Socket连接*/
	if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0)))
		error_exit("socket");
	
	/*绑定函数bind*/
	if (-1 == bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr)))
		error_exit("bind");

	while (1) {
		memset(buff, 0, BUFF_SIZE);

		/*调用recvfrom()等待接受客户端的数据*/
		if (-1 == (ret = recvfrom(sockfd, buff, BUFF_SIZE, 0,
						(struct sockaddr *)&cltaddr, &addrlen)))
			error_exit("recvfrom");
		printf("%s:%d->%s\n", 
				inet_ntoa(cltaddr.sin_addr), ntohs(cltaddr.sin_port), buff);
		
		/*向客户端发送数据*/
		if (-1 == sendto(sockfd, buff, strlen(buff)+1, 0,
					(struct sockaddr *)&cltaddr, sizeof(cltaddr)))
			error_exit("sendto");
	}
	close(sockfd);

	return 0;
}

补充声明两个函数:
htons(): 将2字节的本机字节序转换成网络字节序。
inet_addr(): 将点分十进制的IP地址转换成二进制的IP地址,并且按照网络字节序存放。
通信验证(本机):
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值