Linux下的TCP&UDP通信作业
题目描述:
- 一: 对于TCP传输,实现客户端发送某特定信息/字符时结束信息传输,断开连接。
- 二:编写程序验证在同一台机器上TCP和UDP能不能同时占用同一个端口进行通信。
开始编程:
- 使用vim创建并编辑几个源文件:
vim TCPserver.c
vim TCPclient.c
vim UDPserver.c
vim UDPclient.c
- 分别对应TCP和UDP的服务器端和客户端
其中:
TCPserver.c
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc,char **argv){
int sock,accetp_sock,sock_len,numbytes,i;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
char buff[BUFSIZ];
server_addr.sin_family = AF_INET;
//绑定端口
server_addr.sin_port = htons(8220);
//绑定本地ip(也就是0.0.0.0)
//表示本机的所有ip(可能本机有多个网卡,这能代表所有网卡)
server_addr.sin_addr.s_addr = INADDR_ANY;
//清零
bzero(&(server_addr.sin_zero),8);
sock_len = sizeof(struct sockaddr_in);
printf("start\n");
sock = socket(AF_INET,SOCK_STREAM,0);
//绑定
while(bind(sock,(struct sockaddr *)&server_addr,sock_len)==-1);
printf("bind success\n");
while(listen(sock,10)==-1);
printf("Listening..\n");
printf("等待来自客户端的连接\n");
//开始阻塞
accetp_sock = accept(sock,(struct sockaddr *)&client_addr,&sock_len);
printf("成功连接客户端\n");
numbytes = send(accetp_sock,"\nWelecome to Cheney‘server\n",21,0);
//开始阻塞
while((numbytes = recv(accetp_sock,buff,BUFSIZ,0))>0){
buff[numbytes] = '\0';
printf("%s\n",buff);
if(send(accetp_sock,buff,numbytes,0)<0){
perror("write");
return 1;
}
}
close(accetp_sock);
close(sock);
return 0;
}
TCPclient.c
/***********引入必要的文件**************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main(int argc,char **argv){
int sockfd,numbers;
char buf[BUFSIZ];
struct sockaddr_in sockaddress;
printf("start:\n");
while((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1);
printf("get sockfd:\n");
sockaddress.sin_family = AF_INET;
//绑定端口
sockaddress.sin_port = htons(8220);
//连接某个ip
sockaddress.sin_addr.s_addr = inet_addr(argv[1]);
//清零
bzero(&(sockaddress.sin_zero),8);
//连接
while(connect(sockfd,(struct sockaddr*)&sockaddress,sizeof(struct sockaddr))==-1);
printf("成功连接到服务器\n");
//开始阻塞
numbers = recv(sockfd,buf,BUFSIZ,0);
buf[numbers]= '\0';
printf("%s",buf);
printf("输入要发送的信息:");
scanf("%s",buf);
numbers = send(sockfd,buf,sizeof(buf),0);
//开始阻塞
numbers = recv(sockfd,buf,BUFSIZ,0);
buf[numbers]= '\0';
printf("接收到回信:%s\n",buf);
close(sockfd);
return 0;
}
vim UDPserver.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SERVER_PORT 8220
#define BUFF_LEN 1024
void handle_udp_msg(int fd)
{
char buf[BUFF_LEN]; //接收缓冲区,1024字节
socklen_t len;
int count;
struct sockaddr_in clent_addr; //clent_addr用于记录发送方的地址信息
while(1)
{
memset(buf, 0, BUFF_LEN);
len = sizeof(clent_addr);
count = recvfrom(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&clent_addr, &len); //recvfrom是拥塞函数,没有数据就一直拥塞
if(count == -1)
{
printf("recieve data fail!\n");
return;
}
printf("client:%s\n",buf); //打印client发过来的信息
memset(buf, 0, BUFF_LEN);
sprintf(buf, "I have recieved %d bytes data!\n", count); //回复client
printf("server:%s\n",buf); //打印自己发送的信息
sendto(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&clent_addr, len); //发送信息给client,注意使用了clent_addr结构体指针
}
}
int main(int argc, char* argv[])
{
int server_fd, ret;
struct sockaddr_in ser_addr;
//AF_INET:IPV4;SOCK_DGRAM:UDP
server_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(server_fd < 0)
{
printf("create socket fail!\n");
return -1;
}
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
ser_addr.sin_port = htons(SERVER_PORT); //端口号,需要网络序转换
ret = bind(server_fd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
if(ret < 0)
{
printf("socket bind fail!\n");
return -1;
}
handle_udp_msg(server_fd); //处理接收到的数据
close(server_fd);
return 0;
}
UDPclient.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define SERVER_PORT 8220
#define BUFF_LEN 512
#define SERVER_IP "127.0.0.1"
void udp_msg_sender(int fd, struct sockaddr* dst)
{
socklen_t len;
struct sockaddr_in src;
while(1)
{
char buf[BUFF_LEN] = "TEST UDP MSG!\n";
len = sizeof(*dst);
printf("client:%s\n",buf); //打印自己发送的信息
sendto(fd, buf, BUFF_LEN, 0, dst, len);
memset(buf, 0, BUFF_LEN);
recvfrom(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&src, &len); //接收来自server的信息
printf("server:%s\n",buf);
sleep(1); //一秒发送一次消息
}
}
int main(int argc, char* argv[])
{
int client_fd;
struct sockaddr_in ser_addr;
client_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(client_fd < 0)
{
printf("create socket fail!\n");
return -1;
}
memset(&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
//作为客户端,要指定远端服务器的(ip, port)
ser_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//仅仅连接服务器IP
ser_addr.sin_port = htons(SERVER_PORT); //注意网络序转换
udp_msg_sender(client_fd, (struct sockaddr*)&ser_addr);
close(client_fd);
return 0;
}
编辑完以上源文件之后用GCC将其编译为可执行代码:
gcc TCPserver.c -o TCPserver
gcc TCPclient.c -o TCPclient
gcc UDPserver.c -o UDPserver
gcc UDPclient.c -o UDPclient
执行完之后开始运行调试:
TCP 测试:
可见其可以正常运行,但用户无法连续发送多个消息以及控制何时结束通信,修改部分代码,使其增加退出功能。
这里的修改很简单:只需要将原发送语句增加一个while循环,并令其结束条件为 接受到用户输入‘q’。
char sendline[4096];
printf("输入'q'以结束通信\n");
while(1)
{
printf("输入要发送的信息:");
fgets(sendline,sizeof(sendline),stdin);
sendline[strlen(sendline)-1] = '\0';
if(strncasecmp(sendline,"q",1) == 0) {
printf("已退出\n");
break;
}
int sentbytes = send(sockfd,sendline,strlen(sendline),0);
printf("sent:%d\n",sentbytes);
}
修改后:
可见其满足题1要求,当程序接收到用户输入的‘q’就会自动断开与服务器的连接。
UDP 测试:
最后验证TCP和UDP两种不同的协议是否可以同时通过相同的端口号进行通信。
由于上述代码已经统一端口号为8220,所以不需修改只需要同时运行两个程序,观察能否同时进行通信即可,测试截图如下:
可见,TCP和UDP两种协议是可以同时在同一ip通过相同的端口号进行通信的。