头文件: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;
}