C++网络编程快速入门(一):TCP网络通信基本流程以及基础函数使用

目录

  • 流程概述
  • 服务器端代码实现
  • 客户端代码实现
  • 函数和结构讲解
    • sockaddr_in和sockaddr
    • socket : 创建一个socket连接
    • bind :绑定地址以及端口号问题

流程概述

客户端与服务器之间的网络通信基本原理如下所示,复杂一点的架构可能会添加消息中间件。
对于服务端,通信流程如下:

1、调用socket函数创建监听socket
2、调用bind函数将socket绑定到某个IP和端口号组成的二元组上
3、调用listen函数开启监听
4、当有客户端连接请求时,调用accept函数接受连接,产生一个新的socket(与客户端通信的socket)
5、基于新产生的socket调用send或recv函数开始与客户端进行数据交流
6、通信结束后,调用close函数关闭socket

对于客户端,通信流程如下:

1、调用socket函数创建客户端socket
2、调用connect函数尝试连接服务器
3、连接成功后调用send或recv函数与服务器进行数据交流
4、通信结束后,调用close函数关闭监听socket

在这里插入图片描述

服务器端代码实现

#include <iostream>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>using namespace std;
int main() {// 创建一个监听socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1) {cout << " create listen socket error " << endl;return -1;}// 初始化服务器地址struct sockaddr_in bindaddr;bindaddr.sin_family = AF_INET;bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);bindaddr.sin_port = htons(3000);if (bind(listenfd, (struct sockaddr *)& bindaddr, sizeof(bindaddr)) == -1) {cout << "bind listen socket error" << endl;return -1;}// 启动监听if (listen(listenfd, SOMAXCONN) == -1) {cout << "listen error" << endl;return -1;}while (true) {// 创建一个临时的客户端socketstruct sockaddr_in clientaddr;socklen_t clientaddrlen = sizeof(clientaddr);// 接受客户端连接int clientfd = accept(listenfd, (struct sockaddr *)& clientaddr, &clientaddrlen);if (clientfd != -1) {char recvBuf[32] = {0};// 从客户端接受数据int ret = recv(clientfd, recvBuf, 32, 0);if (ret > 0) {cout << "recv data from cilent , data:" << recvBuf << endl;// 将接收到的数据原封不动地发给客户端ret = send(clientfd, recvBuf, strlen(recvBuf), 0);if (ret != strlen(recvBuf)) {cout << "send data error" << endl;} else {cout << "send data to client successfully, data " << recvBuf <<endl;}} else {cout << "recv data error" <<endl;}close(clientfd);}}// 关闭监听socketclose(listenfd);return 0;
}

客户端代码实现

#include <iostream>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>#define SERVER_ADDRESS "127.0.0.1"
#define SERVER_PORT 3000
#define SEND_DATA "helloworld"using namespace std;int main() {// 创建一个socketint clientfd = socket(AF_INET, SOCK_STREAM, 0);if (clientfd == -1) {cout << " create client socket error " << endl;return -1;}// 连接服务器struct sockaddr_in serveraddr;serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);serveraddr.sin_port = htons(SERVER_PORT);if (connect(clientfd, (struct sockaddr *)& serveraddr, sizeof(serveraddr)) == -1) {cout << "connect socket error" << endl;return -1;}// 向服务器发送数据int ret = send(clientfd, SEND_DATA, strlen(SEND_DATA), 0);if (ret != strlen(SEND_DATA)) {cout << "send data error" << endl;return -1;} else {cout << "send data to client successfully, data " << SEND_DATA <<endl;}// 从服务器拉取数据char recvBuf[32] = {0};ret = recv(clientfd, recvBuf, 32, 0);if (ret > 0) {cout << "recv data to client successfully, data " << recvBuf <<endl;} else {cout << "recv data to client error" << endl;}// 关闭socketclose(clientfd);return 0;
}

函数和结构讲解

sockaddr_in和sockaddr

在讲解套接字编程函数之前,有必要对socket编程的两个不可或缺的结构体进行说明:sockaddrsockaddr_in
结构如下:

struct sockaddr{__SOCKADDR_COMMON (sa_);	/* Common data: address family and length.  */char sa_data[14];		/* Address data.  */};/* Structure describing an Internet socket address.  */
struct sockaddr_in{__SOCKADDR_COMMON (sin_);in_port_t sin_port;			/* Port number.  */struct in_addr sin_addr;		/* Internet address.  *//* Pad to size of `struct sockaddr'.  */unsigned char sin_zero[sizeof (struct sockaddr)- __SOCKADDR_COMMON_SIZE- sizeof (in_port_t)- sizeof (struct in_addr)];};

由于历史的原因,套接字函数中(如connect,bind等)使用的参数类型大多是sockaddr类型的。而如今进行套接字编程的时候大都使用sockaddr_in进行套接字地址填充.因此,这就要求对这些函数进行调用的时候都必须要讲套接字地址结构指针进行类型强制转换,例如:

struct sockaddr_in serv;bind(sockfd,(struct sockaddr *)&serv,sizeof(serv));

socket : 创建一个socket连接

/* Create a new socket of type TYPE in domain DOMAIN, usingprotocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.Returns a file descriptor for the new socket, or -1 for errors.  */
extern int socket (int __domain, int __type, int __protocol) __THROW; 

向用户提供一个套接字,即套接口描述文件字,它是一个整数,如同文件描述符一样,是内核标识一个IO结构的索引。
一般传入参数是这样的:

int clientfd = socket(AF_INET, SOCK_STREAM, 0);

__domain:这个参数指定一个协议簇,也往往被称为协议域。系统存在许多可以的协议簇,常见有AF_INET──指定为IPv4协议,AF_INET6──指定为IPv6,AF_LOCAL──指定为UNIX 协议域。这里指网络层的协议
__type:这个参数指定一个套接口的类型,套接口可能的类型有:SOCK_STREAMSOCK_DGRAMSOCK_SEQPACKETSOCK_RAW等等,它们分别表明字节流、数据报、有序分组、原始套接口。
__protocol:指定相应的传输协议,也就是诸如TCP或UDP协议等等,系统针对每一个协议簇与类型提供了一个默认的协议,我们通过把protocol设置为0来使用这个默认的值。这里指传输层的协议
返回值:socket函数返回一个套接字,即套接口描述字。如果出现错误,它返回-1,并设置errno为相应的值,用户应该检测以判断出现什么错

返回值:成功则返回0,失败返回非0

bind :绑定地址以及端口号问题

在服务器端我们有这样一段代码:

// 初始化服务器地址
struct sockaddr_in bindaddr;
bindaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindaddr.sin_port = htons(3000);
if (bind(listenfd, (struct sockaddr *)& bindaddr, sizeof(bindaddr)) == -1) {cout << "bind listen socket error" << endl;return -1;
}

函数参数解释:

/* Give the socket FD the local address ADDR (which is LEN bytes long).  */
extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)

bind的地址我们使用了宏INADDR_ANY,这个宏定义如下:

/* Address to accept any incoming messages.  */
#define	INADDR_ANY		((in_addr_t) 0x00000000)

如果应用程序不关心bind绑定的IP,则可以使用这个宏,底层的协议栈服务会自动选择一个合适的IP地址,这样在多个网卡机器上选择IP地址会变得简单
如果只想在本机上进行访问,bind函数地址可以使用本地回环地址
如果只想被局域网的内部机器访问,那么bind函数地址可以使用局域网地址
如果希望被公网访问,那么bind函数地址可以使用INADDR_ANY or 0.0.0.0

网络通信的基本逻辑是客户端连接服务器,即从客户端的地址:端口 连接到 服务器的地址:端口上。
一般来说,服务器的端口号是固定的,而客户端的端口号是连接发起时操作系统随机分配的,并且不会分配被占用的端口。端口号是一个short类型的值,其范围为0~65535.
如果将bind函数中的端口号设置为0,那么操作系统会随机为程序分配一个可用的监听端口。一般来说,服务程序不会这么做,因为服务程序是要对外服务的,必须让客户端知道确切的IP地址和端口号。
在特殊的应用中,我们也可以在客户端程序以指定的端口号连接服务器,与普通的流程相比就是在创建socket与发起connect之间多了一次bind操作:

图1 不绑定
图2 绑定

其他相关函数可以到往期文章中查看:
socket编程常见函数使用方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/376818.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php 字符串处理

addcslashes — 为字符串里面的部分字符添加反斜线转义字符addslashes — 用指定的方式对字符串里面的字符进行转义bin2hex — 将二进制数据转换成十六进制表示chop — rtrim() 的别名函数chr — 返回一个字符的ASCII码chunk_split — 按一定的字符长度将字符串分割成小块conve…

使用前端框架Foundation 4来帮助简化响应式设计开发

日期&#xff1a;2013-3-12 来源&#xff1a;GBin1.com Foundation是一套使用广泛的前端开发套件&#xff0c;可以帮助你快速的网站。最近ZURB发布了一个新版本的Foundation 4前端框架&#xff0c;能够有效的帮助你快速的开发响应式的网站。 和另外一个套知名的前端框架BootSt…

C++网络编程快速入门(二):Linux下使用select演示简单服务端程序

目录select参数解释select使用规范select使用缺点基本流程实例代码通信效果演示往期文章select参数解释 extern int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout)…

Android转载一:Android文件命名规范

REF&#xff1a;http://blog.csdn.net/gulianchao/article/details/23391651 (一) Layout命名 1&#xff0e;contentview命名&#xff1a;activity_功能模块.xml 例如&#xff1a;activity_main.xml、activity_more.xml 2&#xff0e;Dialog命名&#xff1a;dialog_描述.xml …

[转]XBRL应用软件分类

1) 分类标准编辑软件(Taxonomy editor)&#xff1a; 分类标准是XBRL技术的应用基础&#xff0c;每一个采用XBRL技术的国家都必须先按各国的GAAP制订XBRL分类标准&#xff0c;上市公司才能据以编制实例文件。由于一套XBRL 2.0或2.1版分类标准必须包含至少一份XML Schema文…

C++网络编程快速入门(三):阻塞与非阻塞式调用网络通信函数

目录阻塞与非阻塞定义send与recvconnect一些问题为什么要将监听socket设置为非阻塞阻塞与非阻塞定义 阻塞模式指的是当前某个函数执行效果未达预期&#xff0c;该函数会阻塞当前的执行线程&#xff0c;程序执行流在超时时间到达或者执行成功后恢复原有流程。非阻塞模式相反&am…

css3实现头像旋转360度

css样式: .div a img{ width: 88px; height: 88px; border-radius: 88px; transition: all 1.2s ease-out 0s;}.div a img:hover{ -webkit-transform:rotate(360deg); -moz-transform:rotate(360deg); -o-transform:rotate(360deg); -ms-transform:rotate(360deg); transform:r…

POJ 2488 深搜

POJ 2488 深搜 要求字典序的顺序。 1 #include <iostream>2 #include <stdio.h>3 #include <string.h>4 using namespace std;5 int n,m,cnt;6 bool success;7 bool sign[30][30];8 int step[30][2];9 int dir[8][2]{ 10 -2,-1,-2,1, 11 …

socket 端口和地址复用

https://blog.csdn.net/weibo1230123/article/details/79978745 https://blog.csdn.net/weixin_42157432/article/details/115560824 在linux socket网络编程中&#xff0c;大规模并发TCP或UDP连接时&#xff0c;经常会用到端口复用&#xff1a; int opt 1; if (setsockopt…

MyEclipse老是弹出problem occurred窗口

有的时候是因为jsp页面中的java脚本有误&#xff0c;比如说<% String name"";>就会出现错误&#xff0c;因为结束标签少了一个百分号&#xff05;。转载于:https://www.cnblogs.com/passer1991/archive/2013/03/15/2961624.html

C++网络编程快速入门(四):EPOLL模型使用

目录基本使用方法step1:创建epollfdstep2:将fd绑定到epollfdstep3:调用epoll_wait检测事件epoll_wait与poll、select区别所在水平触发与边缘触发基本使用方法 step1:创建epollfd 创建一个epollfd&#xff0c;若epoll_create调用成功&#xff0c;则返回一个非负值的epollfd&am…

Mysql中代替like模糊查询的一种方法

使用Mysql的函数instr,可代替传统的like方式查询,并且速度更快。 instr函数&#xff0c;第一个参数是字段&#xff0c;第二个参数是要查询的串&#xff0c;返回串的位置&#xff0c;第一个是1&#xff0c;如果没找到就是0. 例如&#xff1a; select username from prefix_user …

两种大小端判断的方式

网络通信是按照字节流进行数据交换的&#xff0c;主机根据不同的CPU型号可能是大段存储&#xff0c;也可能是小端存储。而网络字节序在TCP/IP协议中已经规定好了&#xff0c;采用大端的排序方式。 所以网络通信中一般将需要传输的整数型值转换成网络字节序。 从本机字节序转换成…

把数据库复制成脚本(包含远程以及数据库数据)

1.启动VS 2.服务器资源管理器 3.连接需要的数据库 4.右键数据库 选择publist to provider.... 5.剩下的 选择数据库 选择存放地址 下一步 这方法应该是用在把08的数据还原到05上面 明天用这个方法去盗取哈公司的数据库 看行不行转载于:https://www.cnblogs.com/Rock-Lee/a…

代理模式用来初始化的延迟下载

package 设计模式; //代理模式实现延迟加载来减小启动时间 //数据库查询接口 interface IDBQery{ public String request(); }class DBQuery implements IDBQery {//创建一个DBQery非常耗时的&#xff0c;这里面我可以在需要DBQuery的时候在创建public DBQuery(){try {Thread.s…

Linux网络故障排查命令(ifconfig、ping、telnet、netstat、lsof、nc、curl、tcpdump)

目录ifconfig-s&#xff0c;显示网卡信息的精简列表-a、up、down将IP地址绑定到某个网卡&#xff0c;以及解绑操作pingtelnetnetstatlsofnc模拟一个服务器程序和客户端程序进行通信发送文件curltcpdump参数连接一个正常的监听端口ifconfig 该命令用来查看当前系统的网卡和IP地…

My Oracle Support Metalink站点最近将放弃flash界面转而使用ADF HTML

根据oracle官方博客的报道《The New My Oracle Support User Interface (HTML-based) 》&#xff0c; MY ORACLE SUPPORT开发team会在最近将support.oracle.com站点从原来的flash界面迁移到基于ADF HTML的用户界面上。 实际上在2012年的 January 27&#xff0c; MOS开发team就…

BF算法

BF(Brute Force)算法是普通的模式匹配算法&#xff0c;BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配&#xff0c;若相等&#xff0c;则继续比较S的第二个字符和 T的第二个字符&#xff1b;若不相等&#xff0c;则比较S的第二个字符和T的第一个字符&…

心跳检测以及应用层心跳包机制设计

博主联系方式&#xff1a; QQ:1540984562 微信&#xff1a;wxid_nz49532kbh9u22 QQ交流群&#xff1a;750313950&#xff08;嵌入式方向&#xff09; QQ交流群&#xff1a;856398158&#xff08;后端方向&#xff09; 目录心跳检测应用场景死连接情况保活传递有效业务数据心跳包…

【APUE】孤儿进程与僵死进程

基本概念&#xff1a; 在unix/linux中&#xff0c;正常情况下&#xff0c;子进程是通过父进程创建的&#xff0c;子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后&#xff0c…