Linux 之BSD Socket网络编程 API

本文详细介绍了socket编程相关函数,包括创建socket对象、绑定IP和端口、服务器监听、客户端发起连接等操作,还说明了TCP和UDP数据的发送接收、关闭套接字以及获取socket信息等函数的使用,最后给出了C/S架构的简单示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

1、创建socket对象

//from /usr/include/sys/socket.h
int socket(int domain,int type,int protocol);

函数执行成功,返回一个socket文件描述符,执行失败返回-1。
函数第1个参数用来指明该socket要使用的地址协议簇,即通信协议类型,常用的选项如下:

#define PF_LOCAL    1             /* 本地通信 ,即AF_LOCAL*/
#define PF_UNIX     PF_LOCAL      /* 本地通信 */
#define PF_FILE     PF_LOCAL      /* 本地通信 */
#define PF_INET     2             /* IPV4协议簇,即AF_INET */ 
#define PF_AX25     3             /* AX.25 */ 
#define PF_IPX      4             /* Novell网协议 */
#define PF_INET6    10            /* IPV6协议簇 */

函数的第2个参数指定socket的类型,选项如下:

//from /usr/include/bits/socket.h
enum __socket_type
{
	SOCK_STREAM = 1;   //TCP
	SOCK_DGRAM = 2;    //UDP
	SOCK_RAM = 3;      //原始套接字
};

函数第3个参数指定使用协议簇中哪个协议,若为0,字系统自动选择。

2、绑定本地IP地址和端口

//from /usr/include/sys/socket.h
int bind(int socket,struct sockaddr* addr,sockelen_t len);

函数执行成功返回0,执行失败返回-1。
函数第1个参数为socket套接字。
函数第2个参数为一个指向strcut sockaddr的指针,根据socket不同的协议类型,其结构也不同,原始定义相对复杂,下面是我整理后的结构体定义,可以直接使用:

/* Structure describing a generic socket address.  */
struct sockaddr
{
 uint16 sa_family;           /* Common data: address family and length.  */
 char sa_data[14];           /* Address data.  */
};

/* Structure describing an Internet socket address.  */
struct sockaddr_in
{
 uint16 sin_family;          /* Address family AF_INET */ 
 uint16 sin_port;            /* Port number.  */
 uint32 sin_addr.s_addr;     /* Internet address.  */
 unsigned char sin_zero[8];  /* Pad to size of `struct sockaddr'.  */
};

/* Ditto, for IPv6.  */
struct sockaddr_in6
{
 uint16 sin6_family;         /* Address family AF_INET6 */
 uint16 sin6_port;           /* Transport layer port # */
 uint32 sin6_flowinfo;       /* IPv6 flow information */
 uint8  sin6_addr[16];       /* IPv6 address */
 uint32 sin6_scope_id;       /* IPv6 scope-id */
};

3、服务器监听网络(服务器)

//from /usr/include/sys/socket.h
int listen(int socket,int num);

该函数使socket套接字变为监听套接字,准备接受客户端的连接,执行成功返回0,失败返回-1。
函数第1个参数为一个socket套接字。
函数第2个参数为客户端请求连接的排队个数。

4、客户端发起连接

//from /usr/include/sys/socket.h
int connect(int socket,struct sockaddr* addr,sockelen_t len );

函数执行成功,则与地址为addr的服务器建立连接,并返回0,失败返回-1。
函数第1个参数为一个socket套接字。
函数第2个参数为服务器地址,第3个参数为地址长度。

5、服务器建立连接

//from /usr/include/sys/socket.h
int accept(int socket,struct sockaddr* addr,sockelen_t len );

该函数将阻塞等待客户端的连接,如果执行成功,函数将返回一个新的socket套接字,从而使原套接字继续等待其他客户端的连接,执行失败返回-1。
函数第1个参数为绑定服务器端口地址的socket套接字。
函数第2个参数用来存储接受到的客户端的端口和地址,第3个参数为地址长度。

6、TCP发送接收数据

//from /usr/include/sys/socket.h
/*向socket发送首地址为buf,长度为len的数据*/
size_t send(int socket,void* buf,size_t len,int flags);

/*从socket接收数据并保存在首地址为buf,长度为len的地址中*/
size_t recv(int socket,void* buf,size_t len,int flags);

若flags为0,则以上函数同read/write函数,为阻塞方式,若为MSG_DONTWAIT,则为非阻塞方式,函数执行成功返回读写数据的大小,失败返回-1。

7、关闭套接字

close(int socket);
int shutdown(int socket,int how);

函数shutdown()可以根据参数how指定关闭套接字的某一端:
how = 0:关闭socket的读端,即往socket写数据使正常的。
how = 1:关闭socket的写端,即往socket读数据使正常的。
how = 2:读写通道均关闭,同close()。

8、UDP发送数据

//from /usr/include/sys/socket.h
ssize_t sendto(int sockfd,void* buf,size_t len,int flags,struct sockaddr* addr,sockelen_t len );

函数第1个参数表示从本地哪个socket描述符发送数据
函数第2个参数表示欲发送的数据首地址
函数第3个参数表示欲发送的数据长度
函数第4个参数flags若为0,则以上函数同read/write函数,为阻塞方式,若为MSG_DONTWAIT,则为非阻塞方式
函数第5个参数表示目标主机的地址信息
函数第6个参数表示目标主机的地址信息占内存大小。
函数执行成功,返回发送的数据大小,失败返回-1。

9、UDP接收数据

ssize_t recvfrom(int sockfd,void* buf,size_t len,int flags,struct sockaddr* addr,sockelen_t* len );

函数第1个参数表示从本地哪个socket描述符接收数据
函数第2个参数表示接收的数据保存的位置
函数第3个参数表示欲接收的数据长度
函数第4个参数flags若为0,则以上函数同read/write函数,为阻塞方式,若为MSG_DONTWAIT,则为非阻塞方式
函数第5个参数表示源主机的地址信息
函数第6个参数表示源主机的地址信息占内存大小。
函数执行成功,返回接收的数据大小,失败返回-1

10、获取socket本地及对端信息

int getsockname(int socket,struct sockaddr* addr,socklen_t* len);
int getpeername(int socket,struct sockaddr* addr,socklen_t* len);

函数getsockname()获取套接字socket(已经至少完成绑定工作的)的地址信息,并保存在长度为len的addr地址中,成功返回0,失败返回-1。
函数getpeername()仅可以获取一个已经连接上服务器的套接字的远程信息,如IP地址、端口等,结果保存在长度为len的addr地址中,成功返回0,失败返回-1

以下是自己写的一个C/S架构的简单例子,主要练习以上函数的用法:
server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#define SERVERPORT  8888
#define SERVERADDR "192.168.103.128"
void* recvmsgs(void* pSockfd)
{
	char buf[1024];
	int* socket;
	if(NULL == pSockfd)
	{
		printf("recvmsg fail!\n");
		return;
	}
	
	socket= (int*)pSockfd;
	
	while(1)
	{
		memset(buf,0,sizeof(buf));
		recv(*socket,buf,1024,0);
		printf("recv a msg:%s\n",buf);
	
		sleep(1);
	}
	
	return;
}
int main()
{
	int opt = 1;
	int ret = -1;
	socklen_t len;
	pthread_t tid = -1;
	int newfd = -1;
	int sockfd = -1;
	char buffer[1024];
	struct sockaddr_in server,clienaddr;
	
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0)
	{
		perror("socket fail!\n");
		return -1;		
	}
	
	memset(&server,0,sizeof(server));
	
	server.sin_family = AF_INET;
	server.sin_port = htons(SERVERPORT);
	server.sin_addr.s_addr = INADDR_ANY;
	
	setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
	
	if(-1 == bind(sockfd,(struct sockaddr*)&server,sizeof(server)))
	{
		perror("connect fail!\n");
		return -1;
	}
	
	if(-1 == listen(sockfd,1))
	{
		perror("listen error!\n");
		return -1;
	}
	
	len = sizeof(struct sockaddr);

	while((newfd= accept(sockfd,(struct sockaddr*)&clienaddr,&len)) != -1)
	{
		printf("newfd:%d,clienaddr:sin_family=%d,sin_port=%d,s_addr=%s\n",newfd,clienaddr.sin_family,ntohs(clienaddr.sin_port),inet_ntoa(clienaddr.sin_addr));
		ret = pthread_create(&tid,NULL,recvmsgs,(void*)&newfd);
		if(-1 == ret)
		{
			perror("pthread_create fail\n");
			return -1;
		}
	}
	return 0;
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#define SERVERPORT  8888
#define SERVERADDR "192.168.103.128"
void* recvmsgs(void* pSockfd)
{
	char buf[1024];
	int* socket;
	if(NULL == pSockfd)
	{
		printf("recvmsg fail!\n");
		return;
	}
	
	socket= (int*)pSockfd;
	
	while(1)
	{
		memset(buf,0,sizeof(buf));
		recv(*socket,buf,1024,0);
		printf("recv a msg:%s\n",buf);
		sleep(1);
	}
	
	return;
}
int main()
{
	int ret = -1;
	pthread_t tid = -1;
	pthread_t tid2 = -1;
	int sockfd = -1;
	char buffer[1024];
	struct sockaddr_in server;
	
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0)
	{
		perror("socket fail!\n");
		return -1;		
	}
	
	memset(&server,0,sizeof(server));
	
	server.sin_family = AF_INET;
	server.sin_port = htons(SERVERPORT);
	server.sin_addr.s_addr = inet_addr(SERVERADDR);
	
	if(-1 == connect(sockfd,(struct sockaddr*)&server,sizeof(server)))
	{
		perror("connect fail!\n");
		return -1;
	}
	
	ret = pthread_create(&tid,NULL,recvmsgs,(void*)&sockfd);
	if(-1 == ret)
	{
		perror("pthread_create fail\n");
		return -1;
	}
	
	while(1)
	{
		memset(buffer,0,sizeof(buffer));
		gets(buffer);	
		send(sockfd,buffer,1024,0);
		sleep(1);
	}
	
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Chiang木

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

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

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

打赏作者

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

抵扣说明:

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

余额充值