简单的C/S模型,实现小写转大写的功能
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLEN 64
#define PORT 2344
int main()
{
struct sockaddr_in serveraddr;
int conncetfd;
char buf[MAXLEN] = "";
conncetfd = socket(AF_INET, SOCK_STREAM, 0);
//将结构体初始化,避免脏数据
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET; //协议族:IPV4协议
//htons(host_to_net_short)将16位的本地数据转换成16位网络数据(大小端转换)
serveraddr.sin_port = htons(PORT); //端口
//将字符串转换成网络IPV4地址格式并存入serveraddr.sin_addr中
inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr);
//发起连接,连接成功前阻塞
connect(conncetfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
//循环读取字符串,并交给服务器处理
while(~scanf("%s", buf))
{
//遇到quit则退出循环
if(!strncmp("quit", buf, 4)) break;
write(conncetfd, buf, strlen(buf));
int retlen = read(conncetfd, buf, MAXLEN);
write(1, buf, retlen);
}
//关闭连接,给服务端发送quit让服务端关闭连接
write(conncetfd, "quit", strlen(buf));
close(conncetfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 2344
#define MAXLEN 64
void myperror(char* msg)
{
perror(msg);
exit(1);
}
int main()
{
//服务端从listen之前的操作基本与客户端同理
struct sockaddr_in serveraddr, clientaddr;
int listenfd, conncetfd, ret_status;
char buf[MAXLEN];
socklen_t client_addrlen;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd < 0) myperror("socket");
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(PORT);
serveraddr.sin_addr.s_addr = INADDR_ANY;
ret_status = bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if(ret_status < 0) myperror("bind");
//对 listenfd 进行监听
ret_status = listen(listenfd, 3);
if(ret_status < 0) myperror("listen");
while(1)
{
//监听到新连接,调用accept完成握手,进行数据传输操作
client_addrlen = sizeof(clientaddr);
conncetfd = accept(listenfd, (struct sockaddr *) &clientaddr,
&client_addrlen);
if(conncetfd < 0) myperror("accept");
char client_addrstr[17];
int retlen;
//后面都是数据传输操作,IO方面知识
while((retlen = read(conncetfd, buf, MAXLEN)) > 0)
{
//收到客户端发送的quit信号,关闭连接,结束程序
if(!strncmp("quit", buf, 4)) break;
printf("received from %s:%d\n",
inet_ntop(AF_INET, &clientaddr.sin_addr, client_addrstr, 16),
ntohs(clientaddr.sin_port));
for(int i = 0; i < retlen; i++)
buf[i] = toupper(buf[i]);
ret_status = write(conncetfd, buf, retlen);
if(ret_status < 0) myperror("write");
}
if(retlen < 0) myperror("read");
close(conncetfd);
printf("client close from %s:%d\n",
inet_ntop(AF_INET, &clientaddr.sin_addr, client_addrstr, 16),
ntohs(clientaddr.sin_port));
}
return 0;
}
服务端和客户端初始化 socket ,得到⽂件描述符;
服务端调⽤ bind ,将绑定在 IP 地址和端⼝;
服务端调⽤ listen ,进⾏监听;
服务端调⽤ accept ,等待客户端连接;
客户端调⽤ connect ,向服务器端的地址和端⼝发起连接请求;
服务端 accept 返回⽤于传输的 socket 的⽂件描述符;
客户端调⽤ write 写⼊数据;服务端调⽤ read 读取数据;
客户端断开连接时,会调⽤ close ,那么服务端 read 读取数据的时候,就会读取到了 EOF ,待处理完数据后,服务端调⽤ close ,表示连接关闭。这里我们特殊处理读到quit就可以关闭连接了。
这⾥需要注意的是,服务端调⽤ accept 时,连接成功了会返回⼀个已完成连接的 socket,后续⽤来传输数据。
struct sockaddr
、struct sockaddr_in
、struct sockaddr_un
struct sockaddr
是通用的套接字地址,使用系统调用如bind的时候其中一个参数的参数类型就是这个。
struct sockaddr
{
unsigned short sa_family; /* 协议族, AF_xxx */
char sa_data[14]; /* 14字节的协议地址 */
};
而 struct sockaddr_in
则是 internet
环境下套接字的地址形式,二者长度一样,都是16个字节。二者是并列结构,指向 sockaddr_in
结构的指针也可以指向 sockaddr
。一般情况下,需要把 sockaddr_in
结构强制转换成 sockaddr
结构再传入系统调用函数中,比如bind。
struct sockaddr_in {
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* 网络地址 */
unsigned char sin_zero[8]; /* 八字节的保留位 */
};
struct sockaddr_un
用于本地 socket 结构,结构如下,用法和struct sockaddr_in
类似。
struct sockaddr_un
{
sa_family_t sun_family; /*地址族,PF_UNIX或AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* 路径名 */
};
htons()
、htonl()
、ntohs()
、ntohl()
htonl()
– “Host to Network Long” – “对32位数据进行本地到网络的字节序转换”
ntohl()
– “Network to Host Long” – “对32位数据进行网络到本地的字节序转换”
htons()
– “Host to Network Short” – “对16位数据进行本地到网络的字节序转换”
ntohs()
– “Network to Host Short” – “对16位数据进行网络到本地的字节序转换”
因为Linux大多是小端,而网络上的机器都是大端,在网络上发送数据需要进行字节序转换,否则可能会发生通信异常。
inet_ntop()
、inet_pton()
、inet_addr()
(1)int inet_pton(int family, const char *strptr, void *addrptr);
将点分十进制的ip地址转化为用于网络传输的数值格式
返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
(2)const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
将数值格式转化为点分十进制的ip地址格式
返回值:若成功则为指向结构的指针,若出错则为NULL
这两个函数是随IPv6出现的函数,对于IPv4地址和IPv6地址都适用,函数中p和n分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构的二进制值。
(3) in_addr_t inet_addr(const char *cp);
inet_addr
函数转换网络主机地址(如192.168.1.10)为网络字节序二进制值,如果参数char *cp
无效,函数返回-1( INADDR_NONE
),这个函数在处理地址为255.255.255.255时也返回-1,255.255.255.255是一个有效的地址,不过inet_addr
无法处理
小林coding —— 图解网络
inet_pton()和inet_ntop()函数详解
struct sockaddr struct sockaddr_in struct sockaddr_un 结构详解