当前位置: 首页 > 最新文章 > 正文

学了C语言想装x能干点啥?

对于搞计算机的人来说,我们一直处在一个被外行深深误解的环境下,我还记得上学时候就发生过这样的对话,这位误会我的人还是我的好兄弟:韩:“白哥,今天用空吗?我电脑坏了,帮我看一下!”告诉他们我们的专业性!bind函数是绑定套接字和sockaddr_in结构体,为了让这个套接字可以在该端口和地址协议规范下完成监听,bind函数将本地地址与套接字关联起来。服务器端完成了套接字端口绑定之后,就要开始监听,l

admin

对于搞计算机的人来说,我们一直处在一个被外行深深误解的环境下,我还记得上学时候就发生过这样的对话,这位误会我的人还是我的好兄弟:韩:“白哥,今天用空吗?我电脑坏了,帮我看一下!”告诉他们我们的专业性!bind函数是绑定套接字和sockaddr_in结构体,为了让这个套接字可以在该端口和地址协议规范下完成监听,bind函数将本地地址与套接字关联起来。服务器端完成了套接字端口绑定之后,就要开始监听,listen函数将套接字置于侦听传入连接的状态。可以设置最大的连接数,在这里我随便设置了2。在这里设计算是偷了个懒,本应该弄一个循环,因为这是尝试连接,如果连接达到上限,就不允许其它的客户端接入了,应该不断尝试连接。

对于搞计算机的人来说,我们一直处在一个被外行深深误解的环境下,我还记得上学时候就发生过这样的对话,这位误会我的人还是我的好兄弟:

韩:“白哥,今天用空吗?我电脑坏了,帮我看一下!”
我:“我不太会修电脑啊!你重启试试”
韩:“啊?你不是计算机专业吗?”
我:“计算机科学与技术也不是计算机修理专业啊…”
有的计算机专业的大佬可能真的会修计算机,也有的不是计算机的大佬,也会修计算机,反正,我不太会修电脑,我只会…重启试试???

为了不让往日悲剧在各位身上重现,各位还是写个程序给周围人看一看!告诉他们我们的专业性!那学习了这么久的C语言基础,不得用C语言来干点啥?写个简易的聊天软件,出去在同学面前装一下呗!众所周知,写代码如果不是为了在别人面前装个x,那将变得毫无意义!如果觉得文章不错,麻烦给个一键三连支持一下,您的支持,是我最大的创作动力!

废话够多了,赶紧进入正题!

我们要实现一个简易的聊天系统,那最起码是两个人交互的,那么肯定是需要两个不同的端,也就是最起码两个程序,来实现这个简单的功能,我们就暂且理解为需要一个服务器端,一个客户端,这样让两个程序完成交互!该代码在Windows系统进行测试,如果需要在linux端实现,需要稍作改动!

一、服务器

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

#pragma once#include<WinSock2.h>#include <stdio.h>#include <stdlib.h>#include<Windows.h>#pragma comment(lib,"WS2_32.lib")123456

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

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;}12345678910111213141516

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

SOCKET WSAAPI socket(  _In_ int af,  _In_ int type,  _In_ int protocol);12345

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

struct sockaddr_in ServerAddress;ServerAddress.sin_family = AF_INET;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;}1234567891011

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

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

if (listen(ListenSocket, 2) == SOCKET_ERROR){printf("Failed listen() \n");return;}123456

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

struct sockaddr_in ClientAddress;int AddressLength = sizeof(ClientAddress);SOCKET ClientSocket;printf("等待客户端连接:\n");ClientSocket = accept(ListenSocket, (SOCKADDR*)&ClientAddress, &AddressLength);if (ClientSocket == INVALID_SOCKET){printf("Failed accept()");}1234567891011121314

客户端和服务器连接成功之后,我们创建一个线程,在线程创建过程中,把客户端的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;}123456789101112

在这个回调线程执行函数中,用于和客户端通信,用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;}12345678910111213141516171819202122

在异步线程可以发送消息的同时,主线程也没闲着,它在接收客户端的数据发送,也是在一个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);}123456789101112131415

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

二、客户端

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

#pragma once#include<WinSock2.h>#include <stdio.h>#include <stdlib.h>#include<Windows.h>#pragma comment(lib,"WS2_32.lib")123456

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

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

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;}123456789101112131415

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

struct sockaddr_in ServerAddress;ServerAddress.sin_family = AF_INET;ServerAddress.sin_port = htons(4567);ServerAddress.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); 123456

初始化结束之后,我们使用connect将客户端的套接字通过这个IP和端口来和服务器进行连接,connect函数建立到指定套接字的连接:

if (connect(CommunicateSocket, (SOCKADDR*)&ServerAddress, sizeof(ServerAddress)) == SOCKET_ERROR){printf(" Failed connect() \n");return;}if (CommunicateSocket == INVALID_SOCKET){printf("Failed accept()");}123456789

一旦连接成功,继续创建线程,这个线程传的是当前与服务器连接起来的CommunicateSocket,这个套接字就是客户端和服务器交流的桥梁:

printf("连接成功!!\r\n");HANDLE ThreadHandle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProcedure,&CommunicateSocket,0,NULL);if (ThreadHandle == NULL){return 0;}123456789101112

创建异步线程还是一样的,将当前传入的套接字,用于给服务器发送消息,发送Over来结束当前会话:

DWORD WINAPI ThreadProcedure(LPVOID Parameter){SOCKET ServerSocket;char BufferData[260];ServerSocket = *(SOCKET*)Parameter;printf("You can speak now:\n");while (1){memset(BufferData, 0, sizeof(BufferData));gets(BufferData);send(ServerSocket, BufferData, strlen(BufferData), 0);if (!strncmp(BufferData, "Over", strlen("Over"))){closesocket(ServerSocket);exit(0);}}return 0;}12345678910111213141516171819202122

那main函数中的主线程肯定还是接受来自服务器端的消息,除非遇到Over指令,来结束对话:

char BufferData[260];while (TRUE){memset(BufferData, 0, sizeof(BufferData));recv(CommunicateSocket, BufferData, sizeof(BufferData), 0);if (!strncmp(BufferData, "Over", strlen("Over"))){CloseHandle(ThreadHandle);ThreadHandle = NULL;break;}printf("Server Said: %s\n", BufferData);}123456789101112131415

到这里,实现就基本结束了,记得代码中断开连接之后,最后关闭套接字。代码记得首先启动服务器,这样才能达到监听的效果,让客户端顺利连接,我们来用动态图演示一下效果:

学了C语言想装x能干点啥?

如果觉得文章不错,麻烦给个点赞+评论+收藏支持一下。

最后我把自己整理的工作期间的电子书籍都分享给大家,可以在这里获取,对于学习计算机的同学帮助非常大,且十分系统:

链接:计算机经典书籍

学了C语言想装x能干点啥?


上一篇: stormmedia文件夹webstorm删除文件(webstorm删除文件) 下一篇:马哥的故事(马哥的故事)
返回顶部