使用套接字實現簡單TCP伺服器客戶端模型
阿新 • • 發佈:2019-02-08
利用套接字實現一個簡單的TCP伺服器客戶端模型基本步驟如下:
1.建立套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
引數描述:
domian:協議域,AF_INET 對應 ipv4,AF_INET6 對應 ipv6, AF_UNIX 表示這個socket是非網路形式的unix域,可以用來進行非網路形式的程序間通訊。
type:指定socket型別
1.SOCK_STREAM 這個協議是按照順序的、可靠的、資料完整的基於位元組流的連線。 這是一個使用最多的socket型別,這個socket是使用TCP來進行傳輸。
2.SOCK_DGRAM 這個協議是無連線的、固定長度的傳輸呼叫。該協議是不可靠的,使用UDP來進行它的連線。
3.SOCK_RAW 這個socket型別提供單一的網路訪問,這個socket型別使用ICMP公共協議。(ping、traceroute使用該協議)
返回值:
成功返回socket描述符,失敗返回-1
2.將伺服器IP與埠和套接字進行繫結
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
引數描述:
sockfd:通過socket()函式建立,用來表示唯一一個socket
addr:指向要繫結給sockfd的協議地址(協議型別,IP,埠號)
addrlen:對應地址長度
返回值:
成功返回0,失敗返回-1
注意:
在IPv4因特網域AF_INET中,套接字地址用結構sockaddr_in表示,如下:
struct sockaddr_in
{
sa_family_t sin_family; //unsigned short 地址族
in_port_t sin_sport; //uint16_t
struct in_addr sin_addr; //IPv4
};
struct in_addr
{
in_addr_t s_addr; //uint32_t
};
3.監聽請求
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
引數描述:
sockfd:要監聽的套接字
backlog:連線佇列長度
backlog 具體一些是什麼意思呢?每一個連入請求都要進入一個連入請求佇列,等待listen 的程式呼叫accept()函式來接受這個連線。當系統還沒有呼叫accept()函式的時候,如果有很多連線,那麼本地能夠等待的最大數目就是backlog 的數值。
返回值:
成功返回0,失敗返回-1
4.伺服器端接受監聽到的請求
當伺服器端進行監聽到一個連線請求時,就可以使用accept()函式來接受這個請求。
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
引數描述:
socket:伺服器的套接字
addr:用於儲存客戶端的協議地址(協議型別,IP,埠號)
addrlen:地址長度(sizeof(addr))
返回值:
成功返回一個新的套接字描述符,代表與返回客戶端的TCP連線
5.客戶端連線伺服器
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
引數描述:
sockfd:客戶端的sockfd
addr:用於儲存伺服器的協議地址(協議型別,IP,埠號)
addrlen:地址長度(sizeof(addr))
返回值:
成功返回0,失敗返回-1
注意:
在實現過程中還應注意網路位元組序的問題,主機的位元組序是不確定的,有的使用大端儲存,有的使用小端儲存,為了不使網路通訊因為機器之間儲存模式而造成錯誤,TCP/IP協議規定網路資料流使用大端位元組序。
下面庫函式可以完成主機序列和為網路序列的轉換:
#include <arpa/inet.h>
//主機序列到網路序列
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
//網路序列到主機序列
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
server:
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<error.h>
#include<unistd.h>
#include<sys/types.h>
static void Usage(char* proc)
{
printf("Usage: %s [local_ip] [local_port]\n", proc);
}
int startup(char* ip, int port)
{
//1.建立檔案特性套接字
//AF_INET ipv4,SOCK_STREAM,基於位元組流服務
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0)
{
perror("socket");
return 2;
}
printf("sock = %d\n", sock);
struct sockaddr_in local;
//確定地址協議型別
local.sin_family = AF_INET;
//繫結埠
local.sin_port = htons(port);
//繫結ip
//這裡的IP是點分十進位制,使用inet_addr()可以將其轉換成二進位制
local.sin_addr.s_addr = inet_addr(ip);
//2.繫結網路特性將套接字與伺服器ip和埠號繫結
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
{
perror("bind");
return 3;
}
//3.監聽請求
if(listen(sock, 10) < 0)
{
perror("listen");
return 4;
}
return sock;
}
int main(int argc, char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
return 1;
}
//獲取套接字
int listen_sock = startup(argv[1], atoi(argv[2]));
while(1)
{
//用於儲存連線的客戶端地址資訊(地址型別,IP,埠號)
struct sockaddr_in client;
socklen_t len = sizeof(client);
//4.接受監聽到的連線
int new_sock = accept(listen_sock,(struct sockaddr*)&client, &len);
if(new_sock < 0)
{
perror("accept");
return 5;
}
while(1)
{
char buf[1024];
//從套接字讀取資訊到buf中
ssize_t s = read(new_sock, buf, sizeof(buf) - 1);
if(s > 0)
{
buf[s] = 0;
printf("client# %s\n", buf);
printf("Please Enter# ");
fflush(stdout);
//從鍵盤輸入資訊到buf中
ssize_t _s = read(0, buf, sizeof(buf)-1);
buf[_s-1] = 0;
//傳送資訊到套接字
write(new_sock, buf, strlen(buf));
}
else if(s == 0)
{
printf("client colse !!\n");
return 6;
}
else
{
perror("read");
return 7;
}
}
}
return 0;
}
client:
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<sys/types.h>
static void Usage(char *proc)
{
printf("Usage %s [server_ip] [server_port]\n", proc);
}
int main(int argc, char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
return 1;
}
//建立套接字
int sock = socket(AF_INET, SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
return 2;
}
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]);
//將建立的套接字,連入指定的網路服務中,這裡連到了伺服器
if(connect(sock,(struct sockaddr*)&server, sizeof(server)) < 0)
{
perror("connect111");
return 3;
}
char buf[1024];
while(1)
{
printf("Please Enter# ");
fflush(stdout);
//從鍵盤寫入內容到緩衝區
ssize_t s = read(0, buf, sizeof(buf)-1);
if(s >0)
{
buf[s-1] = 0;
printf("server# ");
fflush(stdout);
//將緩衝區內容通過套接字傳送到伺服器
write(sock, buf, strlen(buf));
//在從套接字中讀取伺服器的迴應資訊
ssize_t _s = read(sock, buf, sizeof(buf)-1);
if(_s > 0)
{
buf[_s] = 0;
printf("%s\n", buf);
}
if(s < 0)
{
perror("read");
return 4;
}
}
}
return 0;
}