一.定义:
套接字(socket)是一种 通信机制,凭借这种机制,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行。
套接字的创建和使用与管道是有区别的,因为套接字明确地将客户和服务器区分开来。套接字机制可以实现多个客户连接到一个服务器。
二.套接字属性:
套接字的特性由是三个属性确定,它们是:
1.域(domain):指定套接字通信中使用的网络介质。
(1)UNIX套接字:AF_INET,指的是Internet网络。
(2)文件系统套接字:AF_UNIX,这个域的底层协议是文件输入输出。
2.类型(type):
每个套接字可能有不同的通信方式,而每种通信方式又有不同的特性。但AF_UNIX域的套接字没有这样的问题。
因特网协议提供了两种通信机制:
(1)流(stream):由类型SOCK_STREAM指定。
(2)数据报(datagram):由类型SOCK_DGRAM指定。
3.协议(protocol):
只要底层的传输机制允许一个协议来提供要求的套接字类型,我们就可以为套接字选择一个特定的协议。UNIX网络套接字和文件系统套接字中,你可以不需要选择一个特定的协议,只需要使用默认值即可。
三.套接字使用机制:
1.socket函数:
(1)功能:
创建一个套接字并返回一个描述符,该描述符可以访问该套接字。
(2)原型:
int socket(int domain, int type, int protocol);
2.bind函数:
(1)功能:
给套接字命名
(2)原型:
int bind(int socket, const struct sockaddr *address, size_t address_len);
3.listen函数:
(1)功能:
为了能够在套接字上接收进入的连接,服务器程序必须创建一个队列来保存未处理的请求。
(2)原型:
int listen(int socket, int backlog);
4.accept函数:
(1)功能:
一旦服务器程序创建并命名了套接字之后,它就可以通过该函数来等待客户建立对该套接字的连接。
(2)原型:
int accept(int socket, struct sockaddr *address, size_t *address_len);
5.connect函数:
(1)功能:
客户程序通过在一个未命名套接字和服务器监听套接字之间建立连接的方法来连接到服务器。
(2)原型:
int connect(int socket, const struct sockaddr* *address, size_t address_len);
6.close函数:
(1)功能:
终止服务器和客户上的套接字连接
四.例子:
分别介绍了tcp和udp两种方式:
1.tcp方式:
由tcp_server.c和tcp_client.c构成:
(1)tcp_server.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXLINE 4096
int main(int argc, int** argv) {
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int msg_len;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("create socket error %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
printf("bind socket error %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
if (listen(listenfd, 10) == -1) {
printf("listen socket error %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
printf("=============waiting for client's request=============\n");
while (1) {
if ((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1) {
printf("accept socket error %s(errno: %d)\n", strerror(errno), errno);
continue;
}
msg_len = recv(connfd, buff, MAXLINE, 0);
buff[msg_len] = '\n';
printf("recv msg from client: %s", buff);
close(connfd);
}
close(listenfd);
}
(2)tcp_client.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXLINE 4096
int main(int argc, char** argv) {
int sockfd;
char sendline[4096];
struct sockaddr_in servaddr;
if (argc != 2) {
printf("usage: ./client <ipaddress>\n");
exit(0);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("creat socket error: %s(errno %d)\n", strerror(errno), errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
printf("inet_pton error for %s\n", argv[1]);
exit(0);
}
2.udp方式:
由udp_server.c和udp_client.c构成:
(1)udp_server.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char** argv) {
int sock;
if (argc != 2) {
printf("Usage: %s port\n", argv[0]);
exit(0);
}
printf("This is a UDP server, I can only receive message from client and relpy with same masseage.\n");
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[1]));
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(1);
}
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind");
exit(1);
}
char buf[512];
int msg_len;
struct sockaddr_in clientAddr;
int addr_len = sizeof(clientAddr);
while (1) {
if ((msg_len = recvfrom(sock, buf, 511, 0, (struct sockaddr*)&clientAddr, &addr_len)) > 0) {
buf[msg_len] = 0;
printf("%s, %u says %s\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), buf);
msg_len = sendto(sock, buf, msg_len, 0, (struct sockaddr*)&clientAddr, addr_len);
if (msg_len < 0) {
perror("sendto");
break;
}
} else {
perror("recvfrom");
break;
}
}
return 0;
}
(2)udp_client.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char** argv) {
int sock;
struct sockaddr_in addr;
if (argc != 3) {
printf("Usage: %s ip port\n", argv[0]);
exit(0);
}
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(1);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);
if (addr.sin_addr.s_addr == INADDR_NONE) {
printf("Incorrect IP address\n");
close(sock);
exit(1);
}
char buf[512];
int msg_len;
int addr_len = sizeof(addr);
while (1) {
gets(buf);
msg_len = sendto(sock, buf, strlen(buf), 0, (struct sockaddr*)&addr, addr_len);
if (msg_len < 0) {
perror("sendto");
break;
}
msg_len = recvfrom(sock, buf, 511, 0, (struct sockaddr*)&addr, &addr_len);
if (msg_len > 0) {
buf[msg_len] = 0;
printf("received: ");
puts(buf);
} else if (msg_len == 0) {
printf("server closed");
break;
} else if (msg_len == -1) {
perror("recvfrom");
break;
}
}
close(sock);
return 0;
}