语法

一、套接字及创建

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_TCP,IPPROTO_UDP

2. 创建套接字

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报头的协议字段中的数字相同。(有关详细信息,请参见手动协议)

二、端口绑定

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

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

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;

}

三、收发信息

1. read() 和write()

#include

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函数接收到的数据;

参数三:指明buf的长度;

参数四 :一般置为0。

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

与send()函数的比较:

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

send函数

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

参数一:指定发送端套接字描述符;

参数二:存放应用程序要发送数据的缓冲区;

参数三:实际要发送的数据的字节数;

参数四:一般置为0。

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

实例

1. TCP通信的简单实现

TCP_Server.cpp

//

// Created by AirCL on 2024/8/12.

//

#include

#include

#include

#include

#pragma warning(disable: 4996)

#pragma comment(lib, "ws2_32.lib")

#define USER_ERROR -1

using namespace std;

void space2_(char* str){

for(unsigned int i = 0; i < strlen(str); i++){

if (str[i] == ' '){

str[i] = '_';

}

}

}

void printTime(){

time_t curTime;

time(&curTime);

char* now = ctime(&curTime);

cout << "[" << now << "]" << endl;

}

void printTime(in_addr addr){

time_t curTime;

time(&curTime);

char* now = ctime(&curTime);

cout << "[" << now << "](From " << inet_ntoa(addr) << ")" << endl;

}

void interactive(SOCKET socketOfClient, struct sockaddr_in cSin){

char recvData[200];

char sendData[200];

printTime();

cout << "接收到一个连接:" << inet_ntoa(cSin.sin_addr) << endl;

while (true){

// 读取消息

int ret = recv(socketOfClient, recvData, 200, 0);

if (ret < 0){

printTime();

cout << "Something wrong!" << endl;

continue;

}

if (!strcmp(recvData, "quit")){

break;

}

printTime(cSin.sin_addr);

cout << "读取消息:" << recvData << endl;

cout << "请发送消息:";

gets(sendData);

printTime();

cout << "发送消息:" << sendData << endl;

send(socketOfClient, sendData, 199, 0);

if (!strcmp(sendData, "quit")){

break;

}

}

}

int main(){

WSADATA wsaData;

// 打开网络库、启动网络库,启动了这个库,这个库里的函数才能使用

if (WSAStartup(MAKEWORD(2, 2), &wsaData)){

cout << "Failed to load WinSock" << endl;

return USER_ERROR;

} else{

cout << "Load WinSock success" << endl;

}

// 创建socket

SOCKET socketOfServer;

SOCKET socketOfClient;

socketOfServer = socket(AF_INET, SOCK_STREAM, 0);

// 确保socket创建成功

if (socketOfServer == INVALID_SOCKET){

cout << "socket() Failed: " << WSAGetLastError() << endl;

return USER_ERROR;

} else{

cout << "socket() Success" << endl;

}

// 填写套接字信息

struct sockaddr_in sSin;

memset(&sSin, 0, sizeof(sockaddr_in));

sSin.sin_family = AF_INET;

sSin.sin_port = htons(20000);

sSin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

//将服务器的socket和服务器的ip地址和端口绑定

if (bind(socketOfServer, (struct sockaddr*)& sSin, sizeof(sSin)) == SOCKET_ERROR){

cout << "bind() Failed: " << WSAGetLastError() << endl;

return USER_ERROR;

} else{

cout << "bind() Success" << endl;

}

// 启动监听

if (listen(socketOfServer, 3) == SOCKET_ERROR){

printTime();

cout << "listen() Failed: " << WSAGetLastError() << endl;

return USER_ERROR;

} else{

cout << "listen() Success" << endl;

}

struct sockaddr_in cSin;

int cSinLen = sizeof(struct sockaddr_in);

// 不断监听,直到接收到客户端的连接

cout << "================Waiting for connect .. ==================" << endl;

while (true){

// 建立tcp连接

socketOfClient = accept(socketOfServer, (struct sockaddr *)& cSin, &cSinLen);

if (socketOfClient == INVALID_SOCKET){

cout << "accept() Failed: " << WSAGetLastError() << endl;

printTime();

} else{

interactive(socketOfClient, cSin);

}

}

closesocket(socketOfServer);

WSACleanup();

return 0;

}

TCP_Client.cpp

//

// Created by AirCL on 2024/8/12.

//

#include

#include

#include

#include

#include

#pragma warning(disable: 4996)

#pragma comment(lib, "ws2_32.lib")

#define USER_ERROR -1

using namespace std;

void printTime(){

time_t curTime;

time(&curTime);

char* now = ctime(&curTime);

cout << "[" << now << "]" << endl;

}

void printTimeFrom(in_addr addr){

time_t curTime;

time(&curTime);

char* now = ctime(&curTime);

cout << "[" << now << "](From " << inet_ntoa(addr) << endl;

}

void printTimeTo(in_addr addr){

time_t curTime;

time(&curTime);

char* now = ctime(&curTime);

cout << "[" << now << "](To " << inet_ntoa(addr) << endl;

}

int main(){

char recvData[200];

char sendData[200];

int ret;

// 启动套接字编程,不启动无法运行相关API

WSADATA wsaData;

if (WSAStartup(MAKEWORD(2, 2), &wsaData)){

cout << "Failed to load WinSock" << endl;

return USER_ERROR;

} else{

cout << "Load WinSock Success" << endl;

}

// 创建套接字

SOCKET socketOfClient = socket(AF_INET, SOCK_STREAM, 0);

if (socketOfClient == INVALID_SOCKET){

cout << "Failed socket()" << endl;

return USER_ERROR;

} else{

cout << "Socket() Success" << endl;

}

// 设置套接字信息

struct sockaddr_in serverIn;

serverIn.sin_family = AF_INET;

serverIn.sin_port = htons(20000);

serverIn.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

cout << "===================Trying to connect...====================" << endl;

// 连接服务器

if (connect(socketOfClient, (struct sockaddr*)&serverIn, sizeof(serverIn)) == -1){

cout << "Failed to connect()" << endl;

return USER_ERROR;

} else{

printTime();

cout << "connect to " << inet_ntoa(serverIn.sin_addr) << ":" << serverIn.sin_port << endl;

while (true){

memset(recvData, 0, sizeof(recvData));

memset(sendData, 0, sizeof(sendData));

cout << "请发送消息:" << endl;

gets(sendData);

printTimeTo(serverIn.sin_addr);

cout << "发送消息:" << sendData << endl;

send(socketOfClient, sendData, strlen(sendData), 0);

if (!strcmp(sendData, "quit")){

break;

}

// 接受消息

ret = recv(socketOfClient, recvData, 200, 0);

printTimeFrom(serverIn.sin_addr);

cout << "读取消息:" << recvData << endl;

}

}

closesocket(socketOfClient);

WSACleanup();

return 0;

}

我们实现了两台机子的互相通信,能不能实现多个机子之间的互相通信呢?仔细想想,这就涉及到同时干多件事情的操作了。因此需要开多线程进行程序的处理。

TCP Server.cpp (多线程)

//server.cpp

#include

#include

#include

#include

#include

#include

#pragma warning(disable : 4996)

#pragma comment(lib, "ws2_32.lib")

#define USER_ERROR -1

using namespace std;

void space2_(char* str);

void print_time();

void print_time_from(in_addr addr);

void print_time_to(in_addr addr);

bool GetAddressBySocket(SOCKET m_socket, SOCKADDR_IN& m_address);

void delete_last_line();

//用于交互

DWORD WINAPI interactive(LPVOID lpThreadParameter);

int main()

{

WSADATA wsaData;

// 打开网络库/启动网络库,启动了这个库,这个库里的函数/功能才能使用

if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)

{

printf("Failed to load Winsock.\\n");

return USER_ERROR;

}

SOCKET socket_of_server;

SOCKET socket_of_client;

//part1 创建socket

socket_of_server = socket(AF_INET, SOCK_STREAM, 0);

//part1 end

// 确保socker创建成功

if (socket_of_server == INVALID_SOCKET)

{

printf("socket() Failed:%d\\n", WSAGetLastError());

return USER_ERROR;

}

//part2 填写套接字信息

struct sockaddr_in s_sin;

s_sin.sin_family = AF_INET;

s_sin.sin_port = htons(20000);

s_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

//part2 end

//part3 将服务器的socket和服务器的ip地址和端口号绑定

if (bind(socket_of_server, (struct sockaddr*)&s_sin, sizeof(s_sin)) == SOCKET_ERROR)

{

printf("blind() Failed:%d\\n", WSAGetLastError());

return USER_ERROR;

}

//part4 启动监听

if (listen(socket_of_server, 3) == SOCKET_ERROR)

//part4 end

{

print_time();

printf("listen() Failed:%d\\n", WSAGetLastError());

return USER_ERROR;

}

struct sockaddr_in c_sin;

int c_sin_len = sizeof(struct sockaddr_in);

// 不断监听,直到接收到客户端的连接

printf("==========Waiting for connect...==========\\n");

while (1)

{

//part5 建立tcp连接

socket_of_client = accept(socket_of_server, (struct sockaddr*)&c_sin, &c_sin_len);

//part5 end

if (socket_of_client == INVALID_SOCKET)

{

printf("accept() Failed:%d", WSAGetLastError());

print_time();

}

else

{

HANDLE hThread = CreateThread(NULL, 0, interactive, (LPVOID)socket_of_client, 0,NULL);

}

}

closesocket(socket_of_server);

WSACleanup();

return 0;

}

void space2_(char* str)

{

for (auto i = 0; i < strlen(str); i++)

{

if (str[i] == ' ')

{

str[i] = '_';

}

}

}

void print_time() {

time_t cur_time;

time(&cur_time);

char* now = ctime(&cur_time);

now[24] = '\\0';

printf("[%s]", now);

}

void print_time_from(in_addr addr) {

time_t cur_time;

time(&cur_time);

char* now = ctime(&cur_time);

now[24] = '\\0';

printf("[%s](From %s):", now, inet_ntoa(addr));

}

void print_time_to(in_addr addr) {

time_t cur_time;

time(&cur_time);

char* now = ctime(&cur_time);

now[24] = '\\0';

printf("[%s](To %s):", now, inet_ntoa(addr));

}

DWORD WINAPI interactive(LPVOID lpThreadParameter)

{

char recvData[200];

char sendData[200];

SOCKADDR_IN c_sin;

print_time();

SOCKET socket_of_client = (SOCKET)lpThreadParameter;

GetAddressBySocket(socket_of_client,c_sin);

printf("接收到一个连接:IP(%s)Port(%d)\\r\\n", inet_ntoa(c_sin.sin_addr), ntohs(c_sin.sin_port));

while (1)

{

recvData[0] = '\\0';

//part6 读取消息

int ret = recv(socket_of_client, recvData, 199, 0);

//part6 end

if (ret < 0)

{

//m.lock();

print_time();

printf("***Something wrong***\\n");

//m.unlock();

continue;

}

recvData[ret] = '\\0';

// 如果客户端发送了quit,那么就退出

if (strcmp(recvData, "quit") == 0)

break;

//m.lock();

print_time_from(c_sin.sin_addr);

printf("读取消息:【%s】\\n",recvData);

//printf("%s\\n", recvData);

printf("请发送消息:");

gets_s(sendData);

// space2_(sendData);

delete_last_line();

print_time_to(c_sin.sin_addr);

printf("发送消息:【%s】\\n", sendData);

//printf("\\n");

//m.unlock();

//part7 发送消息

send(socket_of_client, sendData, 199, 0);

//part7 end

if (strcmp("quit", sendData) == 0)

break;

}

}

//通过套接字获取IP、Port等地址信息

bool GetAddressBySocket(SOCKET m_socket, SOCKADDR_IN& m_address)

{

memset(&m_address, 0,sizeof(m_address));

int nAddrLen = sizeof(m_address);

//根据套接字获取地址信息

if (::getpeername(m_socket, (SOCKADDR*)&m_address, &nAddrLen) != 0)

{

//printf("Get IP address by socket Failed!n");

return false;

}

//读取IP和Port

//cout << "IP: " << ::inet_ntoa(m_address.sin_addr) << " PORT: " << ntohs(m_address.sin_port) << endl;

return true;

}

void delete_last_line()

{

printf("\\033[1A"); //先回到上一行

printf("\\033[K"); //清除该行

printf("\\r \\r");

}

2. UDP通信的简单实现

UDP_Server.cpp

//UDP_server.cpp

#include

#include

#include

#include

#pragma warning(disable : 4996)

#pragma comment(lib, "ws2_32.lib")

#define USER_ERROR -1

using namespace std;

void space2_(char *str);

void print_time();

void print_time(in_addr addr);

int main()

{

char recvData[200];

char sendData[200];

WSADATA wsaData;

// 打开网络库/启动网络库,启动了这个库,这个库里的函数/功能才能使用

if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)

{

printf("Failed to load Winsock.\\n");

return USER_ERROR;

}

SOCKET socket_of_server;

//part1 创建socket

socket_of_server = socket(AF_INET, SOCK_DGRAM, 0);

//part1 end

// 确保socker创建成功

if (socket_of_server == INVALID_SOCKET)

{

printf("socket() Failed:%d\\n", WSAGetLastError());

return USER_ERROR;

}

//part2 填写套接字信息

struct sockaddr_in server_in;

server_in.sin_family = AF_INET;

server_in.sin_port = htons(20000);

server_in.sin_addr.S_un.S_addr =inet_addr("10.129.46.156");

//part2 end

//part3 将服务器的socket和服务器的ip地址和端口号绑定

if (bind(socket_of_server, (struct sockaddr *)&server_in, sizeof(server_in)) == SOCKET_ERROR)

{

printf("blind() Failed:%d\\n", WSAGetLastError());

return USER_ERROR;

}

struct sockaddr_in dest_addr;

int len = sizeof(struct sockaddr_in);

printf("——————————聊天室已经启动——————————\\n");

while (1)

{

memset(recvData,'\\0',sizeof(recvData));

memset(sendData,'\\0',sizeof(sendData));

recvfrom(socket_of_server,recvData,200,0,(struct sockaddr *)&dest_addr,&len);

print_time(dest_addr.sin_addr);

printf("收到消息:%s\\n",recvData);

printf("输入要传输的信息:");

gets(sendData);

printf("\\033[1A"); //先回到上一行

printf("\\033[K"); //清除该行

print_time();

printf("发送消息:%s\\n",sendData);

sendto(socket_of_server,sendData,strlen(sendData),0,(struct sockaddr *)&dest_addr,len);

}

closesocket(socket_of_server);

WSACleanup();

return 0;

}

void space2_(char *str)

{

for(unsigned int i=0; i

{

if(str[i] == ' ')

{

str[i] = '_';

}

}

}

void print_time(){

time_t cur_time;

time(&cur_time);

char *now=ctime(&cur_time);

now[24]='\\0';

printf("[%s]",now);

}

void print_time(in_addr addr){

time_t cur_time;

time(&cur_time);

char *now=ctime(&cur_time);

now[24]='\\0';

printf("[%s](From %s):",now,inet_ntoa(addr));

}

UDP_Client.cpp

//UDP_client.cpp

#include

#include

#include

#include

#include

#include

#pragma comment(lib, "ws2_32.lib")

#pragma warning(disable : 4996)

#define USER_ERROR -1

void print_time_from(in_addr addr);

void print_time_to(in_addr addr);

void print_time();

void print_time(in_addr addr);

int main()

{

// time_t now;

// char* curr_time = time(&now);

char recvData[200];

char sendData[200];

//启动套接字编程,不启动无法运行相关API

WSADATA wsaData;

if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)

{

printf("Failed to load Winsock.\\n");

return USER_ERROR;

}

//part1 创建套接字

SOCKET socket_client = socket(AF_INET, SOCK_DGRAM, 0);

//end part1

if (socket_client == INVALID_SOCKET)

{

printf(" Failed socket() \\n");

return 0;

}

//part2 设置套接字信息 这里是需要

struct sockaddr_in client_in;

client_in.sin_port =htons(20001);

client_in.sin_addr.S_un.S_addr = inet_addr("10.128.18.146");

client_in.sin_family =AF_INET;

//part2 end

//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;

}

struct sockaddr_in dest_addr;

dest_addr.sin_family = AF_INET;

dest_addr.sin_port = htons(20000);

dest_addr.sin_addr.s_addr = inet_addr("10.129.46.156");

int len = sizeof(dest_addr);

printf("——————————聊天室已经启动——————————\\n");

while(1)

{

memset(recvData,'\\0',sizeof(recvData));

memset(sendData,'\\0',sizeof(sendData));

printf("输入要传输的信息:");

gets(sendData);

printf("\\033[1A"); //先回到上一行

printf("\\033[K"); //清除该行

print_time_to(dest_addr.sin_addr);

printf("发送消息:%s\\n",sendData);

sendto(socket_client,sendData,strlen(sendData),0,(struct sockaddr *)&dest_addr,sizeof(dest_addr));

recvfrom(socket_client,recvData,200,0,(struct sockaddr *)&dest_addr,&len);

print_time_from(dest_addr.sin_addr);

printf("收到消息:%s\\n",recvData);

}

closesocket(socket_client);

WSACleanup();

return 0;

}

void print_time(){

time_t cur_time;

time(&cur_time);

char *now=ctime(&cur_time);

now[24]='\\0';

printf("[%s]",now);

}

void print_time(in_addr addr){

time_t cur_time;

time(&cur_time);

char *now=ctime(&cur_time);

now[24]='\\0';

printf("[%s](From %s):",now,inet_ntoa(addr));

}

void print_time_from(in_addr addr) {

time_t cur_time;

time(&cur_time);

char* now = ctime(&cur_time);

now[24] = '\\0';

printf("[%s](From %s):", now, inet_ntoa(addr));

}

void print_time_to(in_addr addr) {

time_t cur_time;

time(&cur_time);

char* now = ctime(&cur_time);

now[24] = '\\0';

printf("[%s](To %s):", now, inet_ntoa(addr));

}

sockaddr和sockaddr_in详解

struct sockaddr 和 struct sockaddr_in 这两个结构体用来处理网络通信的地址。

1. sockaddr

sockaddr在头文件#include 中定义,sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了,如下:

struct sockaddr

{

unsigned short sa_family;//2字节,地址族,AF_xxx

char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息

};

2. sockaddr_in

sockaddr_in在头文件#include或#include 中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下:

struct in_addr {

unsigned long s_addr; // 32位IPV4地址打印的时候可以调用inet_ntoa()函数将其转换为char *类型.

};

struct sockaddr_in {

short sin_family; // 2 字节 ,地址族,e.g. AF_INET, AF_INET6

unsigned short sin_port; // 2 字节 ,16位TCP/UDP 端口号 e.g. htons(3490),

struct in_addr sin_addr; // 4 字节 ,32位IP地址

char sin_zero[8]; // 8 字节 ,不使用

};

sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序(HBO)。

3. 总结

二者长度一样,都是16个字节,即占用的内存大小是一致的,因此可以互相转化。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。

sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。

sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数。

注释中标明了属性的含义及其字节大小,这两个结构体一样大,都是16个字节,而且都有family属性,不同的是:

sockaddr用其余14个字节来表示sa_data,而sockaddr_in把14个字节拆分成sin_port, sin_addr和sin_zero分别表示端口、ip地址。sin_zero用来填充字节使sockaddr_in和sockaddr保持一样大小。

sockaddr和sockaddr_in包含的数据都是一样的,但他们在使用上有区别:

程序员不应操作sockaddr,sockaddr是给操作系统用的

程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便。

一般的用法为:

程序员把类型、ip地址、端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给系统调用函数

4. 用法

//创建sockaddr_in结构体变量

struct sockaddr_in serv_addr;

memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充

serv_addr.sin_family = AF_INET; //使用IPv4地址

serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址

serv_addr.sin_port = htons(1234); //端口号

Copyright © 2088 世界杯乒乓球赛_2014世界杯十佳球 - mz286.com All Rights Reserved.
友情链接