版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_40313634/article/details/80633155
功能:两个exe件实现信息交流。
优化方向:
1. 客户端断开连接后,服务器端应遵循四次握手,也要断开连接,重新绑定端口,继续监听等待下次连接。
2. 服务器或客户端均应可连续发送多条消息,而不是发送完毕后等待对方相应后才能继续发送。
3. 目前必须先启动服务器,再启动客户端。能否也可以先启动客户端,直接服务器启动后再建立连接,而不是直接断掉(当服务器未启动时)。
常用的 socket 接口函数:
1. int socket(int domain, int type, int protocol);
功能:根据指定的协议和类型,创建一个socket,并返回socket 的描述信息(类似创建文件后返回的文件指针)。
入参解释:
int domain :入参,指定socket 的协议族,常用的有 AF_INET(IPV4协议族)、AF_UNIX(UXIX 本地通信协议)等。 int type :指定socket 类型。常用的有 SOCK_STREAM(面向连接的,如发送邮件等)、SOCK_DGRAM(面向无连接的,如广播等)等等。
int protocol : 指定具体使用那个协议。常用的协议有,IPPROTO_TCP(TCP传输协议)、IPPTOTO_UDP(UDP传输协议)等等。
返回值: int socket :创建的socket 的描述符,唯一标识一个socket。
2. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:给创建的socket 绑定对应的协议和端口。
入参解释:
int sockfd :入参,socket 描述符。
sockaddr *addr :入参,指向要绑定到socket 上的协议信息。不同协议,指针结构不同。
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
}; // IPV4 指定的信息。
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
socklen_t addrlen : 入参,对应的sockaddr 的地址长度。
返回值: int bind :绑定成功与失败(SOCKET_ERROR )。
3. int listen(int sockfd, int backlog);
功能:服务器监听绑定的的端口,等待客户端连接。
入参解释:
int sockfd :入参,socket 描述符。
int backlog :入参,socket 可以排队的最大连接个数。
返回值:
int listen: 有无(SOCKET_ERROR )连接。
4. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:客户机请求连接服务器。
入参解释:
int sockfd :入参,socket 描述符。
socketaddr *addr :入参,socket 的地址量。
socklen_t : 入参,socket 地址的长度。
返回值:
int conect: 客户机连接服务器成功与否。
5. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数解释:socket 描述符(入参),socket 的地址量(出参),socket 地址的长度(出参)。
6. int close(int fd);
功能:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。
服务器端代码:
创建套接字 -> 绑定端口 -> 接受连接请求 -> 接受或发送数据 -> 断开连接。
#include "InitSock.h"
#include <stdio.h>
#include <iostream>
using namespace std;
CInitSock initSock; // 初始化Winsock库
int main()
{
// 创建套节字:IPV4 协议族,面向无连接,TCP 协议。
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sListen == INVALID_SOCKET)
{
printf("Failed socket() \n");
return 0;
}
// 填充套接字地址信息。
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(4567); //1024 ~ 49151:普通用户注册的端口号
sin.sin_addr.S_un.S_addr = INADDR_ANY; // INADDR_ANY:本机IP,如果是多网卡机器,表示多个IP 的集合。
// 绑定套节字到本地主机地址
if(::bind(sListen, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("Failed bind() \n");
return 0;
}
// 监听端口,最多允许2个排队的socket。
if(::listen(sListen, 2) == SOCKET_ERROR)
{
printf("Failed listen() \n");
return 0;
}
// 循环接受客户的连接请求
sockaddr_in remoteAddr;
int nAddrLen = sizeof(remoteAddr);
SOCKET sClient = 0;
char szText[] = " TCP Server Demo! \r\n";
while(sClient==0)
{
// 接受一个新连接
//((SOCKADDR*)&remoteAddr)一个指向sockaddr_in结构的指针,用于获取对方地址
sClient = ::accept(sListen, (SOCKADDR*)&remoteAddr, &nAddrLen);
if(sClient == INVALID_SOCKET)
{
printf("Failed accept()");
}
else
{
printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
}
}
while(TRUE)
{
// 向客户端发送数据
while( NULL != gets(szText) );
::send(sClient, szText, strlen(szText), 0);
// 从客户端接收数据
char buff[256] ;
int nRecv = 0;
while( ::recv(sClient, buff, 256, 0) > 0)
{
buff[nRecv] = '\0';
printf(" Client :%s\n", buff);
}
}
// 关闭同客户端的连接
::closesocket(sClient);
// 关闭监听套节字
::closesocket(sListen);
return 0;
}
客户端代码:
#include "InitSock.h"
#include <stdio.h>
#include <iostream>
using namespace std;
CInitSock initSock; // 初始化Winsock库
int main()
{
// 创建套节字
SOCKET s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(s == INVALID_SOCKET)
{
printf(" Failed socket() \n");
return 0;
}
// 也可以在这里调用bind函数绑定一个本地地址
// 否则系统将会自动安排
// 填写远程地址信息
sockaddr_in servAddr;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(4567);
// 注意,这里要填写服务器程序(TCPServer程序)所在机器的IP地址
// 如果你的计算机没有联网,直接使用127.0.0.1即可
servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if(::connect(s, (sockaddr*)&servAddr, sizeof(servAddr)) == -1)
{
printf(" Failed connect() \n");
return 0;
}
char buff[256];
char szText[256] ;
while(TRUE)
{
//从服务器端接收数据
int nRecv = ::recv(s, buff, 256, 0);
if(nRecv > 0)
{
buff[nRecv] = '\0';
printf("接收到数据:%s\n", buff);
}
// 向服务器端发送数据
gets(szText) ;
szText[255] = '\0';
::send(s, szText, strlen(szText), 0) ;
}
// 关闭套节字
::closesocket(s);
return 0;
}
封装的InitSock.h
#include <winsock2.h>
#include <stdlib.h>
#include <conio.h>
#include <stdio.h>
#pragma comment(lib, "WS2_32") // 链接到WS2_32.lib
class CInitSock
{
public:
CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
{
// 初始化WS2_32.dll
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if(::WSAStartup(sockVersion, &wsaData) != 0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup();
}
};
int socket(int domain, int type, int protocol);