【C/C++套接字编程】套接字的基本概念与基础语法

TCP/UDP实验为牵引,学习套接字编程的相关知识,再进一步深化对TCP/UDP的理解

目录

前言

Socket编程语法

1. 套接字及创建

什么是套接字?

创建套接字

2. 端口绑定

3. 收发信息

与recv()函数的比较:

与send()函数的比较:

编程实例 

总结


系列博客

【C/C++套接字编程】TCP协议通信的简单实现_Mr_Fmnwon的博客-CSDN博客

【C/C++套接字编程】TCP通信实验_Mr_Fmnwon的博客-CSDN博客

【C/C++套接字编程】UDP协议通信的简单实现_Mr_Fmnwon的博客-CSDN博客

【C/C++套接字编程】UDP通信实验_Mr_Fmnwon的博客-CSDN博客


前言

计算机网络课学习了TCP/UDP相关的内容与知识,并组织了套接字实现TCP/UDP通信的实验。为了更好了解、掌握套接字编程的相关知识,特此做出总结。

参考文献

什么是套接字?Socket基本介绍_Ostrich5yw的博客-CSDN博客

套接字之读写:recvfrom()、read() 和sendto() 、write()_read和recvfrom_傻不拉几的程序员的博客-CSDN博客


Socket编程语法


1. 套接字及创建

什么是套接字?

套接字是一种通信机制(通信的两方的一种约定),socket屏蔽了各个协议的通信细节,提供了tcp/ip协议的抽象,对外提供了一套接口,同过这个接口就可以统一、方便的使用tcp/ip协议的功能。这使得程序员无需关注协议本身,直接使用socket提供的接口来进行互联的不同主机间的进程的通信。我们可以用套接字中的相关函数来完成通信过程。

套接字的特性有三个属性确定,它们是:域(domain),类型(type),和协议(protocol)。

:指定套接字通信中使用的网络介质。最常见的套接字域是 AF_INET(IPv4)或者AF_INET6(IPV6),它是指 Internet 网络。

类型

流套接字(SOCK_STREAM): 流套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送,并按顺序接收。流套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP 数据报套接字(SOCK_DGRAM): 数据报套接字提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP(User Datagram Protocol)协议进行数据的传输。 原始套接字(SOCK_RAW): 原始套接字与标准套接字(标准套接字指的是前面介绍的流套接字和数据报套接字)的区别在于:原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送数据必须使用原始套接字。

协议IPPROTO_TCPIPPROTO_UDP

创建套接字

int sockfd = socket(domain, type, protocol)

sockfd:套接字描述符,一个整数(如文件句柄) domain:整数,通信域,例如AF_INET(IPv4协议),AF_INET6(IPv6协议) type:通信类型 SOCK_STREAM:TCP(可靠,面向连接) SOCK_DGRAM:UDP(不可靠,无连接的) protocol: Internet协议(IP)的协议值,为0。这与出现在数据包IP报头的协议字段中的数字相同。(有关详细信息,请参见手动协议)


2. 端口绑定

套接字作为一层抽象,可以说是端口的代理人,主机用户只需要和本套接字进行交互,而不在意套接字的具体实现过程。因此,套接字创建完毕后,要和端口进行绑定,之后的信息流进流出端口,其复杂的过程就被抽象为与套接字这个代理人的交互过程了。

自然要知道绑定的端口的地址,可以通过如下设置

struct sockaddr_in client_in;
client_in.sin_port =htons(20001);//端口号20001
client_in.sin_addr.S_un.S_addr = inet_addr("10.128.18.146");//ipv4地址
client_in.sin_family =AF_INET;//选择ipv4协议簇

这个绑定过程,我们通过bind()来实现。

bind():

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

首先如上设置IP/Port信息,接下来将创建的套接字与之进行绑定

//part3 将用户端的socket和用户端的ip地址和端口号绑定
if (bind(socket_client, (struct sockaddr *)&client_in, sizeof(client_in)) == SOCKET_ERROR)
{
    printf("blind() Failed:%d\\n", WSAGetLastError());        
    return USER_ERROR;
}

3. 收发信息

1、read() 和write()

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0。
ssize_t write(int fd, const void *buf, size_t count);
如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。

流字节套接字(例如TCP套接字)上的read和write函数所表现的行为不同于通常的文件I/O。字节流套接字上调用read或write输入或输出的字节数可能比请求的数量少(我们称之为部分读和部分写),然而这不是出错的状态。这个现象的原因在于内核中用于套接字的缓冲区可能已达到了极限。此时所需要的是调用者再次调用read或write函数,以输入或输出剩余的字节

2、recvfrom()和sendto() recvfrom()从(已连接)套接口上接收数据,并捕获数据发送源的地址。对于SOCK_STREAM类型的套接口,最多可接收缓冲区大小个数据。对于数据报类套接口,队列中第一个数据报中的数据被解包,但最多不超过缓冲区的大小。如果数据报大于缓冲区,那么缓冲区中只有数据报的前面部分,其他的数据都丢失了,并且recvfrom()函数返回WSAEMSGSIZE错误。

SendTo指向一指定目的地发送数据,sendto()适用于发送(未建立连接)的UDP数据包 (参数为SOCK_DGRAM)。

int recvfrom(SOCKET s,void *buf,int len,unsigned int flags, struct sockaddr *from,int *fromlen);
参数:
s:       标识一个已连接套接口的描述字。
buf:     接收数据缓冲区。
len:     缓冲区长度。
flags:   调用操作方式。
from:   (可选)指针,指向装有源地址的缓冲区。
fromlen:(可选)指针,指向from缓冲区长度值。
若无错误发生,recvfrom()返回读入的字节数。如果连接已中止,返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
int sendto (IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags, IN const struct sockaddr FAR *to, IN int tolen);
参数:
s        套接字
buff     待发送数据的缓冲区
size     缓冲区长度
Flags    调用方式标志位, 一般为0, 改变Flags,将会改变Sendto发送的形式
addr    (可选)指针,指向目的套接字的地址
len addr 所指地址的长度
返回值为整型,如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR。

与recv()函数的比较:

UDP使用recvfrom()函数接收数据,他类似于标准的read(),但是在recvfrom()函数中要指明目的地址。从套接字上接收一个消息。对于recvfrom ,可同时应用于面向连接的和无连接的套接字。recv一般只用在面向连接的套接字,几乎等同于recvfrom,只要将recvfrom的第五个参数设置NULL。不管是recv还是recvfrom,都有两种模式,阻塞和非阻塞,可以通过ioctl函数来设置。阻塞模式是一直等待直到有数据到达,非阻塞模式是立即返回,需要通过消息,异步事件等来查询完成状态。

recv函数

int recv( SOCKET s, char *buf, int len, int flags)

**功能:**不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。

参数一:指定接收端套接字描述符; 参数二:指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据; 参数三:指明buf的长度; 参数四 :一般置为0。


与send()函数的比较:

是向一个已经连接的socket发送数据,而sendto则是面向未建立连接的UDP数据报。不论是客户端还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

send函数

int send( SOCKET s,char *buf,int len,int flags );

功能:不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

参数一:指定发送端套接字描述符; 参数二:存放应用程序要发送数据的缓冲区; 参数三:实际要发送的数据的字节数; 参数四:一般置为0。


编程实例 

【C/C++套接字编程】TCP协议通信的简单实现_Mr_Fmnwon的博客-CSDN博客

【C/C++套接字编程】UDP协议通信的简单实现_Mr_Fmnwon的博客-CSDN博客

结合起来食用最佳 


总结

关于TCP/UDP的实验实例,将在新的博客中给出。

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的套接字上位机编程C++源码示例: ```c++ #include <iostream> #include <winsock2.h> // 包含Winsock库的头文件 #pragma comment(lib,"ws2_32.lib") // 链接Winsock库文件 using namespace std; int main() { WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaData) != 0) // 初始化Winsock库 { cout << "启动Winsock库失败!" << endl; return 0; } // 创建套接字 int serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (serverSocket == INVALID_SOCKET) { cout << "创建套接字失败!" << endl; WSACleanup(); return 0; } // 绑定套接字 sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8888); serverAddr.sin_addr.s_addr = INADDR_ANY; if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { cout << "绑定套接字失败!" << endl; closesocket(serverSocket); WSACleanup(); return 0; } // 监听套接字 if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) { cout << "监听套接字失败!" << endl; closesocket(serverSocket); WSACleanup(); return 0; } while (true) { // 接受客户端连接 sockaddr_in clientAddr; int clientAddrLen = sizeof(clientAddr); int clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrLen); if (clientSocket == INVALID_SOCKET) { cout << "接受客户端连接失败!" << endl; closesocket(serverSocket); WSACleanup(); return 0; } // 接收客户端数据 char recvBuf[1024] = { 0 }; int recvLen = recv(clientSocket, recvBuf, sizeof(recvBuf), 0); if (recvLen == SOCKET_ERROR) { cout << "接收数据失败!" << endl; closesocket(clientSocket); continue; } cout << "接收到客户端数据:" << recvBuf << endl; // 发送数据到客户端 const char* sendBuf = "Hello, Client!"; int sendLen = send(clientSocket, sendBuf, strlen(sendBuf), 0); if (sendLen == SOCKET_ERROR) { cout << "发送数据失败!" << endl; closesocket(clientSocket); continue; } cout << "发送数据到客户端:" << sendBuf << endl; // 关闭客户端套接字 closesocket(clientSocket); } // 关闭套接字 closesocket(serverSocket); WSACleanup(); return 0; } ``` 这个示例程序实现了一个简单的TCP服务器,它可以接受客户端的连接请求,并接收来自客户端的数据,并向客户端发送回复数据。注意,这段代码只是一个简单的示例,实际的套接字编程可能需要更多的处理来确保程序的正确性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值