TCP/IP网络编程之--socket编程

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 Server 

3,启动Windows环境下Socket测试软件:

Windows:

Linux:

4,启动Linux环境上的客户端程序

Linux客户端程序打印:

 Linux服务端程序打印:

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值