套接字编程
Socket编程
Socket编程:应用进程使用传输层提供的服务才能够交换报文,实现应用协议,实现应用
TCP/IP:应用进程使用Socket API访问传输服务
地点:界面上的SAP
方式:Socket API
目标:学习如何构建能借助sockets进行通信的C/S应用程序
socke:分布式应用进程之间的门,传输层协议提供的端到端服务接口
2种传输层服务的socket类型
- TCP:可靠的、字节流的服务
- UDP:不可靠(数据UDP数据报)服务
TCP套接字编程
套接字:应用进程与端到端传输协议(TCP或UDP)之间的门户
TCP服务:从一个进程向另一个进程可靠的传输字节流
服务器首先运行,等待连接建立
服务器进程必须先处于运行状态
- 创建欢迎socket
- 和本地端口捆绑
- 在欢迎socket上阻塞式等待接受用户的连接
客户端主动和服务器建立连接
创建客户端本地套接字(隐式捆绑到本地port)
- 指定服务器进程的IP地址和端口号,与服务器进程连接
当与客户端连接请求到来时
- 服务器接受来自客户端的请求,接触阻塞式等待,返回一个新的socket(与欢迎socket不一样),与客户端通信
- 允许服务器与多个客户端通信
- 使用的IP和源端口来区分不同的客户端
连接API调用有效时,客户端P与服务器之间建立了TCP连接
从应用程序的角度
TCP在客户端和服务器进程之间提供了可靠的、字节流(管道)服务
C/S模式的应用样例
- 客户端从标准输入装置读取一行字符,发送给服务器
- 服务器从socket读取字符
- 服务器将字符串装换成大写,然后返回给客户端
- 客户端从socket种读取一行字符,然后打印出来
实际上,这里描述了C-S之间交互的动作次序
数据结构sockaddr_in
IP地址和port捆绑关系的数据结构(标示进程的端节点)
struct socketaddr_in{
short sin_family; //AF_INET
u_short sin_port; //port
struct in_addr sin_addr; //IP address unsigned long
char sin_zero[8]; //align
};
属性 | 说明 |
---|---|
sin_family | 地址簇,这个结构体不仅仅用于IP的通信,还可以用于其他的通信,这里设置为常量AF_INET,表明是TCP/IP的协议簇 |
sin_port | 端口号 |
sin_addr | ip地址 |
sin_zero | 起对其作用,因为ipx的地址长度比ip的长度,其他地址也是 |
数据结构hostent
域名和IP地址的数据结构
struct hostent {
char *h_name; //域名
char **h_aliases; //别名
int h_addrtype;
int h_length; //地址长度
char **h_addr_list; //IP地址
#define h_addr h_addr_list[0];
};
属性 | 类型 | 说明 |
---|---|---|
h_name | 字符串 | 主机域名 |
h_aliases | 字符串数组 | 主机的一系列别名 |
h_addrtype | ||
h_length | 数字 | 地址长度 |
h_addr_list | 字符串数组 | ip地址,可以将其复制到sockaddr_in的ip中 |
作为调用域名解析函数时的参数 返回后,将IP地址拷贝到 sockaddr_in的IP地址部分
C/S socket交互:TCP
例子:C客户端(TCP)
// client.c
void main(int argc, char *argv[]) {
// sad表示 server addr
struct sockaddr_in sad; /* structure to hold an IP address of server */
int clientSocket; /* socket descriptor */
struct hostent *ptrh; /* pointer to a host table entry */
char Sentence[128];
char modifiedSentence[128];
// argv[0]是程序的名字
host = argv[1]; // argv[1] 表示服务器的域名
port = atoi(argv[2]); // argv[2]表示服务端的端口
clientSocket = socket(PF_INET, SOCK_STREAM, 0);
// 这里底层自动使用了bind
// sad先清0
memset((char *)&sad,0,sizeof(sad)); /* clear sockaddr structure */
sad.sin_family = AF_INET; /* set family to Internet */
// port先转换成短整形,然后设置成网络次序
sad.sin_port = htons((u_short)port);
ptrh = gethostbyname(host);
/* Convert host name to IP address */
//将IP地址拷贝到sad.sin_addr
memcpy(&sad.sin_addr, ptrh->h_addr, ptrh->h_length);
connect(clientSocket, (struct sockaddr *)&sad, sizeof(sad));
gets(Sentence); // get input stream from client
n=write(clientSocket, Sentence, strlen(Sentence)+1); // send line to server
n=read(clientSocket, modifiedSentence, sizeof(modifiedSentence)); // read line from server
printf("FROM SERVER: %s\n",modifiedSentence);
close(clientSocket); // close the connection
}
例子:C服务器(TCP)
// server.c
void main(int argc, char *argv[]) {
// 只有一个参数,就是服务端的端口号
struct sockaddr_in sad; /* structure to hold an IP address of server*/
struct sockaddr_in cad; /*client */
int welcomeSocket, connectionSocket; /* socket descriptor */
struct hostent *ptrh; /* pointer to a host table entry */
char clientSentence[128];
char capitalizedSentence[128];
// port
port = atoi(argv[1]);
welcomeSocket = socket(PF_INET, SOCK_STREAM, 0);
memset((char *)&sad,0,sizeof(sad)); /* clear sockaddr structure */
sad.sin_family = AF_INET; /* set family to Internet */
sad.sin_addr.s_addr = INADDR_ANY; /* set the local IP address */
sad.sin_port = htons((u_short)port);/* set the port number */
// 此处赋值,在表中进行赋值
bind(welcomeSocket, (struct sockaddr *)&sad, sizeof(sad));
// specify the maximum number of clients that can be queued
listen(welcomeSocket, 10)
while(1) {
connectionSocket=accept(welcomeSocket, (struct sockaddr *)&cad, &alen);
n=read(connectionSocket, clientSentence, sizeof(clientSentence));
// capitalize Sentence and store the result in capitalizedSentence
n=write(connectionSocket, capitalizedSentence, strlen(capitalizedSentence)+1);
close(connectionSocket);
}
}
UDP Socket编程
UDP在客户端和服务器之间没有连接
- 没有握手
- 发送端在每一个报文中明确地指明目标的IP地址和端口号
- 服务器必须从收到的分组中提取出发送端的IP地址和端口号
传送的数据可能乱序,也可能丢失
进程视角看UDP服务
UDP为客户端和服务器提供不可靠的字节组的传送服务
C/S交互:UDP
例子:C客户端
/* client.c */
void main(int argc, char *argv[])
{
struct sockaddr_in sad; /* structure to hold an IP address */
int clientSocket; /* socket descriptor */
struct hostent *ptrh; /* pointer to a host table entry */
char Sentence[128];
char modifiedSentence[128];
host = argv[1]; port = atoi(argv[2]);
clientSocket = socket(PF_INET, SOCK_DGRAM, 0);
/* determine the server's address */
memset((char *)&sad,0,sizeof(sad)); /* clear sockaddr structure */
sad.sin_family = AF_INET; /* set family to Internet */
sad.sin_port = htons((u_short)port);
ptrh = gethostbyname(host);
/* Convert host name to IP address */
memcpy(&sad.sin_addr, ptrh->h_addr, ptrh->h_length);
gets(Sentence);
addr_len =sizeof(struct sockaddr);
n=sendto(clientSocket, Sentence, strlen(Sentence)+1, (struct sockaddr *) &sad, addr_len);
n=recvfrom(clientSocket, modifiedSentence, sizeof(modifiedSentence),(struct sockaddr *) &sad, &addr_len);
printf("FROM SERVER: %s\n",modifiedSentence);
close(clientSocket);
}
例子:C服务器
/* server.c */
void main(int argc, char *argv[])
{
struct sockaddr_in sad; /* structure to hold an IP address */
struct sockaddr_in cad;
int serverSocket; /* socket descriptor */
struct hostent *ptrh; /* pointer to a host table entry */
char clientSentence[128];
char capitalizedSentence[128];
port = atoi(argv[1]);
serverSocket = socket(PF_INET, SOCK_DGRAM, 0);
memset((char *)&sad,0,sizeof(sad)); /* clear sockaddr structure */
sad.sin_family = AF_INET; /* set family to Internet */
sad.sin_addr.s_addr = INADDR_ANY; /* set the local IP address */
sad.sin_port = htons((u_short)port);/* set the port number */
bind(serverSocket, (struct sockaddr *)&sad, sizeof(sad));
while(1) {
n=recvfrom(serverSocket, clientSentence, sizeof(clientSentence), 0
(struct sockaddr *) &cad, &addr_len );
/* capitalize Sentence and store the result in capitalizedSentence*/
n=sendto(serverSocket , capitalizedSentence, strlen(capitalizedSentence)+1,
(struct sockaddr *) &cad, &addr_len);
}
}