2024年学了C语言想装x能干点啥?手把手教你写个聊天软件来玩玩_想,x,c,Java校招面试指南

Docker步步实践

目录文档:

①Docker简介

②基本概念

③安装Docker

④使用镜像:

⑤操作容器:

⑥访问仓库:

⑦数据管理:

⑧使用网络:

⑨高级网络配置:

⑩安全:

⑪底层实现:

⑫其他项目:

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

一、服务器

首先来看服务器端,先来搞定几个头文件,不然其中的一些库函数会没法调用:

#pragma once
#include<WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
#include<Windows.h>//必须在<WinSock2.h>的下面包含,否则编译不通过
#pragma comment(lib,"WS2\_32.lib")//要包含WinSock2.h必须要包这个库

头文件中的这些库那都是必须要包含的内容,不然之后函数的调用就会出现一堆的报错,下来我们看一下main函数:

	//初始化套接字类库 
	//WSAStartup函数用于初始化Ws2\_32.dll动态链接库。
	//在使用套接字函数之前,一定要初始化Ws2\_32.dll动态链接库 
	WSADATA WsaData = { 0 };
	if (WSAStartup(MAKEWORD(2, 2), &WsaData) != 0)
	{
		return;
	}

	// 创建监听套接字
	SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (ListenSocket == INVALID_SOCKET)
	{
		printf("Failed socket() \n");
		return;
	}

第一件事情就是初始化套接字类库,因为我们需要利用套接字来完成进程间通信,所以类库肯定是要首先初始化的,接下来是创建一个监听套接字,在创建监听套接字的时候需要注意,socket函数中传的参数是非常关键的:

SOCKET WSAAPI socket(
  _In_ int af,//地址家族规范,在这里我们传的是AF\_INET 这是IPv4协议规范
  _In_ int type,//这个参数我们传递SOCK\_STREAM,可靠的数据流传输,因为TCP协议
  _In_ int protocol//传输控制协议,用的TCP
);

这个函数的三个参数在TCP/IP通信中,基本是固定搭配套餐!当我们把监听套接字创建出来之后,需要将接听套接字与端口绑定:

	// 填充sockaddr\_in结构
	struct sockaddr\_in ServerAddress;
	ServerAddress.sin_family = AF_INET;//Ipv4协议家族
	ServerAddress.sin_port = htons(4567);   //端口号
	ServerAddress.sin_addr.S_un.S_addr = INADDR_ANY;//客户端是本地地址
	// 绑定套接字
	if (bind(ListenSocket, (LPSOCKADDR)&ServerAddress, sizeof(ServerAddress)) == SOCKET_ERROR)
	{
		printf("Failed bind() \n");
		return;
	}

上面的代码中,有一个结构体sockaddr_in其中包含了三个成员,有地址协议家族、监听端口号和监听的地址。其中端口号是随便设置的,只要在端口号范围之内,不要和知名端口号重复就行,我随便写了个4567,保证客户端也连接到这个端口就行!
bind函数是绑定套接字和sockaddr_in结构体,为了让这个套接字可以在该端口和地址协议规范下完成监听,bind函数将本地地址与套接字关联起来。

服务器端完成了套接字端口绑定之后,就要开始监听,listen函数将套接字置于侦听传入连接的状态。可以设置最大的连接数,在这里我随便设置了2。

	// 进入监听模式 监听队列 最大连接数设置为 2
	if (listen(ListenSocket, 2) == SOCKET_ERROR)
	{
		printf("Failed listen() \n");
		return;
	}

那监听上线之后,就等着客户端的连接过来,需要一个叫做accept的函数来接受客户端的连接,accept函数允许对套接字的传入连接尝试。在这里设计算是偷了个懒,本应该弄一个循环,因为这是尝试连接,如果连接达到上限,就不允许其它的客户端接入了,应该不断尝试连接。但是这里我们主要为了讲一下实现原理,用于间单的测试还是没问题的



	//用于接受客户端连接的IP地址等信息
	struct sockaddr\_in ClientAddress;
	int AddressLength = sizeof(ClientAddress);//计算这个长度在accept处使用
	
	SOCKET ClientSocket;
	printf("等待客户端连接:\n");
	// 接受一个新连接
	ClientSocket = accept(ListenSocket, (SOCKADDR\*)&ClientAddress, &AddressLength);
	if (ClientSocket == INVALID_SOCKET)
	{
		printf("Failed accept()");
	}

客户端和服务器连接成功之后,我们创建一个线程,在线程创建过程中,把客户端的Socket当作参数传递给线程,这个线程用于给客户端发送消息:

	printf("接收到连接:%s \r\n", inet\_ntoa(ClientAddress.sin_addr));

	HANDLE ThreadHandle = CreateThread(NULL,
		0,
		(LPTHREAD_START_ROUTINE)ThreadProcedure,
		&ClientSocket,
		0,
		NULL);
	if (ThreadHandle == NULL)
	{
		return 0;
	}

在这个回调线程执行函数中,用于和客户端通信,用gets来读取数据,遇到回车读取结束,然后只要保持连接,就可以一直给客户端发送消息,如果想断开连接,输入Over即可。

//相当于一个发送消息模块
DWORD WINAPI ThreadProcedure(LPVOID Parameter)
{
	SOCKET ClientSocket;
	char BufferData[260];//最大发送的字符数
	ClientSocket = \*(SOCKET\*)Parameter;
	printf("You can speak now:\n");
	while (1)
	{
		memset(BufferData, 0, sizeof(BufferData));
		gets(BufferData);
		// 向客户端发送数据
		send(ClientSocket, BufferData, strlen(BufferData), 0);
		if (!strncmp(BufferData, "Over", strlen("Over")))
		{
			// 关闭同客户端的连接 退出程序
			closesocket(ClientSocket);
			exit(0);
		}
	}
	return 0;
}

在异步线程可以发送消息的同时,主线程也没闲着,它在接收客户端的数据发送,也是在一个while循环中,一直接受者来自客户端的消息,直到客户端发出Over指示,断开连接:

	//用于接收数据
	char BufferData[260];
	while (TRUE)
	{
		memset(BufferData, 0, sizeof(BufferData));
		recv(ClientSocket, BufferData, sizeof(BufferData), 0);
		if (!strncmp(BufferData, "Over", strlen("Over")))
		{
			CloseHandle(ThreadHandle);
			ThreadHandle = NULL;
			break;
		}
		printf("Client Said: %s\n", BufferData);

	}

到这里,一个简单的服务器端就搞定了!!!接下来我们看一下客户端的实现吧:

二、客户端

客户端的代码实现逻辑其实和服务器端是相当接近的,我们需要包含的头文件也没有变化:

#pragma once
#include<WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
#include<Windows.h>//必须在<WinSock2.h>的下面包含,否则编译不通过
#pragma comment(lib,"WS2\_32.lib")//要包含WinSock2.h必须要包这个库

这些头文件都是必须包含的,在之前就已经说过了,因为实现逻辑很接近,所以我就找那些不太一样的地方来给大家解释一下:

一上来那肯定是main函数了,里面还是一样,初始化类库,创建套接字:

	//初始化套接字类库 
	//WSAStartup函数用于初始化Ws2\_32.dll动态链接库。在使用套接字函数之前,一定要初始化Ws2\_32.dll动态链接库 
	WSADATA v1 = { 0 };
	if (WSAStartup(MAKEWORD(2, 2), &v1) != 0)
	{
		return;
	}

	// 创建套接字
	SOCKET CommunicateSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (CommunicateSocket == INVALID_SOCKET)
	{
		printf(" Failed socket() \n");
		return;
	}

然后我们需要声明并且给sockaddr_in结构体赋值,这里有所不同,对于地址协议家族和端口号来说是一样的,尤其端口号,肯定要和服务器保持一致,然后我们讲连接的地址写为“127.0.0.1”,这是连接到本地的IP地址,在本机方便测试:

// 填写远程地址信息
	struct sockaddr\_in ServerAddress;


# 最后

各位读者,由于本篇幅度过长,为了避免影响阅读体验,下面我就大概概括了整理了

![](https://img-blog.csdnimg.cn/img_convert/25973070f410f2b316268f8274db689c.webp?x-oss-process=image/format,png)

![](https://img-blog.csdnimg.cn/img_convert/ddebdee24404905674f50388a5c1d9ff.webp?x-oss-process=image/format,png)

![](https://img-blog.csdnimg.cn/img_convert/4f30aad609b4544c3f0335161a95048a.webp?x-oss-process=image/format,png)

![](https://img-blog.csdnimg.cn/img_convert/15a8d83580c658f2b3e1ad326053c9e7.webp?x-oss-process=image/format,png)

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

863)]

[外链图片转存中...(img-6kytocXb-1715061655863)]

[外链图片转存中...(img-HN030jwN-1715061655864)]

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值