windows下socket编程实现客户端和服务端互发消息_采用winsocket消息发送-程序员宅基地

技术标签: c++  网络  socket  计算机网络  

WinSock

在windows系统下有一个api专门提供socket编程,这个api即为WinSock,其中特别需要注意的是先需要调用WSAStartup函数初始化Windows Sockets API,结束后需要调用WSACleanup函数释放掉Windows Sockets DLL

socket编程

关于socket在客户端、服务端之间的通信可如图所示:
在这里插入图片描述
其中socket()函数是创建套接字函数,其函数如下:socket(int af,int type,int protocol) 其中af代表地址族,其中AF_INET代表TCP协议,type代表套接字的类型,套接字一般分为三种类型,即流式套接字数据报套接字原始套接字,protocol代表协议。
bind()是绑定套接字函数,其函数原型如下:bind(SOCKET s,const sockaddr*addr,int namelen),其中s代表套接字,addr代表一个数据结构sockaddr_in,其结构如下:
struct sockaddr_in{
u_char sin_len;//地址长度
u_char sin_family;//地址族(tcp/ip:AF_INET)
u_short sin_port;//端口号
struct in_addr sin_addr;//ip地址
char sin_zero[8];//未用,置零
}

一般而言只需服务端绑定即可。对于服务端绑定来说,由于服务端可以有多个网卡,可能有多个ip地址,其形式如图所示:
在这里插入图片描述
对于此可以采用地址通配符ANADDR_ANY,这样无论通过哪个地址都可以接受请求。
listen()函数是监听函数,其函数原型如下:listen(SOCKET S,int backlog) 其中s代表套接字,backlog代表监听队列的长度,一般而言监听队列的最大长度为20
accpet()函数是接受函数,当监听到连接请求之后,accept函数会返回一个新的套接字与客户端建立连接,而原来的套接字则继续等待连接请求。其函数原型如下:accept(SOCKET s,sockaddr*addr,int addrlen)
connect()函数是连接函数,是客户端与服务端的连接函数,其函数原型如下:connect(SOCKET S,const sockaddr*name,int namelen),其中name是指定的服务端的地址信息。
send()函数是发送数据函数,其函数原型如下:send(SOCKET*s,const char * buf,int len,int flags),其中flags未用,默认为0。
recv函数是接收数据函数,其函数原型如下:recv(SOCKET s,char*buf,int len,int flags)
closesocket()函数是关闭套接字函数,其函数原型如下:closesocket(SOCKET S)
其通信流程如下:首先服务端先创建套接字,然后绑定套接字,然后调用监听函数,其监听函数会把客户端的连接请求形成一个队列,然后调用accept()函数接受请求,创建一个新的套接字,让这个套接字与客户端套接字建立连接。之后调用recv()函数和send()函数实现客户端和服务端的数据接收和发送。在完成后,会调用closesocket()函数关闭套接字,这时服务端会返回到accept()函数中去继续接受请求。

代码

代码如下:
服务端:

#include<stdio.h>
#include<iostream>
#include<string>
#include<windows.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

int main(int argc, char* argv[]) {
    
	//初始化版本号
	WORD socketVersion = MAKEWORD(2, 2);
	WSADATA wsdata;
	if (WSAStartup(socketVersion, &wsdata) != 0) {
    
		return 1;
	}
	//创建套接字
	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (serverSocket == INVALID_SOCKET) {
    
		cout << "无效的套接字" << endl;
		return 1;
	}
	//绑定套接字
	sockaddr_in addr_in;
	addr_in.sin_family = AF_INET;
	addr_in.sin_port = htons(8888);
	addr_in.sin_addr.S_un.S_addr = INADDR_ANY;//地址通配符
	if (bind(serverSocket, (sockaddr*)&addr_in, sizeof(addr_in)) == SOCKET_ERROR) {
    
		cout << "绑定失败" << endl;
		return 1;
	}
	//监听套接字
	if (listen(serverSocket, 10) == SOCKET_ERROR) {
    
		cout << "监听失败" << endl;
		return 1;
	}
	SOCKET connection_socket;//调用accept()函数时产生的新的套接字
	sockaddr_in connection_addr_in;//新的套接字对应的地址
	char data[1000];//存储要转发的数据
	int flag = 0;//标志是否被连接
	int len = sizeof(connection_addr_in);
	while (true) {
    
		if (!flag) {
    
			cout << "正在连接中..." << endl;
		}
		connection_socket = accept(serverSocket, (sockaddr*)&connection_addr_in, &len);
		if (connection_socket == INVALID_SOCKET) {
    
			flag = 0;
			cout << "接受失败" << endl;
			return 1;
		}
		if (!flag) {
    
			cout << "收到一个连接:" << inet_ntoa(connection_addr_in.sin_addr) << endl;
			flag = 1;
		}
		int num = recv(connection_socket, data,100,0);
		if (num > 0) {
    
			data[num] = '\0';
			cout << "服务端收到:" << data << endl;
		}
		string data;
		getline(cin, data);
		const char* senddata;
		senddata = data.c_str();
		send(connection_socket, senddata, strlen(senddata), 0);
		closesocket(connection_socket);
	}
	closesocket(serverSocket);
	WSACleanup();//关闭
	return 0;

}

客户端代码:

#include<iostream>
#include<winsock2.h>
#include<string>
#include<Ws2tcpip.h>
#include<windows.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

int main() {
    
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if (WSAStartup(sockVersion, &data) != 0) {
    
		return 1;
	}
	while (true) {
    
		SOCKET clientsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//地址族,流式套接字,tcp协议
		if (clientsocket == INVALID_SOCKET) {
    
			cout << "Socket error" << endl;
			return 1;
		}
		sockaddr_in sock_in;//结构体,内包含地址族,端口,ip地址
		sock_in.sin_family = AF_INET;
		sock_in.sin_port = htons(8888);//htons函数是实现将端口号的顺序弄成与本地的一样,由于有大小端之分
		inet_pton(AF_INET, "192.168.200.113", &sock_in.sin_addr.S_un.S_addr);//将ip地址转化成二进制模式传给sock_in中的ip地址,传输的时候是以二进制传输的
		if (connect(clientsocket, (sockaddr*)&sock_in, sizeof(sock_in)) == SOCKET_ERROR) {
    
			cout << "连接失败!" << endl;
			return 1;
		}
		string data;
		const char *senddata;
		getline(cin, data);
		senddata = data.c_str();
		send(clientsocket, senddata, strlen(senddata), 0);//将数据发送给套接字
		char revdata[1000];
		int num = 0;
		num = recv(clientsocket, revdata, strlen(revdata), 0);//接收发过来的信息
		if (num > 0) {
    
			revdata[num] = '\0';
			cout << "客户端收到:" <<revdata << endl;
		}
		closesocket(clientsocket);//将套接字关闭
	}
	WSACleanup();//关闭
}

其中关于htons()函数主要是将本地字节顺序转换成网络字节顺序(两字节)由于机器的字节顺序有大端顺序和小端顺序,相应的也有ntohs()函数将网络字节顺序转化成本地字节顺序。
其结果如下:
(先启动服务端再启动客户端)
在这里插入图片描述
以上
参考:哈工大《计算机网络》,博客链接:c++socket编程

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_50271250/article/details/119189727

智能推荐

linux音乐应用程序,适用于节拍,循环,录音的最佳免费Linux音乐制作应用程序 | MOS86...-程序员宅基地

文章浏览阅读374次。有人认为您需要最新的MacBook Pro和一套昂贵的商业软件来制作音乐,但是即使在macOS上,也有很多免费软件可以制作音乐。 金钱不能买人才,有才能的人通常可以充分利用免费工具。当然,在高端合成器引擎或工作站上放上数千美元可能会让生活变得更轻松,但是当您可以使用免费的音乐制作软件时,这并不是前提条件。 Linux是一些功能强大且灵活的音乐制作应用程序的所在地,面向初学者和专业人士。让我们来看看..._音频制作工具linux版本下载

Docker 学习笔记 - 进阶四 Docker网络和 Docker compose_dockercompose refers to undefined network playedu--程序员宅基地

文章浏览阅读1.8k次。docker network笔记记录学习尚硅谷周阳老师的 docker 教程链接地址: https://www.bilibili.com/video/BV1gr4y1U7CY?p=1_dockercompose refers to undefined network playedu-network:

环形进度条-程序员宅基地

文章浏览阅读70次。jQuery + CSS3实现原理原理非常的简单,在这个方案中,最主要使用了CSS3的transform中的rotate和CSS3的clip两个属性。用他们来实现半圆和旋转效果。半环的实现先来看其结构。html<div class="pie_right"> <div class="right"></div> &..._环形进度条 .square{ margin: 20px auto; width: 200

消息同步的实现-程序员宅基地

文章浏览阅读1.7k次。一、背景在编程中通信可以说是使用的最频繁的了,一个好的通信框架可以很大的提高系统的稳定性和编程的简洁性。可能之前我们使用现有的框架可以不考虑消息同步的问题,但是我觉得了解一点这方面的内容还是很有帮助的。今天给大家讲讲一个比较好的消息的同步的策略。二、为什么使用消息同步在通信的时候使用消息同步可以降低代码的复杂度,避免使用回调来处理业务,可以使代码更..._移动端即时消息和pc端即时消息同步时序图

三星服务器链接在哪个文件夹,三星手机无线连接,访问局域网电脑共享目录中的电影照片设置教程...-程序员宅基地

文章浏览阅读1.9k次。原标题:三星手机无线连接,访问局域网电脑共享目录中的电影照片设置教程有时候我们在刷手机的时候需要把手机的资料等传到电脑上,或者把电脑上的资料电影等传到手机上,用软件传又慢,用数据线又麻烦,那么在同局域网下手机有没办直接访问电脑共享文件夹目录呢?方法是有的,通过以下的配置方法就能实现,在配置之前要注意下无线路由器下的电脑和手机是否都在同一个局域网,如果你是多个无线路由器组合起来的,要看下是怎么组合的..._三星手机怎么访问共享文件

类中static变量和成员变量-程序员宅基地

文章浏览阅读7.2k次,点赞6次,收藏19次。类中static变量和普通变量的区别(一便解释了摘要中的内容)​  要理解标题,我们首先来看下static变量本身的特殊之处,即它存储在全局数据区。在类中的static变量也是如此。C++的内存分布代码区 :函数体的二进制代码全局数据区:全局变量和static变量堆 :动态内存分配 -&gt; 通过new栈 ..._类中static变量

随便推点

freemaker if 多个条件_FreeMarker中if标签内的判断条件-程序员宅基地

文章浏览阅读4.9k次。reeMarker中的标签除了里面直接判断 boolean 类型的变量外,也可以进行表达式判断,有几个细节记录一下1. 判断对象是否存在(null)经常会用到,如果对象 != null 则xxxx,在freemarker中表达比较奇怪,例如判断 target 是否为null,如果不为 nll 则做xxx动作xxxx#if>(目标变量后面连续两个??)2. 字符串或数字比较java里标准字符串..._freemarker if多条件

micropython 文档_micropython文档中缺失的说明-程序员宅基地

文章浏览阅读65次。PYB: sync filesystemsPYB: soft rebootMicroPython v1.6-310-g1937953 on 2016-03-28; PYBv1.0 with STM32F405RGType "help()" for more information.>>>import os, sys>>> dir(os)['__name__', 'uname', 'chdir', ..._micropython导入文件dir显示不全

自定义的转场动画实现方式以及原理介绍_转场特效的代码原理-程序员宅基地

文章浏览阅读1.1k次。BreatheView项目地址:arvinljw/BreatheView 简介:自定义的转场动画实现方式以及原理介绍更多:作者 提 Bug 标签:Animation-这是一个自定义的转场动画实现方式,目前支持两种方式:圆形展开 矩形展开效果如下:使用引用:在根目录级的 build.gradle 中添加allprojects { ..._转场特效的代码原理

root认证失败 tera term_解决Ubuntu 18.04使用root账户登录显示“认证失败”-程序员宅基地

文章浏览阅读514次。解决Ubuntu 18.04使用root账户登录显示“认证失败”时间:2020-06-26 11:54:15来源:作者:今天是土土第一次分享自己在学习中的经历,写的不好,请多见谅。最近土土有一个课程设计的任务——linux内核编译。因为Linux内核编译需要root用户权限。但是土土的ubnutu没有root,所以首先就需要建立一个root用户。普通用户登录,打开终端。设定root用户密码..._ubuntu18.04 进入root 鉴定故障

Linux下常用文件解压(包括rpm、deb包)_rpm的文件需要解药吗-程序员宅基地

文章浏览阅读1.5k次。http://www.hao32.com/unix-linux/306.htmlLinux下怎么解后缀名是gzip的文件?1.以.a为扩展名的文件:#tar xv file.a2.以.z为扩展名的文件:#uncompress file.Z3.以.gz为扩展名的文件:#gunzip file.gz4.以.bz2为扩展名的文件:#bunzip2 fi_rpm的文件需要解药吗

html5 密码加密,1、5 写注册的后台并写前台html&密码加密&id 随机-程序员宅基地

文章浏览阅读451次。1 public void save(Student student) {// TODO Auto-generated method stubstudent.setSid(UUID.randomUUID().toString());student.setPwd(MD5Util.md5(student.getPwd()));studentRepository.save(student);}在..._html网页加密随机密码

推荐文章

热门文章

相关标签