標頭檔:sys/socket.h
相關結構體:
struct sockaddr
{
unsigned short sa_family; //地址族
char sa_data[14]; //14位元組協議地址
};
struct sockaddr_in
{
short int sin_family; //地址族
unsigned short int sin_port; //連接埠號碼
struct in_addr sin_addr; //IP地址
unsigned char sin_zero[8]; //填充0以保持與struct sockaddr同樣大小
};
struct in_addr
{
unsigned long s_addr; // that’s a 32-bit long, or 4 bytes
};
註:這兩個地址類型結構體在標頭檔中定義。
相關函數:
<開啟通訊端>
int socket(int af,int type,int protocol); //返回socket通訊端,在後面的調用使用它。
af指定通訊發生地區(地址族)
UNIX系統有:AF_UNIX,AF_INET,AF_NS等。
DOS、WINDOWS中支援:AF_INET(網際網地區)
註:地址族與協議族相同。
type為SOCK_STREAM(建立TCP/IP串連的流式通訊端)或SOCK_DGRAM(建立不需連線的UDP資料通訊端)
註:進行資料報方式的資料轉送sendto()和recvfrom()時要用SOCK_DGRAM 不然會產生錯誤。
protocol通常為0
<指定本地地址>
int bind(int sockfd,struct sockaddr *my_addr,int addrlen);
sockfd為socket返回的通訊端。
my_addr指向包含本機IP地址和連接埠號碼等資訊的sockaddr類型指標。
addrlen通常為sizeof(struct sockaddr)
註:定義本機地址通常為
struct sockaddr_in my_addr;
... ...
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(指定連接埠號碼); //htons()進行位元組順序轉換,轉換成網路位元組優先順序。
my_addr.sin_addr.s_addr=INADDR_ANY; //INADDR_ANY自動擷取本機IP地址
<監聽請求>
int listen(int sockfd,int backlog);
backlog指定在請求隊列中允許的最大請求數,進入隊列的請求將等待accept()它們。
註:伺服器程式的通常執行順序為 sockfd=socket( ... ... ); bind(sockfd, ..., ...); listen(sockfd, ..., ...);
用SOCK_STREAM模式需要 accept()串連請求。
<接受串連請求>
int accept(int sockfd,struct sockaddr * ob_addr,int *addrlen); //返回一個新的通訊端,可以通過該通訊端與發出請求的用戶端進行資料轉送。
ob_addr為一個指向sockaddr類型指標,接受請求後,用戶端的地址資訊將儲存在*ob_addr中。
addrlen為指向int型的指標,*addrlen值為sizeof(struct sockaddr)。
<請求串連>
int connect(int sockfd,struct sockaddr * ob_addr,int addrlen); //向目標地址發送串連請求。
*ob_addr為一個設定好的sockaddr類型目標地址。
註:通常
struct sockaddr_in ob_addr;
... ...
char ip[20]={"127.0.0.1"};
... ...
ob_addr.sin_family=AF_INET;
ob_addr.sin_addr.s_addr=inet_addr(ip); //inet_addr()函數將名為ip的字串轉化為所需要的ip地址類型。
//相反的 inet_ntoa()函數可將這種類型轉化為字串類型。如:cout<<inet_ntoa(ob_addr.sin_addr);
ob_addr.sin_port=htons(目標連接埠號碼); //必須對應伺服器監聽的指定連接埠,bind和connect中的端點地址必須一樣,用戶端自己的端點地址中連接埠號碼設定為0,意思是讓系統自動選擇連接埠號碼。
<SOCK_STREAM模式資料轉送>
int send(int sockfd,void * buf,int len,int flags); //通過sockfd通訊端發送訊息
sockfd為串連上的某通訊端。
*buf為要傳輸的資料
len是資料長度(以位元組為單位)。
flags一般為0
int recv(int sockfd,void * buf,int len,int flags); //通過sockfd通訊端接受訊息並存在*buf中
<SOCK_DGRAM模式資料轉送>
int sendto(int sockfd,void * buf,int len,int flags,struct sockaddr * ob_addr,int addrlen);
ob_addr為sockaddr類型的指標,指向設定好的目標地址。
addrlen常為sizeof(struct sockaddr)
int recvfrom(int sockfd,void *buf, int len,int flags,struct sockaddr * ob_addr,int addrlen);
ob_addr為一個指向sockaddr類型指標,接受資料後,發送端的地址資訊將儲存在*ob_addr中。
<關閉通訊端>
bool close(int sockfd);
註:用完了要關!
本文所用到的其他函數在netinet/in.h和arpa/inet.h中均可找到。
如果是在windows下,用VC寫socket程式,則標頭檔為winsock.h。其他函數基本相同。
可以參考:
《Windows Sockets 網路程式設計大全》蔣東興等編著 清華大學出版社
附:
//Linux 下socket通訊 伺服器端設計
#include <iostream>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<string>
#define ListenNum 20
#define BufLen 1024
using namespace std;
int main()
{
int sock,sock_new;
int buf_len;
socklen_t sin_size=sizeof(struct sockaddr);
int re,se;
char * buf_r=new char [BufLen];
char * buf_s=new char [BufLen];
struct sockaddr_in my_addr;
struct sockaddr_in ob_addr;
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock==-1) {cout<<"socket error"<<endl;exit(0);}
else {cout<<sock<<endl;}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(3490);
my_addr.sin_addr.s_addr=INADDR_ANY;
if(bind(sock,(struct sockaddr *)& my_addr,sizeof(struct sockaddr))<0)
{
cout<<"bind error"<<endl;
exit(0);
}
cout<<"socket port: "<<ntohs(my_addr.sin_port)<<endl;
if(listen(sock,ListenNum)<0)
{
cout<<"listen error"<<endl;
exit(0);
}
while(1)
{
sock_new=accept(sock,(sockaddr *)&ob_addr,&sin_size);
if(sock_new<0)
{
cout<<"accept error"<<endl;
exit(0);
}
else
{
int p=fork();
if(p==0)
{
cout<<"connect to :"<<inet_ntoa(ob_addr.sin_addr)<<" : "<<htons(ob_addr.sin_port)<<endl;
int s=fork();
if(s==0)
{
do
{
memset(buf_r,0,sizeof(buf_r));
re=recv(sock_new,buf_r,BufLen,0);
if(re==-1)
{
cout<<"recv error"<<endl;
exit(0);
}
else if(re==0)
{
cout<<inet_ntoa(ob_addr.sin_addr)<<"connection ended"<<endl;
close(sock_new);
}
else
{
cout<<inet_ntoa(ob_addr.sin_addr)<<" -> "<<buf_r<<endl;
}
}while(re>0);
}
else
{
do
{
memset(buf_s,0,sizeof(buf_s));
cin.getline(buf_s,BufLen);
se=send(sock_new,buf_s,BufLen,0);
if(se==-1)
{
cout<<"send error"<<endl;
exit(0);
}
else
{
cout<<"sended!"<<endl;
}
}while(1);
}
}
}
}
close(sock);
return 0;
}
//Linux 下socket通訊 用戶端設計
#include <iostream>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#define ListenNum 20
#define BufLen 1024
using namespace std;
int main()
{
int sock,sock_new;
int buf_len;
socklen_t sin_size=sizeof(struct sockaddr);
char * buf_s=new char [BufLen];
char * buf_r=new char [BufLen];
char * ip=new char [20];
int re;
memset(ip,0,sizeof(ip));
struct sockaddr_in my_addr;
struct sockaddr_in ob_addr;
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock==-1) {cout<<"socket error"<<endl;exit(0);}
else {cout<<sock<<endl;}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(0);
my_addr.sin_addr.s_addr=INADDR_ANY;
if(bind(sock,(struct sockaddr *)& my_addr,sin_size)<0)
{
cout<<"bind error"<<endl;
exit(0);
}
cout<<"socket port: "<<ntohs(my_addr.sin_port)<<endl;
cout<<"input ip : ";
cin.getline(ip,20);
ob_addr.sin_family=AF_INET;
ob_addr.sin_addr.s_addr=inet_addr(ip);
ob_addr.sin_port=htons(3490);
if(connect(sock,(sockaddr *)&ob_addr,sizeof(sockaddr))<0)
{
cout<<"connect error"<<endl;
exit(0);
}
else
{
cout<<"connect success"<<endl;
int p=fork();
if(p==0)
{
while(cin.getline(buf_s,BufLen))
{
if(send(sock,buf_s,BufLen,0)!=-1)
cout<<"sended!"<<endl;
else cout<<"send error"<<endl;
}
}
else
{
do
{
memset(buf_r,0,sizeof(buf_r));
re=recv(sock,buf_r,BufLen,0);
if(re==-1)
{
cout<<"recv error"<<endl;
exit(0);
}
else if(re==0)
{
cout<<inet_ntoa(ob_addr.sin_addr)<<"connection ended"<<endl;
close(sock);
}
else
{
cout<<inet_ntoa(ob_addr.sin_addr)<<" -> "<<buf_r<<endl;
}
}while(re>0);
}
}
return 0;
}