• socket实现简单聊天【linux】


    前言

    本篇blog学习于基于Linux平台使用socket实现 一个最简单的聊天系统,并详细演示了demo,同时对代码做了更加详细的注解,方便学习。

    前置知识

    socket()函数

    创建套接字

    在 Linux 下使用 <sys/socket.h> 头文件中 socket() 函数来创建套接字.
    
    原型为:int socket(int af, int type, int protocol);
    
    1.af:地址族(Address Family),也就是 IP 地址类型,常用为AF_INET 和 AF_INET6
    
    2.type:type为数据传输方式/套接字类型.
    常用的有SOCK_STREAM(流格式套接字/面向连接的套接字)SOCK_DGRAM(数据报套接字/无连接的套接字)
    
    3.protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    bind()函数

    绑定端口

    服务器端要用 bind() 函数将套接字与特定的 IP 地址和端口绑定起来
    
    int bind(int sock, struct sockaddr *addr, socklen_t addrlen);  //Linux
    
    1.sock 为 socket 文件描述符
    
    2.addr 为 sockaddr 结构体变量的指针
    
    3.addrlen 为 addr 变量的大小,可由 sizeof() 计算得出。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    sockaddr_in 结构体

    struct sockaddr_in{
        sa_family_t     sin_family;   //地址族(Address Family),也就是地址类型
        uint16_t        sin_port;     //16位的端口号
        struct in_addr  sin_addr;     //32位IP地址
        char            sin_zero[8];  //不使用,一般用0填充
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    sockaddr_in 结构体

    listen() 函数

    表示让一个套接字处于监听到来的连接请求的状态

    listen() 函数可以让套接字进入被动监听状态
    
    原型为int listen(int sock, int backlog);  //Linux
    
    sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    accept() 函数

    当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。
    
    原型为:int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);  //Linux
    
    accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    send()函数和recv()函数

    示例图参考recv( )函数返回值说明
    在这里插入图片描述

    int send( SOCKET s, const char FAR *buf,int len, int flags );
    
    参数说说明
    
    第一个参数指定发送端套接字描述符;
    
    第二个参数指明一个存放应用程序要发送数据的缓冲区;
    
    第三个参数指明实际要发送的数据的字节数;
    
    第四个参数一般置0===============================================================
    int recv( SOCKET s, char FAR *buf, int len, int flags);
    
    参数说明
    
    第一个参数指定接收端套接字描述符; 
    
    第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据; 
    
    第三个参数指明buf的长度;
    
    第四个参数一般置0
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    代码部分

    server.cpp

    #include 
    #include 
    #include 
     
    #include 
    #include 
    #include 
    #include 
    #include 
     
    #define PORT 6666
    #define BACKLOG 10
    #define MAX_SIZE	2048
     
    
     
    int main()
    	//定义服务器socket的描述符
    	int socket_fd;
    	//定义套接字的地址形式
    	struct sockaddr_in server_addr;
    
    	char buffer[MAX_SIZE]={0};
    	char message[MAX_SIZE];
    	//建立套接字
    	socket_fd = socket(AF_INET,SOCK_STREAM,0);
    	//bzero函数的作用是将s指针指向的地址的前n个字节清零
    	bzero(&server_addr,sizeof(server_addr));
     	//填写信息 
    	server_addr.sin_family = AF_INET;
    	server_addr.sin_port=htons(PORT);
    	server_addr.sin_addr.s_addr = INADDR_ANY;
     	//绑定端口 
    	bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(server_addr));
     	//开始监听 
    	listen(socket_fd,BACKLOG);
     
    	printf("Server bind success \n");
     	
    // 	定义客户端socket的描述符
    	struct sockaddr_in peer_addr;
    	socklen_t peer_len  = sizeof(peer_addr);
    	while(1)
    	{
    		int client_fd;
    		/*
    		*SOCKET accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
            *服务器调用accept函数从处于监听状态的流套接字sd的客户连接队列中
            *取出排在最前面的的一个客户连接请求,
            *并创建一个与sd同类的新套接字描述符与客户端套接字创建连接通道。
    		*/
    		client_fd = accept(socket_fd,(struct sockaddr* )&peer_addr,&peer_len);
    		printf("Receive a connect from clinet fd[%d,ip:%s,port:%d]\n",
    			client_fd,inet_ntoa(peer_addr.sin_addr),ntohs(peer_addr.sin_port));
    		while (1)
    		{
    			memset(buffer,0,sizeof(buffer));
    			int recv_len = recv(client_fd,buffer,MAX_SIZE,0);
    			if(recv_len == 0)
    			{
    				printf("Clinet is already closed\n");
    				break;
    			}
    			else
    			{
    				printf("Recv len is %d \n",recv_len);
    			}
    			printf("Clinet says:\n");
    			strcpy(message,buffer);
    			fputs(message,stdout);
    			memset(buffer,0,MAX_SIZE);
    			printf("Please input:\n");
    			fgets(buffer,sizeof(buffer),stdin);
     
    			send(client_fd,buffer,recv_len,0);
    		}
    		close(client_fd);
    		break;
    	}
    	close(socket_fd);
    	printf("Server exit\n");
    	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
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    client.cpp

    #include 
    #include 
    #include 
     
    #include 
    #include 
    #include 
    #include 
    #include 
     
     
    #define CLIETN_PORT	6666
    #define MAX_SIZE 2048
     
    int main(int argc,char *argv[])
    {
    	
    	if(argc != 2)
    	{
    		printf("Please input server ip\n");
    		exit(1);
    	}
     
    	char buffer[MAX_SIZE];
    	//建立 
    	int socket_fd = socket(AF_INET,SOCK_STREAM,0);
    	const char *server_ip = argv[1];
     
    	struct sockaddr_in server_addr;
     
    	bzero(&server_addr,sizeof(server_addr));
     
    	server_addr.sin_family = AF_INET;
    	server_addr.sin_port = htons(CLIETN_PORT);
    	//inet_pton是一个IP地址转换函数,可以在将点分文本的IP地址转换为二进制网络字节序”的IP地
    	inet_pton(AF_INET,server_ip,&server_addr.sin_addr);
    	connect(socket_fd,(struct sockaddr*)&server_addr,sizeof(server_addr));
     
    	printf("Connect to server success\n");
     
    	memset(buffer,0,MAX_SIZE);
     
     
    	printf("Please input:\n");
     
    	while (fgets(buffer,sizeof(buffer),stdin) != NULL)
    	{
    		if(strncmp(buffer,"quit",4) == 0)
    		{
    			printf("intpu  is: quit break\n");
    			break;
    		}
    		else
    		{
    			printf("intpu  is: %s\n",buffer);			
    		}
     
    		send(socket_fd,buffer,strlen(buffer),0);
    		memset(buffer,0,sizeof(buffer));
    		recv(socket_fd,buffer,sizeof(buffer),0);
    		printf("Server says:\n");
    		fputs(buffer,stdout);
    		memset(buffer,0,sizeof(buffer));
     
    		printf("Please input:\n");
     
    	}
     
    	printf("Client will be closed\n");
    	close(socket_fd);
    	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
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    Demo演示♥

    首先,我们先执行文件的编译,接着先./server,然后再执行./client注意:client后面要加上ip。具体如下图所示.
    在这里插入图片描述

    参考资料:

    http://c.biancheng.net/view/2344.html

  • 相关阅读:
    c#设计模式-行为型模式 之 迭代器模式
    掌动智能浅析Web自动化测试的重要性
    国际通用回收标准-GRS、RCS的答疑
    Java泛型
    万界星空科技云MES系统生产全流程追溯功能介绍
    在linux(centos7)部署xxl-job的过程
    Media Foundation播放器
    The 2022 ICPC Asia Regionals Online Contest (I)-D题题解(DFS暴搜+剪枝+打表去重+二分)
    SpringCloud微服务电商系统在Kubernetes集群中上线详细教程
    AT32F403A VGA(一)
  • 原文地址:https://blog.csdn.net/weixin_42888638/article/details/127423292