运用知识

套接字相关函数socket()、bind()、listen()、connect()、accept()、recv()、send()、select()、close()
https://blog.csdn.net/weixin_45525272/article/details/107732407
atoi
atoi (表示 ascii to integer)是把字符串转换成整型数的一个函数,应用在计算机程序和办公软件中。int atoi(const char *nptr)函数会扫描参数 nptr字符串,会跳过前面的空白字符(例如空格,tab缩进)等。如果 nptr不能转换成 int 或者 nptr为空字符串,那么将返回 0 。特别注意,该函数要求被转换的字符串是按十进制数理解的。atoi输入的字符串对应数字存在大小限制(与int类型大小有关),若其过大可能报错-1。
htons
htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处。
bzero()函数
原型:extern void bzero(void *s, int n);
参数说明:s 要置零的数据的起始地址;
n 要置零的数据字节个数。
用法:#include <string.h>
功能:置字节字符串s的前n个字节为零且包括‘\0’。
说明:bzero无返回值,并且使用string.h头文件,string.h曾经是posix标准的一部分,但是在POSIX.1-2001标准里面,这些函数被标记为了遗留函数而不推荐使用。在POSIX.1-2008标准里已经没有这些函数了。推荐使用memset替代bzero。

代码示例

Linux——多进程服务器与客户端并发通信_Linux

服务器

/*************************************************************************
    > File Name: server_fork.c
    > Author: 杨永利
    > Mail: 1795018360@qq.com 
    > Created Time: 2020年08月01日 星期六 10时02分04秒
 ************************************************************************/

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

int main(int argc, char* argv[]){
	if (argc!=2)
	{
		/* code */
		printf("参数错误!\n");
		return -1;
	}
	// 第一步 创建套接字描述符
	int server_socket=socket(PF_INET,SOCK_STREAM,0);
	//第二步 创建服务器地址结构
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[1]));
    //INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。 一般来说,在各个系统中均定义成为0值。
    server_addr.sin_addr.s_addr = /*htonl()*/INADDR_ANY;
    // 第三步 绑定套接字
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
    {
    	/* code */
    	printf("绑定套接字失败!\n");
    	return -1;
    }
    // 第四步 监听套接字
    if (listen(server_socket,10))
    {
    	/* code */
    	printf("监听套接字失败!\n");
    	return -1;
    }
    // 第五步 多进程连接
    struct sockaddr_in client_addr;
    int client_addr_size=sizeof(client_addr);
    while(1)
   	{
   		int client_socket=0;
   		bzero(&client_addr,sizeof(client_addr));
	    if((client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_size)) == -1)
	    {
	      	printf("接收客户端失败!\n-----client_socket=%d",client_socket);
	      	return -1;
	    }
	    // 创建子进程 fork()
	    /*
			负值:创建子进程失败。
			零:返回到新创建的子进程。
			正值:返回父进程或调用者。该值包含新创建的子进程的进程ID
	    */
	    int pid=fork();
	    // 创建新的子进程成功
	    if (pid==0)
	    {
	    	// 创建临时客户端地址结构体用于存放开始accept到的客户端数据
	    	struct sockaddr_in client_in_addr;
	    	client_in_addr.sin_addr.s_addr=client_addr.sin_addr.s_addr;
	    	client_in_addr.sin_port=client_addr.sin_port;
	    	printf("来了一个新的客户端,ip是:%s,端口:%d\n", inet_ntoa(client_addr.sin_addr),client_in_addr.sin_port);
	    	// 关闭当前进程的套接字描述符,
	    	close(server_socket);
            char buf[1024];
            int recv_len;
            while(1)
            {
            	bzero(buf, sizeof(buf));
                if((recv_len = recv(client_socket, buf, sizeof(buf), 0)) < 0)
                    return -1;
                // 判断如果接收到为0就有个客户端退出,跳出循环
                if(!recv_len){
                    printf("有客户端退出了,ip:%s 端口:%d\n", inet_ntoa(client_in_addr.sin_addr),client_in_addr.sin_port);//struct in_addr
                	break;
                }
                printf("客户端-%s -%d 对我说:%s\n",inet_ntoa(client_in_addr.sin_addr),client_in_addr.sin_port,buf);
            	if(send(client_socket, buf, strlen(buf), 0) <= 0)
                    return -1;
            }
            close(client_socket);
            return 0;
	    }
	    // 返回给父进程子进程id  父进程就可以关闭接收的套接字描述符
        else if(pid > 0)
        {
            close(client_socket);
        }
        else
        //创建子进程失败,直接退出
           exit(-1);
   	}

    return 0;
}

客户端

/*************************************************************************
    > File Name: client.c
    > Author: 杨永利
    > Mail: 1795018360@qq.com 
    > Created Time: 2020年08月01日 星期六 10时01分47秒
 ************************************************************************/

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

int main(int argc, char* argv[]){
	if (argc != 3)
	{
		printf("参数错误!\n");
		return -1;
	}
	// 第一步创建网络通信套接字描述符
	int client_socket=socket(AF_INET,SOCK_STREAM,0);
	// 第二步创建服务器的地址结构体
	struct sockaddr_in server_addr;
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family=AF_INET;
	server_addr.sin_port=htons(atoi(argv[2]));
	server_addr.sin_addr.s_addr=inet_addr(argv[1]);
	// 第三步 连接服务器
	if (connect(client_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1)
	{
		/* code */
		printf("连接服务器失败!\n");
		return -1;
	}
	printf("连接服务器成功!\n");
	char message[1024];
	bzero(message,sizeof(message));
	char buf[1024];
	printf("我是客户端,我将给服务器发送内容\n");
	printf("如果希望聊天退出,请输入q或者Q:\n");
	while(1)
	{
		printf("我发送给服务器的:\n");
		scanf("%s",buf);
		if (strlen(buf)==1)
		{
			if(buf[0] == 'q' || buf[0] == 'Q')
                break;
		}
		if(strlen(buf) != write(client_socket, buf, strlen(buf))){
            printf("write() error!\n");
            exit(-1);
        }
        if(read(client_socket, message, sizeof(message)) <= 0){
            printf("read() error!\n");
            return -1;
        }
        printf("服务器对我说:%s\n", message);
        bzero(buf, sizeof(buf));
        bzero(message, sizeof(message));
	}
	close(client_socket);
	printf("服务器关闭了!\n");
    return 0;
}