套接字编程
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_INETu_short sin_port; //portstruct in_addr sin_addr; //IP address unsigned longchar 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 addrstruct 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先清0memset((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_addrmemcpy(&sad.sin_addr, ptrh->h_addr, ptrh->h_length);connect(clientSocket, (struct sockaddr *)&sad, sizeof(sad));gets(Sentence); // get input stream from clientn=write(clientSocket, Sentence, strlen(Sentence)+1); // send line to servern=read(clientSocket, modifiedSentence, sizeof(modifiedSentence)); // read line from serverprintf("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];// portport = 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 queuedlisten(welcomeSocket, 10)while(1) {connectionSocket=accept(welcomeSocket, (struct sockaddr *)&cad, &alen);n=read(connectionSocket, clientSentence, sizeof(clientSentence));// capitalize Sentence and store the result in capitalizedSentencen=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);}
}