一、TCP/UDP对比
- TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前 不需 要建立连接
- TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
- TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
- UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
- 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP首部开销20字节;UDP的首部开销小,只有8个字节
- TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
二、端口号作用
- 一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等
- 这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。
- 实际上是通过“IP地址+端口号”来区 分不同的服务的。
- 端口提供了一种访问通道,服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69。
三、字节序
- Little endian 小端字节序:低字节存储在起始地址。
- Big endian 大端字节序:高字节存储在起始地址。
- 网络字节序=大端字节序
四、字节序转换api
#include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue);
//返回网络字节序的值uint32_t
htonl(uint32_t host32bitvalue);
//返回网络字节序的值uint16_t
ntohs(uint16_t net16bitvalue);
//返回主机字节序的值uint32_t
ntohl(uint32_t net32bitvalue);
//返回主机字节序的值
- h代表host,n代表net,s代表short(两个字节),l代表long(4个字节),通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取。
五、Sockt服务器和客户端的开发步骤
- 创建套接字
- 为套接字添加信息(IP地址和端口号)
- 监听网络连接
- 监听到有客户端接入,接受一个连接
- 数据交互
- 关闭套接字,断开连接
六、相关函数原型
- int socket(int domain, int type, int protocol);
- bind()函数
- 地址转换API
int inet_aton(const char* straddr,struct in_addr *addrp); //把字符串形式的“192.168.1.123”转为网络能识别的格式 char* inet_ntoa(struct in_addr inaddr); //把网络格式的ip地址转为字符串形式
- 监听listen()函数
- 连接accept()函数
- 数据收发函数
- 数据收发常用第二套API
- 客户端的connect函数
七、客户端接入服务器进行聊天
sever.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int s_fd, c_fd;
int c_size;
char readbuf[128];
char msg[128];
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(msg, 0, sizeof(msg));
memset(readbuf, 0, sizeof(readbuf));
//1. socket
s_fd = socket(AF_INET, SOCK_STREAM, 0);
// if(s_fd == -1){
// perror("socket");
// exit(-1);
// }
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8080);
inet_aton("192.168.101.72",&s_addr.sin_addr);
bind(s_fd, (struct sockaddr *)&s_addr , sizeof(struct sockaddr_in));
listen(s_fd, 10);
while(1) {
c_size =sizeof(struct sockaddr_in);
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &c_size);
if(c_fd != -1) {
if(fork() == 0) {
printf("connect, clint ip = %s\n", inet_ntoa(c_addr.sin_addr));
while(1) {
memset(readbuf, 0, sizeof(readbuf));
memset(msg, 0, sizeof(msg));
int n_read = read(c_fd, readbuf, 20);
if(n_read != -1) {
printf("readbuf = %s, n_read = %d\n", readbuf, n_read);
}
printf("input :\n");
gets(msg);
write(c_fd, msg, strlen(msg));
}
}
}
}
return 0;
}
client.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int s_fd;
char readbuf[128];
char msg[128];
struct sockaddr_in s_addr;
memset(msg, 0, sizeof(msg));
memset(readbuf, 0, sizeof(readbuf));
//1. socket
s_fd = socket(AF_INET, SOCK_STREAM, 0);
// if(s_fd == -1){
// perror("socket");
// exit(-1);
// }
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8080);
inet_aton("192.168.101.72",&s_addr.sin_addr);
int c_ret = connect(s_fd, (struct sockaddr *) &s_addr, sizeof(struct sockaddr));
if(c_ret == -1) {
perror("client error");
exit(-1);
}
while(1) {
memset(msg, 0, sizeof(msg));
memset(readbuf, 0, sizeof(readbuf));
printf("clientt input :\n");
gets(msg);
write(s_fd, msg, strlen(msg));
read(s_fd, readbuf, 128);
printf("client readbuf = %s\n", readbuf);
}
return 0;
}