0.简介 socket时网络中2个主机之间进行任何类型的网络通信的虚拟口,任何网络通信都是通过套接字来进行。 按照功能分为: socket客户端:连接到远程系统来获取数据的应用程序 socket服务器:使用socket接受传入连接,并提供数据的应用程序。
1 建立socket客户端 其中包含以下的内容: 1.创建一个socket 2.连接到远程服务器 3.发送数据 4.接受数据 5.关闭socket 在本部分中,我们编写socket客户端,www.baidu.com作为socket服务器。 更具体的说:www.baidu.com是http服务器,网络浏览器是http客户端。
1.1 创建一个socket 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main() { int socket_desc; socket_desc = socket(AF_INET,SOCK_STREAM,0); if (socket_desc == -1) { printf ("could not create socket" ); } return 0; }
socket函数创建了一个socket,并且返回了一个socket描述符,可以在其他的函数中别使用,socket函数具体有以下的属性:
1 2 3 4 5 int socket(int domain,int type ,int protocol) int domain: AF_UNIX(本机通信),AF_INET(TCP/IP-IPv4),AF_INET6(TCP/IP-IPv6) int type : SOCK_STREAM(TCP),SOCK_DGRAM(UDP),SOCK_RAW int protocol =0确定套接字需要的协议簇和类型 返回值:成功返回套接字,失败返回-1,错误代码写入“errno”
1.2将socket连接到服务器 通过socket连接到远程服务器,需要ip地址和端口号进行连接
1.2.1 创建sockaddr_in结构 1 struct sockaddr_in server;
sockaddr_in 结构如下面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // IPv4 AF_INET sockets: struct sockaddr_in { short sin_family; // e.g. AF_INET, AF_INET6 unsigned short sin_port; // e.g. htons(3490) struct in_addr sin_addr; // see struct in_addr, below char sin_zero[8]; // zero this if you want to }; struct in_addr { unsigned long s_addr; // load with inet_pton() }; struct sockaddr { unsigned short sa_family; //地址族,AF_xxx char sa_data [14]; // 14个字节的协议地址 };
函数inet_addr是将ip地址转换为长格式的函数:
1 server.sin_addr.s_addr = inet_addr("183.232.231.172" );
这里时baidu的ip地址。下面有如何找出给定域名的ip地址。
1.2.2 连接 “连接”的概念适用于SOCK_STREAM / TCP类型的套接字。连接是指可靠的数据“流”,以便可以有多个这样的流,每个流具有自己的通信。可以将其视为不受其他数据干扰的管道。
其他套接字,例如UDP,ICMP,ARP都没有“连接”的概念。这些是基于非连接的通信。这意味着您一直在发送或接收来自任何人和每个人的数据包。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 int main(int argc , char *argv[]) { int socket_desc; struct sockaddr_in server; //Create socket socket_desc = socket(AF_INET , SOCK_STREAM , 0); if (socket_desc == -1) { printf ("Could not create socket" ); } server.sin_addr.s_addr = inet_addr("183.232.231.172" ); server.sin_family = AF_INET; server.sin_port = htons( 80 ); //Connect to remote server if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) { puts("connect error" ); return 1; } puts("Connected" ); return 0; }
1.3 通过socket发送数据 通过send函数发送数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 int main(int argc , char *argv[]) { int socket_desc; struct sockaddr_in server; char *message; //Create socket socket_desc = socket(AF_INET , SOCK_STREAM , 0); if (socket_desc == -1) { printf ("Could not create socket" ); } server.sin_addr.s_addr = inet_addr("183.232.231.172" ); server.sin_family = AF_INET; server.sin_port = htons( 80 ); //Connect to remote server if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) { puts("connect error" ); return 1; } puts("Connected\n" ); //Send some data message = "GET / HTTP/1.1\r\n\r\n" ; if ( send(socket_desc , message , strlen(message) , 0) < 0) { puts("Send failed" ); return 1; } puts("Data Send\n" ); return 0; }
备注:也可以通过write来将数据发送到socket
1.4 通过socket接受数据 函数recv用于在socket上接受数据,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 int main(int argc , char *argv[]) { int socket_desc; struct sockaddr_in server; char *message , server_reply[2000]; //Create socket socket_desc = socket(AF_INET , SOCK_STREAM , 0); if (socket_desc == -1) { printf ("Could not create socket" ); } server.sin_addr.s_addr = inet_addr("183.232.231.172" ); server.sin_family = AF_INET; server.sin_port = htons( 80 ); //Connect to remote server if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) { puts("connect error" ); return 1; } puts("Connected\n" ); //Send some data message = "GET / HTTP/1.1\r\n\r\n" ; if ( send(socket_desc , message , strlen(message) , 0) < 0) { puts("Send failed" ); return 1; } puts("Data Send\n" ); //Receive a reply from the server if ( recv(socket_desc, server_reply , 2000 , 0) < 0) { puts("recv failed" ); } puts("Reply received\n" ); puts(server_reply); return 0; }
baidu返回了html。
备注:我们也可以通过read读取socket的数据。
1.5 关闭socket 用close函数,需要unistd.h头文件
以上,socket客户端已经基本完成。
1.6 补充:获取主机的IP地址 连接到到远程主机,需要IP地址。用gethostbyname函数,域名作为参数,返回hostent类型的结构。
1 2 3 4 5 6 7 8 9 /* 单个主机的数据库描述*/ struct hostent { char *h_name; /* 主机的正式名称*/ char **h_aliases; /* 别名列表*/ int h_addrtype; /* 主机地址类型*/ int h_length; /* 地址的长度*/ char **h_addr_list; /* 服务器的地址列表*/ };
在 h_addr_list中有IP地址。通过以下的代码来使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 int main(int argc , char *argv[]) { char *hostname = "www.baidu.com" ; char ip[100]; struct hostent *he; struct in_addr **addr_list; int i; if ( (he = gethostbyname( hostname ) ) == NULL) { //gethostbyname failed herror("gethostbyname" ); return 1; } //Cast the h_addr_list to in_addr , since h_addr_list also has the ip address in long format only addr_list = (struct in_addr **) he->h_addr_list; for (i = 0; addr_list[i] != NULL; i++) { //Return the first one; strcpy(ip , inet_ntoa(*addr_list[i]) ); } printf ("%s resolved to : %s\n" , hostname , ip); return 0; }