Socket编程技巧
(一)Socket编程的重要数据结构
1、网络字节顺序与主机字节顺序
两种主机字节顺序:低位先存,高位先存(根据机器的不同和有区别,目前的INTER主机为低位先存方式)
网络字节顺序:对于16位或32位整数TCP/IP要求高位先存
相关转换函数:
htons()--"Host to Network Short";
htonl()--"Host to Network Long";
ntohs()--"Network to Host Short";
ntohl()--"Network to Host Long";
2、地址结构
(1)struct sockaddr //套接字地址结构
{
unsigned short sa_family; //internet协议族AF_INET
char sa_data[14]; //14字节的协议地址,包括IP地址和端口
};
(2)struct sockaddr_in //in 代表internet
{
short int sin_family; //internet协议族=AF_INET,IPV4; =AF_INET6, IPV6
unsigned short int sin_port; //端口号,必须是网络字节顺序调用htons()函数
struct in_addr sin_addr; //internet地址,必须是网络字节顺序调用htonl()函数
unsigned char sin_zero[8] //填0
};
(3)struct in_addr //IPv4地址,4个字节
{
unsigned long s_addr;
};
(二)Socket编程中几个重要的函数
(1)socket():套接字创建—指定协议一元
int socket(int domain,int type,int protocal)
int domain, //通信域AF_INET—internet域;AF_UNIX—UNIX域
int type, //指定socket的类型:SOCK_STREAM(流式套接字,TCP),SOCK_DGRAM(数据报套接字,UDP),SOCK_RAW(原始数据报套接字)
int protocol //指定网络协议,0表示由系统选择合适协议,默认TCP/IP协议
返回值:成功---整型socket号sockfd ,失败--- -1
例子:socket(AF_INET, SOCK_STREAM,0)
socket(AF_INET, SOCK_ DGRAM ,0)
(2)bind():命名套接字---指定本地二元,绑定套接字号
int bind(int sockfd,struct sockaddr *my_addr,int addrlen)
int sockfd, //socket描述符
struct sockaddr *my_addr, //指向包含有本机IP地址及端口号等信息的sockaddr类型的指针
int addrlen, //sizeof(struct sockaddr)
返回值:成功----0; 失败---- <0,SOCKET_ERROR错误
作用:将套接字地址与套接字号关联----指点了三元组套接字标识
例子:bind(18, my_addr, sizeof(struct sockaddr))
my_addr.sin_port= htonl (2222); // =0,系统随机设定1024~5000;自己设定,应选>1024
my_addr.sin_addr.s_addr=htonl(INADDR_ANY);//允许服务器应用进程侦听每个网络接口上的客户机请求.
(3)listen():监听连接---服务程序监听连接请求,并进行排队,供accept进行后续处理.
int listen(int sockfd,int backlog)
int sockfd, //服务器通过该套接字侦听客户请求
int backlog //请求队列长度,超过长度,服务器拒绝连接请求
返回值:0--成功,-1--失败
(4)accept()允许服务器通过套接字接收一个连接请求—服务程序使用
作用:从listen队列取出第一个连接请求,创建一新的套接字,准备提供并发服务.
int accept(ing sockfd,struct sockaddr *addr,int *addrlen)
int sockfd, //被监听的socket号,服务器从该套接字接收connect请求
struct sockaddr * addr, //存放提出连接请求服务的客户机套接字地址
int *addrlen //客户机套接字地址addr长度,sizeof(sockaddr)
返回值:若成功建立一个连接,返回的是一个新的socekt号,供新的连接使用;父进程则通过以前的套接字继续接收客户请求. -1--失败
(5)connect():请求与远程套接字连接---客户程序使用
int connect(int sockfd,struct sockaddr *serv_addr,int addrlen)
int sockfd, //本地套接字号
struct sockaddr *serv_addr, //serv_addr包含服务器IP地址和端口号地址结构的指针
int addrlen //serv_addr的长度,sizeof(sockaddr)
返回:0---成功,-1---失败
connect()与accept()建立一个全相关五元组
(6)send():发送数据
int send(int sockfd,const void *msg,int len,int flags)
int sockfd, //已连接的用来传输数据的套接字号
const void *msg, //指向要发送数据缓冲区的指针
int len, //以字节为单位的数据的长度
int flags //指定传输控制方式,一般情况下置为0,MSG_OOB,(发送TCP紧急带外数据)
返回值:实际发送的数据长度,可能少于len(7)接收数据
(7)recv():接受数据
int recv(int sockfd,void *buf,int len,unsigned int flags)
int sockfd, //接受数据的套接字号
void *buf, //存放接收数据的缓冲区指针
int len, //缓冲区的长度
unsigned int flags //调用方式.一般情况下置为0,MSG_OOB:读取套接字上的紧急带外数据,MSG_PEEK:仅查看数据,不取出
返回:0----连接被关闭,>0接收的总字节数,<0 SOCKET_ERROR
(8)sendto():UDP—发送数据
int sendto(int sockfd,const void *msg,int len,unsigned int flags,const struct sockaddr *to,int tolen)
int sockfd,
const void * msg, //指向要发送数据的指针
int len, //以字节为单位的数据的长度
unsigned int flags, //flags一般情况下置为0
const struct sockaddr * to, //存放目的机套接字地址
int tolen // sizeof(sockaddr)
返回值:实际发送的数据长度,可能少于len
(9)recvfrom():UDP—接收数据
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen)
int sockfd,
void * buf, //存放接收数据的缓冲区指针
int len, //缓冲区的长度
unsigned int flags //flags一般情况下置为0
struct sockaddr *from, //存保发送来数据的源机的套接字地址
int *fromlen //sizeof(formlen)
返回值:收到的数据长度,如果=-1 出错
(10)关闭套接字
int close(int sockfd); //关闭指定的socket。
返回:0--关闭成功,SOCKET_ERROR--错误
(11)通知关闭套接字
int shutdown(int sockfd,int how)
直接用close关闭套接字,可能会导致数据丢失.在close前,应调用shutdown发送给对方关闭通知,shutdown并不关闭套接字,套接字所占资源将保留到close套接字时.
参数how可以设为下列值:
0:不允许继续接收数据
1:不允许继续发送数据
2:不允许继续发送和接收数据
返回值:成功---0,出错--- SOCKET_ERROR
(12)辅助socket函数:
int getsockname(int sockfg,struct sockaddr* addr, int *addrlen)
int getpeername(int sockfd, struct sockaddr* addr, int *addrlen);
int getsockopt(int sockfd,int level,int optname, void* optval, int * optlen);
int setsockopt(int sockfd,int level,int optname, const void* optval,int optlen)
unsigned long inet_addr(const char *strptr);
int inet_aton(const char *strptr, struct in_addr *addrpt);
char *inet_ntoa(struct in_addr inaddr);
int ioctl(int fd, int request, …/*void *arg*/)
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
(13)域名与IP转换函数,服务与端口号转换函数。
struct hostent *gethostbyname(const char *name);
struct hostent
{
char *h_name; //https://www.doczj.com/doc/f310921501.html,
char **h_aliases; //https://www.doczj.com/doc/f310921501.html,
int h_addrtype; //2, AF_INET
int h_length; // 4
char **h_addr_list;
}
struct hostent *gethostbyaddr(const char *addr,int len,int family); int gethostname(char *name, int namelen);
第二部分:一个简单的客户端程序
#include
#include
#define PORT (u_short) 1080
#define SERVER_ADDR "202.96.1.144"
int main(void)
{
WSADATA Data;
SOCKET sServer;
SOCKET sClient;
char buffer[MAXBUFLEN];
int status;
int ret;
SOCKADDR_IN serverSockAddr;
int addrLen = sizeof(sockaddr_in);
char *toSendtxt="Test String..";
/* initialize the Windows Socket DLL */
status = WSAStartup(MAKEWORD(2, 2), &Data);
if (status != 0)
{
cout << "ERROR: WSAStartup unsuccessful" << endl;
return 1;
}
/* create a socket */
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sClient == INV ALID_SOCKET)
{
cout << "ERROR: socket unsuccessful" << endl;
status = WSACleanup();
if (status == SOCKET_ERROR)
{
cout << "ERROR: WSACleanup unsuccessful" << endl;
return 2;
}
return 3;
}
serverSockAddr.sin_family = AF_INET;
serverSockAddr.sin_port = htons(PORT);
serverSockAddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
cout << "Trying to connect to IP Address: " << SERVER_ADDR << endl;
/* connect to the server */
status = connect(sClient, (struct sockaddr *)&serverSockAddr, sizeof(serverSockAddr)); if (status == SOCKET_ERROR)
{
cout << "ERROR: connect unsuccessful" << endl;
status=closesocket(sClient);
if (status == SOCKET_ERROR)
{
cout << "ERROR: closesocket unsuccessful" << endl;
return 4;
}
status=WSACleanup();
if (status == SOCKET_ERROR)
{
cout << "ERROR: WSACleanup unsuccessful"<< endl;
return 5;
}
return 6;
}
cout << "Connected..." << endl;
while(1)
{
cout << "Sending..." << endl;
ret = send(sClient, toSendtxt, strlen(toSendtxt) + 1, 0);
if (ret != (int)strlen(toSendtxt) + 1)
{
cout << "Connection terminated" << endl;
status = closesocket(sClient);
if (status == SOCKET_ERROR)
{
cout << "ERROR: closesocket unsuccessful" << endl;
return 7;
}
status = WSACleanup();
if (status == SOCKET_ERROR)
{
cout << "ERROR: WSACleanup unsuccessful" << endl;
return 8;
}
return 9;
}
/* Wait before sending the message again */
Sleep(4800);
}
}
第三部分:一个简单的服务器程序
#include
#include
#include
#include
using namespace std;
#define PORT (u_short) 1080
#define SERVER_ADDR "202.96.1.144"
#define MAXBUFLEN 100
int main(void)
{
WSADATA Data;
SOCKADDR_IN serverSockAddr;
SOCKADDR_IN clientSockAddr;
SOCKET sServer;
SOCKET sClient;
int addrLen = sizeof(SOCKADDR_IN);
int status, ret;
int i = 1;
const string end = "..";
char buffer[MAXBUFLEN];
bool flag = false;
/* initialize the Windows Socket DLL */
status = WSAStartup(MAKEWORD(2, 2), &Data);
if (status != 0)
{
cout << "ERROR: WSAStartup unsuccessful" << endl; return 1;
}
/* zero the sockaddr_in structure */
memset(&serverSockAddr, 0, sizeof(serverSockAddr));
serverSockAddr.sin_port=htons(PORT);
serverSockAddr.sin_family=AF_INET;
serverSockAddr.sin_addr.s_addr=inet_addr(SERVER_ADDR);
/* create a socket */
sServer = socket(AF_INET, SOCK_STREAM, 0);
if (sServer == INV ALID_SOCKET)
{
cout << "ERROR: socket unsuccessful" << endl;
return 2;
}
/* associate the socket with the address */
status = bind(sServer, (struct sockaddr *)&serverSockAddr, sizeof(serverSockAddr)); if (status == SOCKET_ERROR)
{
cout << "ERROR: bind unsuccessful" << endl;
return 3;
}
/* allow the socket to take connections */
status = listen(sServer, 1);
if (status == SOCKET_ERROR)
{
cout << "ERROR: listen unsuccessful" << endl;
return 4;
}
cout << "Waiting for the connection..." << endl;
/* accept the connection request when one is received */
sClient = accept(sServer, (struct sockaddr *)&clientSockAddr, &addrLen);
cout << "Got the connection..." << endl;
while(1)
{
ret = recv(sClient, buffer, MAXBUFLEN, 0);
const char *p_end = search(buffer, buffer+ret, end.begin(), end.end());
if(p_end != buffer+ret)
flag = true;
if ((ret == 0) || (ret == SOCKET_ERROR))
{
cout << "Connection terminated." << endl;
status = closesocket(sClient);
if (status == SOCKET_ERROR)
{
cout << "ERROR: closesocket unsuccessful" << endl;
return 5;
}
status = closesocket(sServer);
if (status == SOCKET_ERROR)
{
cout << "ERROR: closesocket unsuccessful" << endl;
return 6;
}
status = WSACleanup();
if (status == SOCKET_ERROR)
{
cout << "ERROR: WSACleanup unsuccessful" << endl;
return 7;
}
return 8;
}
cout << buffer;
if(flag)
{
cout < cout << i << " OVER" << endl; cout << "...." << endl; cout << "...." << endl; i++; flag = false; } /* the times of cout */ if(i>=10) return 0; } } /********************************************/ /* POST提交数据 /* by xicao from SEU /* post.c */ /********************************************/ #include #include #include #pragma comment(lib,"ws2_32.lib") int InitSocket(); u_long Name2Inet(char* szAddress); int main(int argc,char *argv[]) { char buff[1024]; SOCKET sock; SOCKADDR_IN addr; int postlen; char* url="127.0.0.1";//这个自己修改 if (argc!=3) { printf("Error!\n"); return 0; } postlen=(strlen(argv[1])+strlen(argv[2])+19); _snprintf(buff, 4096, "POST http://%s/pass.asp HTTP/1.1\r\n" // "Accept: */*\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" //貌似这句不能丢"Host: %s\r\n" "Content-Length: %d\r\n" //指post的数据长度 "\r\n" "QQNumber=%s&PassWord=%s\r\n", url, url, postlen, argv[1], argv[2]); if(!InitSocket()) { printf("Winsock Initialization failed.\n"); return 0; } if ((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET) { printf("Can not create socket.\n"); return 0; } addr.sin_family = AF_INET; addr.sin_port = htons(80); addr.sin_addr.s_addr=Name2Inet(url); if (connect(sock,(LPSOCKADDR)&addr,sizeof(addr))==SOCKET_ERROR) { printf("Can not connect to specified host.\n"); return -1; } send(sock,buff,strlen(buff),0); closesocket(sock); return 0; } int InitSocket() { #define MAJOR_VERSION 1 #define MINOR_VERSION 2 int nStatus = 0; WORD wMajorVersion = MAJOR_VERSION; WORD wMinorVersion = MINOR_VERSION; WORD wVersionReqd = MAKEWORD(wMajorVersion, wMinorVersion); WSADATA lpmyWSAData; nStatus =WSAStartup(wVersionReqd, &lpmyWSAData); if(nStatus != 0) { return 0; } return 1; } u_long Name2Inet(char* szAddress) { int name=0; int i=0; if(szAddress==NULL||strlen(szAddress)<1) { return htonl(INADDR_ANY); } for (i=0;szAddress;i++) { if((!isdigit(szAddress))&&szAddress!='.') { name=1;break; } } //不是计算机名,是IP if(!name) return inet_addr(szAddress); //是计算机名 else { struct hostent*host=gethostbyname(szAddress); if(host==NULL) { return htonl(INADDR_ANY); } return *(long*)host->h_addr; } } 1 //TCP Port Check program in windows 2 3 4 #include 5 #include 6 7 #pragma comment(lib,"ws2_32.lib") 8 9 10 int main(int argc, char *argv[]) 11 { 12 SOCKET sockfd; 13 SOCKADDR_IN sockaddr; 14 int port; 15 unsigned long ip; 16 WSADATA wsa; 17 int timeout=2,ret; 18 struct timeval tv; 19 struct fd_set fs; 20 unsigned long ul = 1; 21 22 23 if(argc != 4) 24 { 25 printf("Usage: %s IP Port TimeOut\n",argv[0]); 26 return -1; 27 } 28 if((ip=inet_addr(argv[1]))==INADDR_NONE) 29 { 30 printf("IP Address Error\n"); 31 return -1; 32 } 33 if((port=atoi(argv[2]))==0) 34 { 35 printf("Port Error\n"); 36 return -1; 37 } 38 if((timeout=atoi(argv[3]))==0) 39 { 40 printf("Timeout Error\n"); 41 return -1; 42 } 43 WSAStartup(MAKEWORD(1,1),&wsa); //initialize Ws2_32.dll 44 45 if((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) <= 0) //create a tcp socket 46 { 47 printf("Create socket fail!\n"); 48 return -2; 49 } 50 //socket non-block mode set 51 if((ret = ioctlsocket(sockfd, FIONBIO, (unsigned long*)&ul))==SOCKET_ERROR) 52 { 53 closesocket(sockfd); 54 return -1; 55 } 56 sockaddr.sin_family = AF_INET; 57 sockaddr.sin_port = htons(port); 58 sockaddr.sin_addr.S_un.S_addr = inet_addr(argv[1]); 59 60 //connect to target ip with port 61 connect(sockfd, (SOCKADDR *)&sockaddr, sizeof(sockaddr)); 62 63 https://www.doczj.com/doc/f310921501.html,_sec = timeout; 64 https://www.doczj.com/doc/f310921501.html,_usec = 0; 65 FD_ZERO(&fs); 66 FD_SET(sockfd,&fs); 67 ret = select(sockfd+1,NULL,&fs,NULL,&tv); 68 if(ret<0) 69 { 70 printf("Select Error\n"); 71 closesocket(sockfd); 72 return -1; 73 } 74 else if(ret == 0) 75 { 76 printf(" %s %d Connect fail!\n",argv[1],port); 77 closesocket(sockfd); 78 return -1; 79 } 80 else 81 { 82 printf(" %s %d Connected success!\n",argv[1],port); 83 closesocket(sockfd); 84 } 85 WSACleanup(); //clean up Ws2_32.dll 86 return 0; 87 } 复制代码 2. Linux下TCP端口检测程序 复制代码 #include #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { int sockfd=socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in client; char ip[20]; int port; int timeout; struct timeval tv; if(argc != 4) { printf("Usage : %s ip port timeout\n", argv[0]); exit(1); } strcpy(ip, argv[1]); port = atoi(argv[2]); timeout = atoi(argv[3]); bzero(&client, sizeof(struct sockaddr_in)); client.sin_family = AF_INET; client.sin_addr.s_addr = inet_addr(ip); client.sin_port = htons(port); https://www.doczj.com/doc/f310921501.html,_sec = 5; https://www.doczj.com/doc/f310921501.html,_usec = 0; //set timeout with setsockopt if(setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char*)&tv, sizeof(tv))<0) { perror("setsockopt failed\n"); exit(1); } if(setsockopt(sockfd,SOL_SOCKET,SO_SNDTIMEO,(char*)&tv, sizeof(tv))<0) { perror("setsockopt failed\n"); exit(1); } if(!connect(sockfd,(struct sockaddr*)&client,sizeof(struct sockaddr_in))) { printf("connect ok\n"); }else{ printf("connect fail\n"); } close(sockfd); return 0; }