socket通信
socket是应用层与TCP/IP协议族通信的中间软件抽象层,是一组接口。工作原理如下:
具体过程:服务器端先初始化socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
socket属性
1.创建套接字
套接字的特性由三个属性确定:域(domain), 类型(type)和协议(protocol)。
int socket(int domain, int type, int protocol);
domain:指定socket的类型,一般为AF_INET;
type:是SOCK_STREAM 或SOCK_DGRAM,分别表示TCP连接和UDP连接;
protocol :通常赋值"0"。
socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。
2.命名套接字
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;
//addrlen常被设置为sizeof(struct sockaddr)。
返回值:0表示成功;-1表示遇到错误,并将errno置为相应的错误号。
端口号 : 一般不要置为小于1024的值,因为1~1024是保留端口号 ,可使用大于1024中任何一个没有被占用的端口号。
addr的赋值如下:实现自动获得本机IP地址和随机获取一个没有被占用的端口号。
my_addr.sin_port = ; /* 系统随机选择一个未被使用的端口号 */
my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */
3.套接字地址结构
套接字地址由结构sockaddr_in来指定,在头文件 <netinet/in.h> 中定义。
第一个结构:
struct sockaddr {
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[]; /* 14 字节的协议地址 */
};
sa_family:一般为AF_INET;
sa_data :包含该socket的IP地址和端口号。
第二种结构:
struct sockaddr_in {
short int sin_ family; /* 地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[]; /* 填充0 以保持与struct sockaddr同样大小 */
};
sin_family :通常被赋AF_INET;
sin_port和 sin_addr :转换成为网络字节优先顺序 。
sin_zero :用来将sockaddr_in结构填充到与struct sockaddr同样的长度,应该用bzero()或memset()函数将其置为零。
指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,如果一个函数所需参数类型是sockaddr时,可以在函数调用的时候将一个指向sockaddr_in的指针转换为指向sockaddr的指针;或者相反。
IP地址结构 in_addr 被定义为:
struct in_addr{
unsigned long int s_addr;
};
4.创建队列
int listen(int sockfd, int backlog);
//backlog指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()它们
backlog:队列中等待服务的请求数目,系统缺省值为20,常用值是5。
返回值:-1表示遇到错误,errno被置为相应的错误码。
5.阻塞接收
当某个客户端试图与服务器监听的端口连接时,该连接请求将排队等待服务器 accept()它 。
int accept(int sockfd, void *addr, int *addrlen);
//addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息(某台主机从某个端口发出该请求);
//addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。
返回值:-1表示错误,并且设置相应的errno值。
6.请求连接
connect()函数用来与远端服务器建立一个TCP连接。
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
//sockfd是目的服务器的sockt描述符;serv_addr是包含目的机IP地址和端口号的指针。
返回值:-1表示遇到错误,并且设置相应的errno值。
7.数据传输
send()和recv()——数据传输
int send(int sockfd, const void *msg, int len, int flags);
//sockfd是你想用来传输数据的socket描述符,msg是一个指向要发送数据的指针,Len是以字节为单位的数据的长度。flags一般情况下置为0。
返回值:返回实际发送出的字节数。当send()返回值与len不匹配时,应该对这种情况进行处理。
int recv(int sockfd,void *buf,int len,unsigned int flags);
//sockfd是接受数据的socket描述符;buf 是存放接收数据的缓冲区;len是缓冲的长度。Flags也被置为0。
返回值:返回实际上接收的字节数,或当出现错误时,返回-1并置相应的errno值。
socket使用
例一:server端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h> #define MAXLINE 4096 int main( int argc, char** argv )
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[];
int n; if( ( listenfd = socket( AF_INET, SOCK_STREAM, ) ) == - )
{
printf( "create socket error: %s(errno: %d)\n",strerror( errno ),errno );
exit( );
} memset( &servaddr, , sizeof( servaddr ) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
servaddr.sin_port = htons( ); if( bind( listenfd, ( struct sockaddr* )&servaddr, sizeof( servaddr ) ) == - )
{
printf( "bind socket error: %s(errno: %d)\n",strerror( errno ),errno );
exit( );
} if( listen( listenfd, ) == - )
{
printf( "listen socket error: %s(errno: %d)\n",strerror( errno ),errno );
exit( );
} printf( "======waiting for client's request======\n" );
while( )
{
if( ( connfd = accept( listenfd, ( struct sockaddr* )NULL, NULL ) ) == - )
{
printf( "accept socket error: %s(errno: %d)",strerror( errno ),errno );
continue;
}
n = recv( connfd, buff, MAXLINE, );
buff[n] = '\0';
printf( "recv msg from client: %s\n", buff );
close( connfd );
} close( listenfd );
}
client端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h> #define MAXLINE 4096 int main( int argc, char** argv )
{
int sockfd, n;
char recvline[], sendline[];
struct sockaddr_in servaddr;
#if 0
if( argc != )
{
printf( "usage: ./client <ipaddress>\n" );
exit( );
}
#endif
if( ( sockfd = socket( AF_INET, SOCK_STREAM, ) ) < )
{
printf( "create socket error: %s(errno: %d)\n", strerror( errno ),errno );
exit( );
} memset( &servaddr, , sizeof( servaddr ) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr( "109.123.123.36" );
servaddr.sin_port = htons( ); #if 0
if( inet_pton( AF_INET, argv[], &servaddr.sin_addr ) <= )
{
printf( "inet_pton error for %s\n",argv[] );
exit( );
}
#endif if( connect( sockfd, ( struct sockaddr* )&servaddr, sizeof( servaddr ) ) < )
{
printf( "connect error: %s(errno: %d)\n",strerror( errno ),errno );
exit( );
} printf( "send msg to server: \n" );
fgets( sendline, , stdin );
if( send( sockfd, sendline, strlen( sendline ), ) < )
{
printf( "send msg error: %s(errno: %d)\n", strerror( errno ), errno );
exit( );
} close( sockfd );
exit( );
}
例二:server端:
//s_unix.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIX_DOMAIN "/tmp/UNIX.domain"
int main(void)
{
socklen_t clt_addr_len;
int listen_fd;
int com_fd;
int ret;
int i;
static char recv_buf[];
int len;
struct sockaddr_un clt_addr;
struct sockaddr_un srv_addr;
listen_fd=socket(PF_UNIX,SOCK_STREAM,);
if(listen_fd<)
{
perror("cannot create communication socket");
return ;
} //set server addr_param
srv_addr.sun_family=AF_UNIX;
strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-);
unlink(UNIX_DOMAIN);
//bind sockfd & addr
ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
if(ret==-)
{
perror("cannot bind server socket");
close(listen_fd);
unlink(UNIX_DOMAIN);
return ;
}
//listen sockfd
ret=listen(listen_fd,);
if(ret==-)
{
perror("cannot listen the client connect request");
close(listen_fd);
unlink(UNIX_DOMAIN);
return ;
}
//have connect request use accept
len=sizeof(clt_addr);
com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);
if(com_fd<)
{
perror("cannot accept client connect request");
close(listen_fd);
unlink(UNIX_DOMAIN);
return ;
}
//read and printf sent client info
printf("\n=====info=====\n");
for(i=;i<;i++)
{
memset(recv_buf,,);
int num=read(com_fd,recv_buf,sizeof(recv_buf));
printf("Message from client (%d)) :%s\n",num,recv_buf);
}
close(com_fd);
close(listen_fd);
unlink(UNIX_DOMAIN);
return ;
}
client端
//c_unix.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIX_DOMAIN "/tmp/UNIX.domain"
int main(void)
{
int connect_fd;
int ret;
char snd_buf[];
int i;
static struct sockaddr_un srv_addr;
//creat unix socket
connect_fd=socket(PF_UNIX,SOCK_STREAM,);
if(connect_fd<)
{
perror("cannot create communication socket");
return ;
}
srv_addr.sun_family=AF_UNIX;
strcpy(srv_addr.sun_path,UNIX_DOMAIN);
//connect server
ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
if(ret==-)
{
perror("cannot connect to the server");
close(connect_fd);
return ;
}
memset(snd_buf,,);
strcpy(snd_buf,"message from client");
//send info server
for(i=;i<;i++)
write(connect_fd,snd_buf,sizeof(snd_buf));
close(connect_fd);
return ;
}