linux网络编程中协议簇AF_和PF_的区别
在kernel/include/linux/socket.h文件中定义
AF_XXX:即Supported address families.,目前最大46
PF_XXX:即Protocol families, same as address families
int socket(int domain, int type, int protocol);
支持的socket类型
在参数表中,domain指定使用何种的地址类型, 指定通信协议族(protocol family/address),比较常用的有:
PF_INET, AF_INET: Ipv4网络协议;
PF_INET6, AF_INET6: Ipv6网络协议。
type参数的作用是设置通信的协议类型,可能的取值如下所示:
SOCK_STREAM: 提供面向连接的稳定数据传输,即TCP协议。
SOCK_DGRAM: 使用不连续不可靠的数据包连接。
SOCK_SEQPACKET: 提供连续可靠的数据包连接。
SOCK_RAW: 提供原始网络协议存取。
SOCK_RDM: 提供可靠的数据包连接。
SOCK_DCCP: 数据报拥塞控制协议(Datagram Congestion Control Protocol)
SOCK_PACKET: 与网络驱动程序直接通信。
参数protocol用来指定socket所使用的传输协议编号。这一参数通常不具体设置,一般设置为0即可。
protocol定义在kernel/include/uapi/linux/in.h
/* Standard well-defined IP protocols. */
enum {
IPPROTO_IP = 0, /* Dummy protocol for TCP */
#define IPPROTO_IP IPPROTO_IP
IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
#define IPPROTO_ICMP IPPROTO_ICMP
IPPROTO_IGMP = 2, /* Internet Group Management Protocol */
#define IPPROTO_IGMP IPPROTO_IGMP
IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */
#define IPPROTO_IPIP IPPROTO_IPIP
IPPROTO_TCP = 6, /* Transmission Control Protocol */
#define IPPROTO_TCP IPPROTO_TCP
IPPROTO_EGP = 8, /* Exterior Gateway Protocol */
#define IPPROTO_EGP IPPROTO_EGP
IPPROTO_PUP = 12, /* PUP protocol */
#define IPPROTO_PUP IPPROTO_PUP
IPPROTO_UDP = 17, /* User Datagram Protocol */
#define IPPROTO_UDP IPPROTO_UDP
IPPROTO_IDP = 22, /* XNS IDP protocol */
#define IPPROTO_IDP IPPROTO_IDP
IPPROTO_TP = 29, /* SO Transport Protocol Class 4 */
#define IPPROTO_TP IPPROTO_TP
IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */
#define IPPROTO_DCCP IPPROTO_DCCP
IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */
#define IPPROTO_IPV6 IPPROTO_IPV6
IPPROTO_RSVP = 46, /* RSVP Protocol */
#define IPPROTO_RSVP IPPROTO_RSVP
IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */
#define IPPROTO_GRE IPPROTO_GRE
IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */
#define IPPROTO_ESP IPPROTO_ESP
IPPROTO_AH = 51, /* Authentication Header protocol */
#define IPPROTO_AH IPPROTO_AH
IPPROTO_MTP = 92, /* Multicast Transport Protocol */
#define IPPROTO_MTP IPPROTO_MTP
IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */
#define IPPROTO_BEETPH IPPROTO_BEETPH
IPPROTO_ENCAP = 98, /* Encapsulation Header */
#define IPPROTO_ENCAP IPPROTO_ENCAP
IPPROTO_PIM = 103, /* Protocol Independent Multicast */
#define IPPROTO_PIM IPPROTO_PIM
IPPROTO_COMP = 108, /* Compression Header Protocol */
#define IPPROTO_COMP IPPROTO_COMP
IPPROTO_SCTP = 132, /* Stream Control Transport Protocol */
#define IPPROTO_SCTP IPPROTO_SCTP
IPPROTO_UDPLITE = 136, /* UDP-Lite (RFC 3828) */
#define IPPROTO_UDPLITE IPPROTO_UDPLITE
IPPROTO_MPLS = 137, /* MPLS in IP (RFC 4023) */
#define IPPROTO_MPLS IPPROTO_MPLS
IPPROTO_ETHERNET = 143, /* Ethernet-within-IPv6 Encapsulation */
#define IPPROTO_ETHERNET IPPROTO_ETHERNET
IPPROTO_RAW = 255, /* Raw IP packets */
#define IPPROTO_RAW IPPROTO_RAW
IPPROTO_MPTCP = 262, /* Multipath TCP connection */
#define IPPROTO_MPTCP IPPROTO_MPTCP
IPPROTO_MAX
};
Linux下Socket编程
Linux环境服务端程序socket_server.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <time.h>
#define SERVER_IP "192.168.211.128" //Linux虚拟机做服务器
#define SERVER_PORT 8899
#define MAX_BUF_SIZE 1500
pthread_mutex_t mutex; //互斥锁
char recv_buf[MAX_BUF_SIZE] = {0};
char send_buf[MAX_BUF_SIZE] = {0};
int *read_write_thread(void *arg)
{
int client_sockfd = *(int *)arg;
pthread_t thread_id = pthread_self();
printf("client_sockfd %d pthread_self: %ld\n", client_sockfd, thread_id);
while(1)
{
//5.读取客户端发送来的数据
pthread_mutex_lock(&mutex); //上锁
memset(recv_buf, 0x00, sizeof(recv_buf));
memset(send_buf, 0x00, sizeof(send_buf));
pthread_mutex_unlock(&mutex); //解锁
int len = read(client_sockfd, recv_buf, sizeof(recv_buf)-1);
if(len > 0)
{
recv_buf[len] = '\0';//字符串以“\0”结尾
//6.打印出客户端发来的消息
printf("[客户端消息]: %s >> msgLen: %d\n",recv_buf, len);
//7.加上hello处理后返回给客户端
strcpy(send_buf, recv_buf);
printf("[服务端应答]: %s\n", send_buf);
write(client_sockfd, send_buf, strlen(send_buf));
}
else if(len == 0)
{
printf("client socket close: %s client_sockfd:%d thread_id:%ld\n", strerror(errno), client_sockfd, thread_id);
break;
}
else
{
if(errno == EAGAIN || errno == EWOULDBLOCK)
{
printf("client socket read finish: %s\n", strerror(errno));
}
break;
}
}
//8.关闭客户端连接
close(client_sockfd);
pthread_exit(0);
return NULL;
}
int main(int agrc, char *argv[])
{
pthread_mutex_init(&mutex, NULL); //初始化互斥锁
//1.创建一个socket文件,也就是打开一个网络通讯端口,类型是IPV4(AF_INET)+TCP(SOCK_STREAM)
int sockfd = socket(AF_INET, SOCK_STREAM,0);
if(sockfd < 0)
{
printf("socket create failed: %s\n", strerror(errno));
exit(-1);
}
//2.绑定服务器ip和端口到这个socket
struct sockaddr_in srv_addr;//这里因为是ipv4,使用的结构体是ipv4的地址类型sockaddr_in
bzero(&srv_addr, sizeof(srv_addr));//先清空一下初始的值,写上地址和端口号,可以用bzero(&srv_addr, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
srv_addr.sin_port = htons(SERVER_PORT); //随意选了一个端口8899
int status = bind(sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
if(status < 0)
{
printf("socket bind failed: %s\n", strerror(errno));
exit(-1);
}
//3.将socket设置为监听状态
status = listen(sockfd, 128);//设置最大连接数为128
if(status < 0)
{
printf("socket listen failed: %s\n", strerror(errno));
exit(-1);
}
while(1)
{
//4.准备接收客户端的请求连接,这里的步骤可以重复进行,接收多个客户端的请求
//接收客户端的请求连接后,返回一个新的socket(clt_sock)用于和对应的客户端进行通信
struct sockaddr_in clt_addr;//作为一个传出参数
socklen_t clt_addr_size = sizeof(clt_addr); //作为一个传入+传出参数
int clt_sockfd = accept(sockfd, (struct sockaddr*)&clt_addr, &clt_addr_size);
if(clt_sockfd < 0)
{
printf("client accept failed: %s\n", strerror(errno));
if(errno == EINTR || errno == ECONNABORTED) continue;
break;
}
pthread_t thread_id;
if(pthread_create(&thread_id, NULL,(void *)read_write_thread, &clt_sockfd) != 0)
{
perror("pthread_create failed");
continue;
}
printf("read_write_thread's thread_id %ld\n", thread_id);
}
//9.关闭服务端监听的socket
close(sockfd);
pthread_mutex_destroy(&mutex); //销毁互斥锁
return 0;
}
Linux环境客服端程序socket_client.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define SERVER_IP "192.168.211.128"
#define SERVER_PORT 8899
#define MAX_BUF_SIZE 1500
int main(int argc, char *argv[])
{
//1.创建socket,用于和服务端通信
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) exit(-1);
//2.向服务端发起请求连接
struct sockaddr_in serv_addr;//首先要指定一个服务端的ip地址+端口,表明是向哪个服务端发起请求
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//注意,这里是服务端的ip和端口
serv_addr.sin_port = htons(SERVER_PORT);
int ret = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
while(1)
{
//3.向服务端发送消息
char send_buf[MAX_BUF_SIZE] = "Hello Windows!!!";
char recv_buf[MAX_BUF_SIZE] = {0};
write(sockfd, send_buf,strlen(send_buf));
//4.接收服务端发来的消息
int len = read(sockfd, recv_buf, sizeof(recv_buf)-1);
if(len == 0)
{
close(sockfd);
exit(-1);
}
recv_buf[len] = '\0';
printf("收到服务端的返回:%s:%d\n", recv_buf, len);
sleep(5);
}
//5.关闭socket
close(sockfd);
return 0;
}
编译:
gcc socket_server.c -o socket_server -lpthread
gcc socket_client.c -o socket_client
Windows下Socket编程:
Windows环境服务端程序socket_server.c
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#define SERVER_PORT 8899
int main(int argc, char *argv[])
{
WSADATA wsaData = {0};
//第一步:定义我们需要的winsock的版本,这里是2.2版本(目前最高的版本号)
int status = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (0 != status) //返回非0 ,代表出错
{
printf("failed with error:%d", status);
system("pause");
return 1;
}
//第二部:查看当前系统支持的版本,如果不支持我们上面定义的版本就用不了
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("system not support for this version");
WSACleanup(); //释放系统资源
system("pause");
return 1;
}
else
{
printf("The WinSock 2.2 dll was found");
}
//第三步:开始创建套接字
SOCKET server_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == server_sockfd) {
printf("create socket failed with error:%d",WSAGetLastError());
WSACleanup();
system("pause");
return 1;
}
//第四步:准备结构体,绑定socket
SOCKADDR_IN addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT); //将本地字节序转换成网络字节序
addr.sin_addr.S_un.S_addr = INADDR_ANY;
//如果需要指定IP,可以这样
//addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (SOCKET_ERROR == bind(server_sockfd, (SOCKADDR*) &addr, sizeof(addr))) //成功返回0
{
printf("bind socket failed with error:%d",WSAGetLastError());
closesocket(server_sockfd);
WSACleanup();
system("pause");
return 1;
}
//第五步:监听
if (SOCKET_ERROR == listen(server_sockfd, SOMAXCONN)) //成功返回0
{
printf("listen socket failed with error:%d", WSAGetLastError());
closesocket(server_sockfd);
WSACleanup();
system("pause");
return 1;
}
//第六步,等待连接
SOCKADDR_IN client_addr = {0};
ZeroMemory(&client_addr, sizeof(client_addr));
int len = sizeof(client_addr);
printf("all is okay,waitting for client...");
while(1) //因为可以接收很多的客户,这里使用无限循环
{
// 这一步将会阻塞,直到有客户端连接进来(接客)
SOCKET client_sockfd = accept(server_sockfd,(SOCKADDR*)&client_addr, &len);
//这一步可以优化,用线程来做,主线程只负责接客,子线程来服务客人(有效的连接)。
if(INVALID_SOCKET == client_sockfd)
{
printf("invalide socket,error:%d",WSAGetLastError());
closesocket(server_sockfd);
WSACleanup();
system("pause");
return 1;
}
//客户信息有效,打印下看看吧
printf(">>client$ %s:%d connected!", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
//第七步:开始通讯,这里写一个回声服务器(将收到的数据,原数发回给客户端)
char buff[MAXBYTE] = {0}; //存储客户端发来的信息
int bufflen = 0;
do//客户端可能会发送多次数据,这里暂使用无限循环
{
//每次接收数据前,需要将上一次接收的缓冲区数据清空
ZeroMemory(buff, sizeof(buff));//该函数底层调用的memset函数
bufflen = recv(client_sockfd, buff, sizeof(buff), 0);
if(bufflen == 0)//recv函数可以接受0个参数,代表对方关闭连接了
{
printf("connection closed!");
closesocket(client_sockfd);
}
else if (SOCKET_ERROR == bufflen)
{
printf("recv failed with error:%d", WSAGetLastError());
}
else //接收到了数据
{
buff[bufflen] = '\0';
printf("received:%s",buff);
//send函数可以发送大于等于0的数据
int len = send(client_sockfd,buff,bufflen,0);
if(SOCKET_ERROR == len)
{
printf("send failed with error:%d",WSAGetLastError());
break;
}
else
{
printf("send successfully!recv:%d Bytes,send:%d Bytes",bufflen,len);
}
}
}while(bufflen > 0);
closesocket(client_sockfd);
}
closesocket(server_sockfd);
WSACleanup();
getchar();
return 0;
}
Windows环境客服端程序socket_client.c
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#define SERVER_PORT 8899
#define SERVERADDR "192.168.211.128"
int main(int argc, char *argv[])
{
WSADATA wsaData = {0};
int status = WSAStartup(MAKEWORD(2,2),&wsaData);
if(0 != status)
{
printf("failed with error:%d\n", status);
system("pause");
return 1;
}
//创建套接字
SOCKET client_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET == client_sockfd)
{
printf("create socket failed with error:%d\n",WSAGetLastError());
WSACleanup();
system("pause");
return 1;
}
//准备服务器的信息
SOCKADDR_IN server_addr = {0};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT); //将本地字节序转换成网络字节序
server_addr.sin_addr.S_un.S_addr = inet_addr(SERVERADDR);
//开始连接服务器
if(SOCKET_ERROR == connect(client_sockfd,(SOCKADDR*)&server_addr,sizeof(server_addr)))
{
printf("connect to server with error:%d\n", WSAGetLastError());
closesocket(client_sockfd);
WSACleanup();
system("pause");
return 1;
}
char send_buff[MAXBYTE] = {0};
char recv_buff[MAXBYTE] = {0};
while(1)
{
memset(send_buff, 0, sizeof(send_buff));
printf("请输入需要发送的内容:");
rewind(stdin);
if(gets(send_buff) == NULL) break;
printf("client send_buff:%s:%d\n", send_buff, strlen(send_buff));
int len = send(client_sockfd,send_buff,strlen(send_buff),0);
if(SOCKET_ERROR == len)
{
printf("send failed with error:%d\n",WSAGetLastError());
break;
}
//准备接收服务器的回声
memset(recv_buff, 0, sizeof(recv_buff));
int recv_len = recv(client_sockfd, recv_buff, sizeof(recv_buff), 0);
if(recv_len == 0) //对方关闭连接了
{
printf("service closed!\n");
break;
}
else if (SOCKET_ERROR == recv_len)
{
printf("recv failed with error:%d\n", WSAGetLastError());
break;
}
else
{
printf("received from server:%s\n", recv_buff);
}
}
closesocket(client_sockfd);
WSACleanup();
system("pause");
return 0;
}
编译命令:
gcc -o socket_server.exe socket_server.o -lws2_32
gcc -o socket_client.exe socket_client.o -lws2_32
测试Socket:步骤如下
1,启动Linux环境上服务器监听:
socket$ ./socket_server
2,启动Windows环境下的客服端:
Windows:
Debug>socket_client.exe
请输入需要发送的内容:Hello Linux Server
client send_buff:Hello Linux Server:18
received from server:Hello Linux Server
请输入需要发送的内容:Linux:
read_write_thread's thread_id 140005403870976
client_sockfd 4 pthread_self: 140005403870976
[客户端消息]: Hello Linux Server >> msgLen: 18
[服务端应答]: Hello Linux Server3,启动Windows环境下Socket测试软件:
Windows:
Linux:
4,启动Linux环境上的客户端程序
Linux客户端程序打印:
Linux服务端程序打印: