记录一下基本的socket编程
首先贴几段代码
centos下的server代码
#include<bits/stdc++.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
using namespace std;
int main()
{
int server,client;
struct sockaddr_in serverAddr,clientAddr;
server = socket(AF_INET,SOCK_STREAM,0); //首先创建套接字
if(server==-1)
{
cout<<"socket error"<<endl;
return 0;
}
memset(&serverAddr,0,sizeof(serverAddr)); //手动清零地址结构体
serverAddr.sin_family = AF_INET; //清零后重新设置结构体
serverAddr.sin_port = htons(9999);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t len_sockaddr = sizeof(serverAddr);
if(bind(server,(struct sockaddr*)&serverAddr,len_sockaddr)==-1) //把套接字和结构体相绑定。为什么这里第3个参数是整形呢,猜想是因为设定第二个参数是大小可变的结构体,所以需要手动告知结构体的大小
{
cout<<"bind error"<<endl;
return 0;
}
if(listen(server,5)==-1) //绑定以后就开始监听端口
{
cout<<"listen error"<<endl;
return 0;
}
cout<<"size1 = "<<len_sockaddr<<endl;
len_sockaddr = 0;
client = accept(server,(struct sockaddr*)&clientAddr,&len_sockaddr); //这个服务器接收连接信息,返回值对应抽象的套接字,结构体对应客户的实际地址,第三个参数是指针,猜想是为了保存客户结构体的大小。
cout<<"size2 = "<<len_sockaddr<<endl;
if(client==-1)
{
cout<<"accept error"<<endl;
return 0;
}
char data[] = "this is server.";
write(client,data,sizeof(data)); //写数据,和写文件没啥区别
close(client);
close(server);
}
//g++ server.cpp -std=c++11
centos下的client代码
#include<bits/stdc++.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
using namespace std;
int main()
{
int client;
struct sockaddr_in serverAddr;
client = socket(AF_INET,SOCK_STREAM,0);
if(client==-1)
{
cout<<"socket error"<<endl;
return 0;
}
memset(&serverAddr,0,sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(9999);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t len_sockaddr = sizeof(serverAddr) + 1;
len_sockaddr = 100;
//client = connect(client,(struct sockaddr*)&serverAddr,len_sockaddr);
if(connect(client,(struct sockaddr*)&serverAddr,len_sockaddr)==-1) //第三个参数是为了告知结构体的大小,至少要大于结构体本来的大小。
{
cout<<"connect error"<<endl;
return 0;
}
char data[24];
read(client,data,sizeof(data));
cout<<"data = "<<data<<endl;
close(client);
}
//g++ client.cpp -std=c++11
Win10下的server代码
#include<bits/stdc++.h>
#include<winsock2.h>
using namespace std;
int main()
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) //windows下必须首先调用
{
cout<<"wsastartup error"<<endl;
return 0;
}
SOCKET server,client;
sockaddr_in serverAddr,clientAddr;
server = socket(AF_INET,SOCK_STREAM,0); //首先创建套接字
if(server==-1)
{
cout<<"socket error"<<endl;
return 0;
}
memset(&serverAddr,0,sizeof(serverAddr)); //手动清零地址结构体
serverAddr.sin_family = AF_INET; //清零后重新设置结构体
serverAddr.sin_port = htons(9999);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int len_sockaddr = sizeof(serverAddr);
if(bind(server,(SOCKADDR*)&serverAddr,len_sockaddr)==-1) //把套接字和结构体相绑定。为什么这里第3个参数是整形呢,猜想是因为设定第二个参数是大小可变的结构体,所以需要手动告知结构体的大小
{
cout<<"bind error"<<endl;
cout<<GetLastError()<<endl;
return 0;
}
if(listen(server,5)==-1) //绑定以后就开始监听端口
{
cout<<"listen error"<<endl;
return 0;
}
cout<<"size1 = "<<len_sockaddr<<endl;
//len_sockaddr = 0;
client = accept(server,(SOCKADDR*)&clientAddr,&len_sockaddr); //这个服务器接收连接信息,返回值对应抽象的套接字,结构体对应客户的实际地址,第三个参数是指针,猜想是为了保存客户结构体的大小。
cout<<"size2 = "<<len_sockaddr<<endl;
if(client==-1)
{
cout<<"accept error"<<endl;
return 0;
}
char data[] = "this is server.";
send(client,data,sizeof(data),0); //写数据,和写文件没啥区别
closesocket(client);
closesocket(server);
WSACleanup(); //windows下结束时必须调用
}
Win10下的client代码
#include<bits/stdc++.h>
#include<winsock2.h>
using namespace std;
int main()
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) //windows下必须首先调用
{
cout<<"wsastartup error"<<endl;
return 0;
}
SOCKET client;
sockaddr_in serverAddr;
client = socket(AF_INET,SOCK_STREAM,0);
if(client==-1)
{
cout<<"socket error"<<endl;
return 0;
}
memset(&serverAddr,0,sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(9999);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int len_sockaddr = sizeof(serverAddr) + 1;
len_sockaddr = 100;
//client = connect(client,(struct sockaddr*)&serverAddr,len_sockaddr);
if(connect(client,(SOCKADDR*)&serverAddr,len_sockaddr)==-1) //第三个参数是为了告知结构体的大小,至少要大于结构体本来的大小。
{
cout<<"connect error"<<endl;
return 0;
}
char data[24];
recv(client,data,sizeof(data),0);
cout<<"data = "<<data<<endl;
closesocket(client);
WSACleanup(); //windows下结束时必须调用
}
windows下本人用的dev进行编译,没有用VS,不过效果应该是类似的。
下面针对这些API和一些宏定义进行记录
int socket(PF_INET,SOCK_STREAM,0)
win和linux下,返回值都可以认为是整形,只不过win下多了个宏定义。
linux下失败返回-1,win下失败返回INVALID_SOCKET
用计网的概念来解释这个函数:
第一个参数固定为PF_INET(AF_INET),二者等价。即选择IPv4。
第二个参数可以选择SOCK_STREAM/SOCK_DGRAM/SOCK_RAW。一般使用前两个即可,即选择TCP还是UDP(可靠或者不可靠,面向连接或者无连接),最后一个原始套接字可用于自己实现一些协议。
第三个套接字,一般填0即可。当然,你也可以根据自己的需求,选择相应的协议。
总的来说,就是自己设定网络层和运输层的协议。
int bind(sock_serv,(struct sockaddr*)&addr_serv,sizeof(addr_serv))
win和linux还是类似,win下仍然是宏定义,只是win下的SOCKET_ERROR代表出错,实质还是整数-1。
这个函数,用大白话理解:把套接字绑定到具体端口地址,抽象的对象绑定到实体
int listen(sock_serv,5)
第一个参数为服务器的套接字,第二个参数代表最大连接数量
int connect(sock,(struct sockaddr*)&addr_serv,sizeof(addr_serv))
这是客户端才调用的函数,含义为,这个抽象的套接字连接到实体的端口地址上
int accept(sock_serv,(struct sockaddr*)&addr_clnt,&len_sockaddr)
大白话理解:服务器接收一个连接,所以要保存客户的实体地址(结构体)和客户抽象的套接字(返回值)
int write(sock_clnt,message,sizeof(message));
int read(sock,message,sizeof(message)-1);
linux下套接字读写都是和文件一样的操作:写给哪个对象,写什么数据,写多少个数据;从哪儿读,读到哪儿,读多少。
int send(clnt,message,sizeof(message),0)
int recv(sock,message,sizeof(message)-1,0)
win下多了个参数,一般是用不上。其他的类似于linux下。
socket编程,涉及到CPU内存布局,即大小端的区别。可见
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
in_addr_t inet_addr(const char *cp)
h代表host,n代表network,s代表short,l代表long。结合上面的代码,应该很容易理解这几个函数的意思。其中inet_addr就是把字符串格式的ip地址转换为32位的IP地址。
结构体
struct sockaddr_in
{
short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/
unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/
};
linux下:
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
windows下:
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;
}in_addr;
以下为UDP的一些API
//发送
int FAR sendto (
IN SOCKET s,
IN const char FAR * buf,
IN int len,
IN int flags,
IN const struct sockaddr FAR *to,
IN int tolen
);
//接收
ssize_t recvfrom(int sockfd,void *buf,size_t len,unsigned int flags, struct sockaddr *from,socklen_t *fromlen)
int shutdown(int sock,int how);
SHUT_RD:断开输入流。套接字无法接收数据(即使缓冲区收到数据也会被清除),无法调用输入相关函数。
SHUT_WD:断开输出流。套接字无法发送数据,但如果输出缓冲区中还有未传输的数据,则将传递到目标主机。
SHUT_RDWR:同时断开I/O流。相当于分两次调用shutdown(),其中一次以SHUT_RD为参数,另一次以SHUT_WR为参数
Win下第2个参数,略有不同。
SD_RECEIVE 断开输入
SD_SEND 断开输出
SD_BOTH 断开IO
断开连接,使socket变成单向,只能发送或者只能接收。显然,这是有连接的套接字才能使用。
centos下的udpServer代码
#include<bits/stdc++.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
using namespace std;
#define SIZE 64
int main()
{
int sock_serv,len_str;
socklen_t len_sockaddr;
struct sockaddr_in addr_serv,addr_clnt;
char message[SIZE];
sock_serv = socket(PF_INET,SOCK_DGRAM,0); //首先创建套接字
if(sock_serv == -1)
{
cout<<"socket error"<<endl;
return 0;
}
int port = 8888;
memset(&addr_serv,0,sizeof(addr_serv));//必须手动清零结构体
addr_serv.sin_family = AF_INET; //设置结构体的数据
addr_serv.sin_addr.s_addr = htonl(INADDR_ANY);
addr_serv.sin_port = htons(port);
if(bind(sock_serv,(struct sockaddr*)&addr_serv,sizeof(addr_serv))==-1) //绑定一个套接字
{
cout<<"bind error"<<endl;
return 0;
}
while(true)
{
len_sockaddr = sizeof(addr_clnt);
len_str = recvfrom(sock_serv,message,SIZE,0,(struct sockaddr*)&addr_clnt,&len_sockaddr);
cout<<"recv message "<<message<<endl;
sendto(sock_serv,message,len_str,0,(struct sockaddr*)&addr_clnt,len_sockaddr);
}
close(sock_serv); //关闭套接字
return 0;
}
centos下udpclient代码
#include<bits/stdc++.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
using namespace std;
#define SIZE 64
int main()
{
int sock;
struct sockaddr_in addr_serv,addr_from;
char message[SIZE];
int len_read;
socklen_t len_addr;
sock = socket(PF_INET,SOCK_DGRAM,0); //首先创建套接字
if(sock == -1)
{
cout<<"socket error";
return 0;
}
memset(&addr_serv,0,sizeof(addr_serv)); //手动清零结构体
addr_serv.sin_family = AF_INET; //设置套接字连接的对象
addr_serv.sin_addr.s_addr = inet_addr("192.168.0.118");
addr_serv.sin_port=htons(8888);
while(true)
{
fputs("输入数据,输入q退出\n",stdout);
fgets(message,sizeof(message),stdin);
if(!strcmp(message,"q\n"))
{
break;
}
sendto(sock,message,strlen(message),0,(struct sockaddr*)&addr_serv,sizeof(addr_serv));
len_addr = sizeof(addr_from);
len_read = recvfrom(sock,message,SIZE,0,(struct sockaddr*)&addr_from,&len_addr);
message[len_read] = 0;
cout<<"message from server: "<<message<<endl;
}
close(sock); //关闭套接字
return 0;
}
win下的udpserver代码
#include<bits/stdc++.h>
#include<winsock2.h>
using namespace std;
#define SIZE 64
int main()
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) //windows下必须首先调用
{
cout<<"wsastartup error"<<endl;
return 0;
}
int sock_serv,len_str;
int len_sockaddr;
struct sockaddr_in addr_serv,addr_clnt;
char message[SIZE];
sock_serv = socket(PF_INET,SOCK_DGRAM,0); //首先创建套接字
if(sock_serv == -1)
{
cout<<"socket error"<<endl;
return 0;
}
int port = 8888;
memset(&addr_serv,0,sizeof(addr_serv));//必须手动清零结构体
addr_serv.sin_family = AF_INET; //设置结构体的数据
addr_serv.sin_addr.s_addr = htonl(INADDR_ANY);
addr_serv.sin_port = htons(port);
if(bind(sock_serv,(struct sockaddr*)&addr_serv,sizeof(addr_serv))==-1) //绑定一个套接字
{
cout<<"bind error"<<endl;
return 0;
}
while(true)
{
len_sockaddr = sizeof(addr_clnt);
len_str = recvfrom(sock_serv,message,SIZE,0,(struct sockaddr*)&addr_clnt,&len_sockaddr);
cout<<"recv message "<<message<<endl;
sendto(sock_serv,message,len_str,0,(struct sockaddr*)&addr_clnt,len_sockaddr);
}
closesocket(sock_serv); //关闭套接字
WSACleanup(); //windows下结束时必须调用
return 0;
}
Win下的udpclient代码
#include<bits/stdc++.h>
#include<winsock2.h>
using namespace std;
#define SIZE 64
int main()
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) //windows下必须首先调用
{
cout<<"wsastartup error"<<endl;
return 0;
}
int sock;
struct sockaddr_in addr_serv,addr_from;
char message[SIZE];
int len_read;
int len_addr;
sock = socket(PF_INET,SOCK_DGRAM,0); //首先创建套接字
if(sock == -1)
{
cout<<"socket error";
return 0;
}
memset(&addr_serv,0,sizeof(addr_serv)); //手动清零结构体
addr_serv.sin_family = AF_INET; //设置套接字连接的对象
addr_serv.sin_addr.s_addr = inet_addr("192.168.0.118");
addr_serv.sin_port=htons(8888);
while(true)
{
fputs("输入数据,输入q退出\n",stdout);
fgets(message,sizeof(message),stdin);
if(!strcmp(message,"q\n"))
{
break;
}
sendto(sock,message,strlen(message),0,(struct sockaddr*)&addr_serv,sizeof(addr_serv));
len_addr = sizeof(addr_from);
len_read = recvfrom(sock,message,SIZE,0,(struct sockaddr*)&addr_from,&len_addr);
message[len_read] = 0;
cout<<"message from server: "<<message<<endl;
}
closesocket(sock); //关闭套接字
WSACleanup(); //windows下结束时必须调用
return 0;
}
域名解析和IP地址反查域名
#include<bits/stdc++.h>
#include<winsock2.h> //windows下需要
//linux下需要
//#include<netdb.h>
//#include<arpa/inet.h>
using namespace std;
int main()
{
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) //windows下必须首先调用
{
cout<<"wsastartup error"<<endl;
return 0;
}
/*struct hostent host = *(gethostbyname("www.baidu.com")); //根据域名获取IP地址
cout<<host.h_name<<endl;
cout<<host.h_aliases[0]<<endl;
cout<<host.h_addrtype<<endl;
cout<<host.h_length<<endl;
cout<<inet_ntoa(*(struct in_addr*)host.h_addr_list[0])<<endl;*/
cout<<"after"<<endl;
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
ULONG ip = inet_addr("127.0.0.1");
struct hostent* host1;
//host1 = gethostbyaddr((char*)&addr.sin_addr,4,AF_INET);
host1 = gethostbyaddr((char*)&ip,4,AF_INET); //两种写法可以
if(!host1)
{
cout<<"error"<<endl;
cout<<GetLastError()<<endl; //11004,一直返回这个,后来搜索了一下,gethostbyaddr大概只能查询本地hosts文件里面的内容吧
return 0;
}
cout<<host1->h_name<<endl;
cout<<host1->h_aliases[0]<<endl;
cout<<host1->h_addrtype<<endl;
cout<<host1->h_length<<endl;
cout<<inet_ntoa(*(struct in_addr*)host1->h_addr_list[0])<<endl;
WSACleanup(); //windows下结束时必须调用
}
//linux和windows相比,也就头文件不太一样。还有win下开头和结尾多了对winsock的调用。
struct hostent
{
char *h_name; //正式主机名
char **h_aliases; //主机别名
int h_addrtype; //主机IP地址类型:IPV4-AF_INET
int h_length; //主机IP地址字节长度,对于IPv4是四字节,即32位
char **h_addr_list; //主机的IP地址列表
};
#define h_addr h_addr_list[0] //保存的是IP地址
关闭Linux系统的防火墙,否则Windows不能够连接Linux
(1) 重启后永久性生效: 开启:chkconfig iptables on 关闭:chkconfig iptables off
(2) 即时生效,重启后失效: 开启:service iptables start 关闭:service iptables stop
标签:addr,记录,int,编程,sock,struct,接字,include,SOCKET
From: https://www.cnblogs.com/dayq/p/17429330.html