tcp与udp
- TCP:可靠的,像“顺丰快递”,包裹一定送到,按顺序,签收确认。
- UDP:不可靠的,像“扔报纸”,快速发,不管你收没收,不管顺不顺序。
TCP(Transmission Control Protocol)传输控制协议
特点:
特性 | 描述 |
---|
面向连接 | 三次握手建立连接,断开也要四次挥手 |
可靠传输 | 有确认、有重传、有序号,确保每个数据包都送达 |
有序传输 | 接收的数据顺序和发送的保持一致 |
流量控制 | 控制发送速度,防止接收方处理不过来 |
拥塞控制 | 根据网络状况调整发送速率,防止网络拥堵 |
应用场景:
- 网页访问(HTTP、HTTPS)
- 文件传输(FTP)
- 邮件传输(SMTP、POP3)
UDP(User Datagram Protocol)用户数据报协议
特点:
特性 | 描述 |
---|
无连接 | 不建立连接,直接发,像发短信 |
不可靠传输 | 不确认、不重传,可能丢包 |
更快更轻量 | 没有 TCP 的各种机制,效率高,延迟低 |
不保证顺序 | 数据到达顺序可能和发送顺序不同 |
应用场景:
- 视频直播、语音通话(比如微信语音、腾讯会议)
- DNS 查询(要的是快)
- 游戏实时通信(延迟比丢包更重要)
关键区别对比
项目 | TCP | UDP |
---|
是否连接 | 有连接(三次握手) | 无连接 |
是否可靠 | 可靠,保证顺序和送达 | 不可靠,不保证顺序 |
是否慢一点 | 是(因为有确认/重传) | 否(几乎实时) |
应用 | 网页/文件/邮箱 | 视频/语音/游戏 |
一个类比帮助你理解:
TCP 就像打电话(“喂?能听见吗?”——要确认、能讲话、顺序讲)
UDP 就像发广播(“大家听好了,我说一遍,不管听没听见”)
接下来我用代码简单实现一下tcp的通讯效果。
tcp服务端代码
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#define handle_error(cmd, result) \
if (result < 0) \
{ \
perror(cmd); \
return -1; \
}
void *read_from_client(void * arg)
{
char * read_buf=NULL;
int client_fd=*(int*)arg;
read_buf=malloc(sizeof(char)*1024);
ssize_t count =0;
if(!read_buf)
{
perror("malloc server read_buf");
return NULL;
}
while (count=recv(client_fd,read_buf,1024,0))
{
if (count < 0)
{
perror("recv");
}
fputs(read_buf, stdout);
}
printf("客户端请求关闭\n");
free(read_buf);
return NULL;
}
void *write_from_client(void * arg)
{
char * write_buf=NULL;
int client_fd=*(int*)arg;
write_buf=malloc(sizeof(char)*1024);
ssize_t count =0;
if(!write_buf)
{
perror("malloc server write_buf");
return NULL;
}
while (fgets(write_buf,1024,stdin)!=NULL)
{
count=send(client_fd,write_buf,1023,0);
if(count<0)
{
perror("send");
}
}
printf("接收到控制台的关闭请求不再写入 关闭连接\n");
shutdown(client_fd,SHUT_WR);
free(write_buf);
return NULL;
}
int main(int argc, char const *argv[])
{
int sockfd,temp_result,clientfd;
pthread_t pid_read,pid_write;
struct sockaddr_in server_addr,client_addr;
memset(&server_addr,0,sizeof(server_addr));
memset(&client_addr,0,sizeof(client_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(6666);
sockfd=socket(AF_INET,SOCK_STREAM,0);
handle_error("socket",sockfd);
temp_result=bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));;
handle_error("bind",temp_result);
temp_result=listen(sockfd,128);
handle_error("listen",temp_result);
socklen_t clientaddr_len=sizeof(client_addr);
clientfd=accept(sockfd,(struct sockaddr *)&client_addr,&clientaddr_len);
handle_error("accept",clientfd);
printf("与客户端%s %d建立连接 文件描述符是%d",inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port),clientfd);
pthread_create(&pid_read,NULL,read_from_client,(void*)&clientfd);
pthread_create(&pid_write,NULL,write_from_client,(void*)&clientfd);
pthread_join(pid_read,NULL);
pthread_join(pid_write,NULL);
printf("释放内存\n");
close(clientfd);
close(sockfd);
return 0;
}
tcp客户端代码
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#define handle_error(cmd, result) \
if (result < 0) \
{ \
perror(cmd); \
return -1; \
}
void *read_from_server(void * arg)
{
char * read_buf=NULL;
int client_fd=*(int*)arg;
read_buf=malloc(sizeof(char)*1024);
ssize_t count =0;
if(!read_buf)
{
perror("malloc server read_buf");
return NULL;
}
while (count=recv(client_fd,read_buf,1024,0))
{
fputs(read_buf,stdout);
}
printf("服务端请求关闭\n");
free(read_buf);
return NULL;
}
void *write_from_server(void * arg)
{
char * write_buf=NULL;
int client_fd=*(int*)arg;
write_buf=malloc(sizeof(char)*1024);
ssize_t count =0;
if(!write_buf)
{
perror("malloc server write_buf");
return NULL;
}
while (fgets(write_buf,1023,stdin)!=NULL)
{
count=send(client_fd,write_buf,1023,0);
if(count<0)
{
perror("send");
}
}
printf("接收到控制台的关闭请求不再写入 关闭连接\n");
shutdown(client_fd,SHUT_WR);
free(write_buf);
return NULL;
}
int main(int argc, char const *argv[])
{
int sockfd,temp_result;
pthread_t pid_read,pid_write;
struct sockaddr_in server_addr,client_addr;
memset(&server_addr,0,sizeof(server_addr));
memset(&client_addr,0,sizeof(client_addr));
client_addr.sin_family=AF_INET;
inet_pton(AF_INET,"192.168.6.101",&client_addr.sin_addr);
client_addr.sin_port=htons(8888);
server_addr.sin_family=AF_INET;
inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr);
server_addr.sin_port=htons(6666);
sockfd=socket(AF_INET,SOCK_STREAM,0);
handle_error("socket",sockfd);
temp_result=bind(sockfd,(struct sockaddr *)&client_addr,sizeof(client_addr));
handle_error("bind",sockfd);
temp_result=connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr));
handle_error("connect",sockfd);
printf("成功连上服务端%s %d\n",inet_ntoa(server_addr.sin_addr),ntohs(server_addr.sin_port));
pthread_create(&pid_read,NULL,read_from_server,(void*)&sockfd);
pthread_create(&pid_write,NULL,write_from_server,(void*)&sockfd);
pthread_join(pid_read,NULL);
pthread_join(pid_write,NULL);
printf("释放内存\n");
close(sockfd);
return 0;
}
在实际应用中,我们的server是不可能只有一个client访问的,所以client后面是不需要绑定的,接下来我将展示tcp
和udp
实现通讯的效果
tcp多线程通讯
server
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#define handle_error(cmd, result) \
if (result < 0) \
{ \
perror(cmd); \
return -1; \
}
void *read_from_client_then_write(void *argv)
{
int client_fd = *(int *)argv;
ssize_t count = 0, send_count = 0;
char *read_buf = NULL;
char *write_buf = NULL;
read_buf = malloc(sizeof(char) * 1024);
if (!read_buf)
{
printf("服务端读缓存创建异常,断开连接\n");
shutdown(client_fd, SHUT_WR);
close(client_fd);
perror("malloc sever read_buf");
return NULL;
}
write_buf = malloc(sizeof(char) * 1024);
if (!write_buf)
{
printf("服务端写缓存创建异常,断开连接\n");
free(read_buf);
shutdown(client_fd, SHUT_WR);
close(client_fd);
perror("malloc server write_buf");
return NULL;
}
while ((count = recv(client_fd, read_buf, 1024, 0)))
{
if (count < 0)
{
perror("recv");
}
printf("reveive message from client_fd: %d: %s\n", client_fd, read_buf);
strcpy(write_buf, "reveived~\n");
send_count = send(client_fd, write_buf, 1024, 0);
if (send_count < 0)
{
perror("send");
}
}
printf("客户端client_fd: %d请求关闭连接......\n", client_fd);
strcpy(write_buf, "receive your shutdown signal\n");
send_count = send(client_fd, write_buf, 1024, 0);
if (send_count < 0)
{
perror("send");
}
printf("释放client_fd: %d资源\n", client_fd);
shutdown(client_fd, SHUT_WR);
close(client_fd);
free(read_buf);
free(write_buf);
return NULL;
}
int main(int argc, char const *argv[])
{
int sockfd, temp_result;
struct sockaddr_in server_addr, client_addr;
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(6666);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
handle_error("socket", sockfd);
temp_result = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
handle_error("bind", temp_result);
temp_result = listen(sockfd, 128);
handle_error("listen", temp_result);
socklen_t cliaddr_len = sizeof(client_addr);
while (1)
{
int client_fd = accept(sockfd, (struct sockaddr *)&client_addr, &cliaddr_len);
handle_error("accept", client_fd);
printf("与客户端 from %s at PORT %d 文件描述符 %d 建立连接\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), client_fd);
pthread_t pid_read_write;
if (pthread_create(&pid_read_write, NULL, read_from_client_then_write, (void *)&client_fd))
{
perror("pthread_create");
}
pthread_detach(pid_read_write);
printf("创建子线程并处理为detached状态\n");
}
printf("释放资源\n");
close(sockfd);
return 0;
}
client
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#define handle_error(cmd, result) \
if (result < 0) \
{ \
perror(cmd); \
return -1; \
}
void *read_from_server(void *argv)
{
int sockfd = *(int *)argv;
char *read_buf = NULL;
ssize_t count = 0;
read_buf = malloc(sizeof(char) * 1024);
if (!read_buf)
{
perror("malloc client read_buf");
return NULL;
}
while (count = recv(sockfd, read_buf, 1024, 0))
{
if (count < 0)
{
perror("recv");
}
fputs(read_buf, stdout);
}
printf("收到服务端的终止信号......\n");
free(read_buf);
return NULL;
}
void *write_to_server(void *argv)
{
int sockfd = *(int *)argv;
char *write_buf = NULL;
ssize_t send_count;
write_buf = malloc(sizeof(char) * 1024);
if (!write_buf)
{
printf("写缓存申请异常,断开连接\n");
shutdown(sockfd, SHUT_WR);
perror("malloc client write_buf");
return NULL;
}
while (fgets(write_buf, 1024, stdin) != NULL)
{
send(sockfd, write_buf, 1024, 0);
if (send_count < 0)
{
perror("send");
}
}
printf("接收到命令行的终止信号,不再写入,关闭连接......\n");
shutdown(sockfd, SHUT_WR);
free(write_buf);
return NULL;
}
int main(int argc, char const *argv[])
{
int sockfd, temp_result;
pthread_t pid_read, pid_write;
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
server_addr.sin_port = htons(6666);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
handle_error("socket", sockfd);
temp_result = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
handle_error("connect", temp_result);
pthread_create(&pid_read, NULL, read_from_server, (void *)&sockfd);
pthread_create(&pid_write, NULL, write_to_server, (void *)&sockfd);
pthread_join(pid_read, NULL);
pthread_join(pid_write, NULL);
printf("关闭资源\n");
close(sockfd);
return 0;
}
tcp进程通讯
server
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#define handle_error(cmd, result) \
if (result < 0) \
{ \
perror(cmd); \
return -1; \
}
void zombie_dealer(int sig) {
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
if (WIFEXITED(status)) {
printf("子进程: %d 以 %d 状态正常退出,已被回收\n", pid, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("子进程: %d 被 %d 信号杀死,已被回收\n", pid, WTERMSIG(status));
} else {
printf("子进程: %d 因其它原因退出,已被回收\n", pid);
}
}
}
void *read_from_client_then_write(void *argv)
{
int client_fd = *(int *)argv;
ssize_t count = 0, send_count = 0;
char *read_buf = NULL;
char *write_buf = NULL;
read_buf = malloc(sizeof(char) * 1024);
if (!read_buf)
{
printf("服务端pid: %d: 读缓存创建异常,断开连接\n", getpid());
shutdown(client_fd, SHUT_WR);
close(client_fd);
perror("malloc sever read_buf");
return NULL;
}
write_buf = malloc(sizeof(char) * 1024);
if (!write_buf)
{
printf("服务端pid: %d: 写缓存创建异常,断开连接\n", getpid());
free(read_buf);
shutdown(client_fd, SHUT_WR);
close(client_fd);
perror("malloc server write_buf");
return NULL;
}
while ((count = recv(client_fd, read_buf, 1024, 0)))
{
if (count < 0)
{
perror("recv");
}
printf("服务端pid: %d: reveive message from client_fd: %d: %s\n", getpid(), client_fd, read_buf);
sprintf(write_buf, "服务端pid: %d: reveived~\n", getpid());
send_count = send(client_fd, write_buf, 1024, 0);
if (send_count < 0)
{
perror("send");
}
}
printf("服务端pid: %d: 客户端client_fd: %d请求关闭连接......\n", getpid(), client_fd);
sprintf(write_buf, "服务端pid: %d: receive your shutdown signal\n", getpid());
send_count = send(client_fd, write_buf, 1024, 0);
if (send_count < 0)
{
perror("send");
}
printf("服务端pid: %d: 释放client_fd: %d资源\n", getpid(), client_fd);
shutdown(client_fd, SHUT_WR);
close(client_fd);
free(read_buf);
free(write_buf);
return NULL;
}
int main(int argc, char const *argv[])
{
int sockfd, temp_result;
struct sockaddr_in server_addr, client_addr;
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(6666);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
handle_error("socket", sockfd);
temp_result = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
handle_error("bind", temp_result);
temp_result = listen(sockfd, 128);
handle_error("listen", temp_result);
socklen_t cliaddr_len = sizeof(client_addr);
signal(SIGCHLD, zombie_dealer);
while (1)
{
int client_fd = accept(sockfd, (struct sockaddr *)&client_addr, &cliaddr_len);
handle_error("accept", client_fd);
pid_t pid = fork();
if (pid > 0)
{
printf("this is father, pid is %d, continue accepting...\n", getpid());
close(client_fd);
}
else if (pid == 0)
{
close(sockfd);
printf("与客户端 from %s at PORT %d 文件描述符 %d 建立连接\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), client_fd);
printf("新的服务端pid为: %d\n", getpid());
read_from_client_then_write((void *)&client_fd);
close(client_fd);
exit(EXIT_SUCCESS);
}
}
printf("释放资源\n");
close(sockfd);
return 0;
}
client
这里的客户端程序与tcp多线程下client代码一致
udp通讯
server
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include<unistd.h>
#define handle_error(cmd, result) \
if (result < 0) \
{ \
perror(cmd); \
return -1; \
}
int main(int argc, char const *argv[])
{
int sockfd, temp_result;
char * buf=malloc(1024);
struct sockaddr_in server_addr, client_addr;
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(6666);
sockfd=socket(AF_INET,SOCK_DGRAM,0);
handle_error("socket",sockfd);
temp_result=bind(sockfd,(struct sockaddr*)&server_addr, sizeof(server_addr));
handle_error("bind",temp_result);
do
{
memset(buf,0,1024);
temp_result = recvfrom(sockfd, buf, 1024, 0, (struct sockaddr*)&client_addr, &(socklen_t){sizeof(client_addr)});
handle_error("recvfrom",temp_result);
if(strncmp(buf,"EOF",3)!=0)
{
printf("接收到客户端%s %d信息:%s\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),buf);
strcpy(buf,"ok\n");
}else
{
printf("收到结束信息,准备关闭\n");
}
temp_result=sendto(sockfd,buf,4,0,(struct sockaddr*)&client_addr, sizeof(client_addr));
handle_error("sento",temp_result);
} while (strncmp(buf,"EOF",3));
free(buf);
close(sockfd);
return 0;
}
client
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include<unistd.h>
#define handle_error(cmd, result) \
if (result < 0) \
{ \
perror(cmd); \
return -1; \
}
int main(int argc, char const *argv[])
{
int sockfd, temp_result;
char * buf=malloc(1024);
struct sockaddr_in server_addr, client_addr;
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(6666);
sockfd=socket(AF_INET,SOCK_DGRAM,0);
handle_error("socket",sockfd);
do
{
printf("请输入你想要发送的数据\n");
int buf_len=read(STDIN_FILENO,buf,1024);
temp_result=sendto(sockfd,buf,buf_len,0,(struct sockaddr*)&server_addr,sizeof(server_addr));
handle_error("send",temp_result);
memset(buf, 0,1024);
temp_result=recvfrom(sockfd,buf,1024,0,NULL,NULL);
handle_error("recvfrom",temp_result);
if(strncmp(buf,"EOF",3))
{
printf("接受到服务端%s %d的信息:%s\n",inet_ntoa(server_addr.sin_addr),ntohs(server_addr.sin_port),buf);
}
} while (strncmp(buf,"EOF",3));
free(buf);
close(sockfd);
return 0;
}