根據UDP協議的特性,我們實現一個簡單的回顯服務器。
UDP協議:
傳輸層協議
無連接
不可靠
面向數據報
服務器代碼:
///////////////
// UDP版本的服務器
// 1.綁定端口號
// 2.服務器7*24小時等待客戶端發送的請求
// 3.把響應發回客服端
//////////////
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
void service(int sock)
{
char buf[128];
for(;;)
{
//臨時的sockaddr_in 結構體接收
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
buf[0] = 0;
//接收客戶端發來的數據 數據存在buf
ssize_t s = recvfrom(sock,buf,sizeof(buf)-1,\
0,(struct sockaddr*)&peer,&len);
if(s > 0)
{
buf[s] = 0;
//打印從哪個客戶端接收到的數據
printf("[%s,%d] # %s\n",inet_ntoa(peer.sin_addr),\
ntohs(peer.sin_port),buf);
//把響應發送給客戶端 buf
sendto(sock,buf,strlen(buf),0,\
(struct sockaddr*)&peer,len);
}
}
}
// ./server 0 8080
int main(int arg,char *argv[])
{
if(arg != 3)
{
printf("usage: %s [ip] [port]\n",argv[0]);
return 1;
}
//SOCK_DGRAM表示UDP ipv4: AF_INETET
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock < 0)
{
printf("socket error!\n");
return 2;
}
//初始化sockaddr_in local IPV4: AF_INET
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(atoi(argv[2]));
//字符串IP地址 轉換成 in_addr_t :inet_addr() 看博主博客的地址轉換函數的介紹
local.sin_addr.s_addr = inet_addr(argv[1]);
//綁定端口號
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
printf("bind error\n");
return 3;
}
//業務 --- 回顯
service(sock);
close(sock);
return 0;
}
客戶端代碼:
// UDP 版本的客戶端
// 1. 從標準輸入, 讀入一個字符串
// 2. 把這個字符串通過 socket 發送給服務器
// 3. 嘗試從服務器讀取響應數據
// 4. 把響應結果寫到標準輸出上.
///////////////////////////////////////////////////////
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
//client 127.0.0.1 8080
int main(int arg,char *argv[])
{
if(arg != 3)
{
printf("usage: %s [ip] [port]\n",argv[0]);
return 1;
}
//創建sock < 0 創建失敗 SOCK_DGRAM表示UDP ipv4: AF_INETET
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock < 0)
{
printf("socket error\n");
return 2;
//初始化 sockaddr_in ipv4: AF_INETET
struct sockaddr_in server;
server.sin_family = AF_INET;
//端口號:atoi 字符轉換成數字
server.sin_port = htons(atoi(argv[2]));
//IP地址轉換函數 ----看博主博客地址轉換函數的介紹
server.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t len = sizeof(server);
//緩衝區
char buf[128];
//回顯服務器,死循環
for(;;)
{
buf[128] = 0;
//從標準輸入中讀取存放在buf sizeof(buf)-1 是爲了最後爲一個字節爲0
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s-1] = 0;
//發送數據
sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&server,len);
//接受數據
s = recvfrom(sock,buf,sizeof(buf)-1,0,NULL,NULL);
if(s > 0)
{
buf[s] = 0;
printf("server echo# %s\n",buf);
}
}
}
close(sock);
return 0;
}
運行結果:
client:
server:
ps:
發送函數:
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd: socket
const void *buf: 緩衝區地址
size_t len:緩衝區的大小,buf的大小
int flags: 0
const struct sockaddr *dest_addr,結構體的地址
socklen_t addrlen : 結構體大小
接收函數:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
int sockfd:socket
void *buf:緩衝區地址
size_t len:緩衝區大小
int flags:0
struct sockaddr *src_addr:結構體地址
socklen_t *addrlen:結構體大小