c++ 网络编程 socket sockaddr_in结构体

本文详细介绍了Windows环境下使用Socket进行TCP/IP通信的步骤,包括创建socket、绑定地址、监听、接受连接、发送和接收数据等,并提供了相关的C++代码示例。涉及到的关键函数有bind、listen、accept、connect、recv、send等。同时,讲解了sockaddr_in结构体及其成员的含义,以及如何填充地址信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

socket通信流程:

1、服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket

2、服务器为socket绑定ip地址和端口号

3、服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开

4、客户端创建socket

5、客户端打开socket,根据服务器ip地址和端口号试图连接服务器socket

6、服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回连接信息后才返回,开始接收下一个客户端谅解请求

7、客户端连接成功,向服务器发送连接状态信息

8、服务器accept方法返回,连接成功

9、客户端向socket写入信息

10、服务器读取信息

11、客户端关闭

12、服务器端关闭

使用到的函数:bind() listen() accept() connect() recv() send() recvform() sendto()

int bind(int fd, const struct sockaddr* my_addr, socklen_t addrlen);//成功返回0,失败返回-1,

例:if(bind(sockClient,(LPSOCKADDR)&local,sizeof(local) == SOCKET_ERROR)

int listen(int fd, int backlog);//listen()成功返回0,失败返回-1

例:if(listen(sockSrv, 10) == SOCKET_ERROR) //backlog 设置的数量

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//失败返回-1

例:SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrClient, &len);

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//成功返回0,失败返回-1

例:if (connect(sockClient, (struct  sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET)//#define INVALID_SOCKET   (SOCKET)(~0)非0

int send( SOCKET s,    const char FAR *buf,    int len,    int flags );//socket ,缓冲区 ,长度,0

例:if(send(sockConn, buf, sizeof(buf), 0)==0)//第四个参数一般置0。

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

例:int num = recv(sockClient, buff, sizeof(buff), 0);

int sendto(int sockfd, const void *msg,int len unsigned int flags, const struct sockaddr *to, int tolen);

例:sendto(sockClient, szSrvMsg, sizeof(szSrvMsg), 0, (SOCKADDR*)&addrClnt, nLen);

int recvfrom(int sockfd,void *buf,int len,unsigned int lags,struct sockaddr *from,int *fromlen);

例:if (SOCKET_ERROR != recvfrom(sockClient, szMsg, sizeof(szMsg), 0, (SOCKADDR*)&addrSrv, &nLen))

sockaddr_in结构体:

struct sockaddr { //sockaddr是给操作系统使用
    unsigned short    sa_family;     // 2 bytes address family, AF_xxx,sa_family是地址家族,一般都是“AF_xxx”的形式。通常大多用的是都是AF_INET,代表TCP/IP协议族
    char              sa_data[14];   // 14 bytes of protocol address
};

typedef struct sockaddr_in { //程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口
    short            sin_family;     // 2 bytes e.g. AF_INET, AF_INET6
    //sockaddr用其余14个字节来表示sa_data, sockaddr_in把14个字节拆分成sin_port, sin_addrsin_zero
    unsigned short   sin_port;       // 2 bytes e.g. htons(3490), 端口号
    IN_ADDR sin_addr;            //struct in_addr   sin_addr;       // 4 bytes see struct in_addr, below, IP 地址
    char             sin_zero[8];    // 8 bytes zero this if you want to, sin_zero用来填充字节使sockaddr_in和sockaddr保持一样大小
}SOCKADDR_IN;

typedef struct in_addr {
    union {
      struct{ unsigned char  s_b1, s_b2, s_b3, s_b4; } S_un_b;
      struct{ unsigned short s_w1, s_w2; } S_un_w;
      unsigned long S_addr;         //一般用这个(使用网络字节顺序存)
    }S_un;

#define s_addr  S_un.S_addr

} IN_ADDR;

例:SOCKADDR_IN server_addr;

server_addr.sin_family = AF_INET;

server_addrl.sin_port = htons(1048);

server_addr.sin_addr.s_addr = inet_addr(INADDR_ANY); 等价于 server_addr.sin_addr.S_un.S_addr = inet_addr(INADDR_ANY);

(1)socket创建区别:

TCP:          SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);

UDP:        SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);

创建socket套接字  int socket(int domain, int type, int protocol);

domain指哪一种地址类型;PF_INET, AF_INET: Ipv4网络协议; PF_INET6, AF_INET6: Ipv6网络协议。

Windows socket只支持一个通信区域:AF_INET国际网区域;

type参数是设置通信的协议类型:SOCK_STREAM(有保障)或SOCK_DGRAM(无保障);

参数protocol指socket使用的传输协议编号,一般设置为0.

(2) 加载socket动态链接库(dll)  初始化套接字库
    WORD wVersionRequested; 
    WSADATA wsaData;    // 这结构是用于接收Wjndows Socket的结构信息的 
    int err;      
    wVersionRequested = MAKEWORD( 2, 2 );   // 请求1.1版本的WinSock库        
    err = WSAStartup( wVersionRequested, &wsaData ); 

          if ( err != 0 ) {

     Printf(""加载套接字失败:%d......\n",WSAGetLastError()); 
     return -1;          // 返回值为零的时候是表示成功申请WSAStartup 

           }

     等同于:if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){// 返回值为零的时候是表示成功申请WSAStartup

Printf(""加载套接字失败:%d......\n",WSAGetLastError());

       } 
    if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) {  //检测版本号
        // 检查这个低字节是不是2,高字节是不是1以确定是否我们所请求的2.2版本 
        // 否则的话,调用WSACleanup()清除信息,结束函数 
        WSACleanup( ); 
        return -1;  
    }

void initialization() {

//初始化套接字库

WORD w_req = MAKEWORD(2, 2);//版本号

WSADATA wsadata;

int err;

err = WSAStartup(w_req, &wsadata);

if (err != 0) {

cout << "初始化套接字库失败!" << endl;

}

else {

cout << "初始化套接字库成功!" << endl;

}

//检测版本号

if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {

cout << "套接字库版本号不符!" << endl;

WSACleanup();

}

else {

cout << "套接字库版本正确!" << endl;

}

//填充服务端地址信息

}

(3)填充地址信息: SOCKADDR_IN  server_addr,client_addr;

网络通信地址:sockaddr_in结构体

struct sockaddr { //sockaddr是给操作系统使用
    unsigned short    sa_family;     // 2 bytes address family, AF_xxx,sa_family是地址家族,一般都是“AF_xxx”的形式。通常大多用的是都是AF_INET,代表TCP/IP协议族
    char              sa_data[14];   // 14 bytes of protocol address
};

typedef struct sockaddr_in { //程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口
    short            sin_family;     // 2 bytes e.g. AF_INET, AF_INET6
    //sockaddr用其余14个字节来表示sa_data, sockaddr_in把14个字节拆分成sin_port, sin_addrsin_zero
    unsigned short   sin_port;       // 2 bytes e.g. htons(3490), 端口号
    IN_ADDR sin_addr;            //struct in_addr   sin_addr;       // 4 bytes see struct in_addr, below, IP 地址
    char             sin_zero[8];    // 8 bytes zero this if you want to, sin_zero用来填充字节使sockaddr_in和sockaddr保持一样大小
}SOCKADDR_IN;

 

typedef struct in_addr {
    union {
      struct{ unsigned char  s_b1, s_b2, s_b3, s_b4; } S_un_b;
      struct{ unsigned short s_w1, s_w2; } S_un_w;
      unsigned long S_addr;         //一般用这个(使用网络字节顺序存)
    }S_un;

#define s_addr  S_un.S_addr

} IN_ADDR;

 

例:SOCKADDR_IN server_addr;

server_addr.sin_family = AF_INET;

server_addrl.sin_port = htons(10091);

server_addr.sin_addr.s_addr = inet_addr(INADDR_ANY); 等价于 server_addr.sin_addr.S_un.S_addr = inet_addr(INADDR_ANY);

(4)绑定/监听/接受  bind/listen/accept

if (bind(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {

cout << "套接字绑定失败!" << endl;

WSACleanup();

}

else {

cout << "套接字绑定成功!" << endl;

}

//设置套接字为监听状态

if (listen(s_server, SOMAXCONN) < 0) {

cout << "设置监听状态失败!" << endl;

WSACleanup();

}

else {

cout << "设置监听状态成功!" << endl;

}

cout << "服务端正在监听连接,请稍候...." << endl;

//接受连接请求

len = sizeof(SOCKADDR);

s_accept = accept(s_server, (SOCKADDR *)&accept_addr, &len);

if (s_accept == SOCKET_ERROR) {

cout << "连接失败!" << endl;

WSACleanup();

return 0;

}

cout << "连接建立,准备接受数据" << endl;

(5)接收/发送数据 send/recv

while (1) {

recv_len = recv(s_accept, recv_buf, 100, 0);

if (recv_len < 0) {

cout << "接受失败!" << endl;

break;

}

else {

cout << "客户端信息:" << recv_buf << endl;

}

cout << "请输入回复信息:";

cin >> send_buf;

send_len = send(s_accept, send_buf, 100, 0);

if (send_len < 0) {

cout << "发送失败!" << endl;

break;

}

}

(6)关闭套接字

closesocket(s_server);//关闭套接字

closesocket(s_accept);

WSACleanup();//释放DLL资源

 

(7)客户端连接

//创建套接字

s_server = socket(AF_INET, SOCK_STREAM, 0);

if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {

cout << "服务器连接失败!" << endl;

WSACleanup();

}

else {

cout << "服务器连接成功!" << endl;

}

示例:server 端

#include "pch.h"
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void initialization();
int main() {
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;
	int len = 0;
	//定义发送缓冲区和接受缓冲区
	char send_buf[100];
	char recv_buf[100];
	//定义服务端套接字,接受请求套接字
	SOCKET s_server;
	SOCKET s_accept;
	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	SOCKADDR_IN accept_addr;
	initialization();
	//填充服务端信息
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(5010);
	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (bind(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		cout << "套接字绑定失败!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字绑定成功!" << endl;
	}
	//设置套接字为监听状态
	if (listen(s_server, SOMAXCONN) < 0) {
		cout << "设置监听状态失败!" << endl;
		WSACleanup();
	}
	else {
		cout << "设置监听状态成功!" << endl;
	}
	cout << "服务端正在监听连接,请稍候...." << endl;
	//接受连接请求
	len = sizeof(SOCKADDR);
	s_accept = accept(s_server, (SOCKADDR *)&accept_addr, &len);
	if (s_accept == SOCKET_ERROR) {
		cout << "连接失败!" << endl;
		WSACleanup();
		return 0;
	}
	cout << "连接建立,准备接受数据" << endl;
	//接收数据
	while (1) {
		recv_len = recv(s_accept, recv_buf, 100, 0);
		if (recv_len < 0) {
			cout << "接受失败!" << endl;
			break;
		}
		else {
			cout << "客户端信息:" << recv_buf << endl;
		}
		cout << "请输入回复信息:";
		cin >> send_buf;
		send_len = send(s_accept, send_buf, 100, 0);
		if (send_len < 0) {
			cout << "发送失败!" << endl;
			break;
		}
	}
	//关闭套接字
	closesocket(s_server);
	closesocket(s_accept);
	//释放DLL资源
	WSACleanup();
	return 0;
}
void initialization() {
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
		cout << "初始化套接字库失败!" << endl;
	}
	else {
		cout << "初始化套接字库成功!" << endl;
	}
	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字库版本正确!" << endl;
	}
	//填充服务端地址信息
 
}

client 端

#include "pch.h"
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
void initialization();
int main() {
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;
	//定义发送缓冲区和接受缓冲区
	char send_buf[100];
	char recv_buf[100];
	//定义服务端套接字,接受请求套接字
	SOCKET s_server;
	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	initialization();
	//填充服务端信息
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	server_addr.sin_port = htons(1234);
	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		cout << "服务器连接失败!" << endl;
		WSACleanup();
	}
	else {
		cout << "服务器连接成功!" << endl;
	}
 
	//发送,接收数据
	while (1) {
		cout << "请输入发送信息:";
		cin >> send_buf;
		send_len = send(s_server, send_buf, 100, 0);
		if (send_len < 0) {
			cout << "发送失败!" << endl;
			break;
		}
		recv_len = recv(s_server, recv_buf, 100, 0);
		if (recv_len < 0) {
			cout << "接受失败!" << endl;
			break;
		}
		else {
			cout << "服务端信息:" << recv_buf << endl;
		}
 
	}
	//关闭套接字
	closesocket(s_server);
	//释放DLL资源
	WSACleanup();
	return 0;
}
void initialization() {
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
		cout << "初始化套接字库失败!" << endl;
	}
	else {
		cout << "初始化套接字库成功!" << endl;
	}
	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字库版本正确!" << endl;
	}
	//填充服务端地址信息
 
}

参考:

Windows下与Linux下编写socket程序的区别
http://blog.chinaunix.net/uid-2270658-id-308160.html
sockaddr_in , sockaddr , in_addr区别Socket编程函数集(非常有用)
http://810364804.iteye.com/blog/2094531
ntohs, ntohl, htons,htonl的比较和详解
http://www.360doc.com/content/12/0222/10/54470_188560773.shtml

socket编程 sockaddr sockaddr_in

https://blog.youkuaiyun.com/keen_zuxwang/article/details/72872768?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值