linux网络编程socket套接字
文章目录
前言
本章分享linux下的网络编程(socket)套接字,如何编写一个简易的服务端和客户端,并实现双方通信。
提示:以下是本篇文章正文内容,下面案例可供参考
3
一、网络编程概述
1)TCP/UDP对比
1.TCP面向连接(如打电话要先拨号建立连接),UDP是无连接的,即发送数据之前不需要建立连接。
2.TCP提供可靠的服务也就是说通过TCP连接传送的数据,无差错,不丢失,不重复,并且按序到达,UDP尽最大努力交付,即不保证可靠交付。
3.TCP面向字节流,实际上是TCP把数据看成一串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速度降低(对实时应用很有用,如IP电话,实时视频会议等)。
4.每一条TCP连接只能是点到点的;UDP支持一对多,一对一,和多对一,多对多的交互通信。
5.TCP首部开销20字节;UDP首部开销小只有8个字节。
6.TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。
(二)端口号的作用
一台用于IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等。
这些服务完全可以提供1个IP地址来实现。那么主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因此IP地址与网络服务的关系是一对多的关系。
实际上是通过IP地址+端口号来区分不同的额服务的。端口提供了一种访问通道。
服务器一般都是通过知名端口号来识别的,比如:对于每一个TCP/IP实现来说FTP端口号都是21,每一个TeLnet服务器的TCP端口号都是23号,每一个TFTP(简单文件传送协议)服务器的UDP端口号都是69。
二、字节序
Little endian:将低序字节存储在起始地址 (小端模式)
Big endian:将高序字节存储在起始地址 (大端模式)
网络字节序=大端字节序
二、socket编程步骤
基于socket的网络编程对于服务端和客户端的实现有以下简单步骤:
2.1 服务端的编程
1、创建套接字
2、绑定
3、监听
4、接收
5、读写操作
6、关闭2.2 客户端的编程
1、创建套接字
2、连接
3、读写操作
4、关闭
各函数的使用同样我们还是通过man手册来查看,每个函数的具体参数也不介绍了,使用的时候看看man 手册中的介绍就知道了。
各操作函数原型如下:
//创建套接字
int socket(int domain, int type, int protocol);
//绑定
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//监听
int listen(int sockfd, int backlog);
//接收
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//读数据
ssize_t read(int fd, void *buf, size_t count);
//写数据
ssize_t write(int fd, const void *buf, size_t count);
//关闭
int close(int fd);
//连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
note:以上就是在编程中需要用到的一些函数。
三、socket编程服务端、客户端的实现
在进行编程时我们有以下需要注意的:
1、在进行socket结构体成员配置时 我们是以struct sockaddr_in这个优化结构体进行配置,而这个原本使用的是struct sockaddr 所以在函数中进行传参是需要我们进行强制类型转换成(struct sockaddr *)类型
2、进行查找小技巧
进入 /usr/include 下进行相关查找
譬如:我们想要查看struct sockaddr_in 中的结构体成员
grep “struct sockaddr_in {” * -nir 通过grep管道进行字符串的查看
3、网络字节格式相关转换
主机转网络
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
网络转主机
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
ip地址之间的转换
int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);
下面进入编程正题吧
3.1 服务端实现
#include <stdio.h>
#include <error.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
//搜索技巧 grep “struct sockaddr_in" * -nir
//注意网络字节序转换 地址转网络 htons inet_aton
/*-------------------------------------服务端-------------------------------------*/
int main(void)
{
char readbuf[128] = {0}; //接收数据
char writebuf[128] = "message from server"; //发送数据
int s_fd = 0,c_fd = 0;
int n_read = 0,size = 0;
struct sockaddr_in server,client;
//创建套接字
// int socket(int domain, int type, int protocol);
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
printf("socket failed\n");
exit(-1);
}
size = sizeof(struct sockaddr);
//清空
memset(&server,0,sizeof(struct sockaddr_in));
memset(&client,0,sizeof(struct sockaddr_in));
//结构体初始化
server.sin_family = AF_INET;
server.sin_port = htons(8080);
// int inet_aton(const char *cp, struct in_addr *inp);
inet_aton("127.0.0.1",&server.sin_addr);
//绑定
//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
if(bind(s_fd,(struct sockaddr *)&server,sizeof(struct sockaddr_in)) == -1)
{
perror("bind:");
exit(-1);
}
// int listen(int sockfd, int backlog);
//监听
iif(listen(s_fd,10) == -1)
{
perror("listen:");
exit(-1);
}
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//接收
c_fd = accept(s_fd,(struct sockaddr *)&client,&size);
if(c_fd == -1)
{
printf("accept failed\n");
exit(-1);
}
printf("connect scuuess client ip:%s\n",inet_ntoa(server.sin_addr));
//读写操作
// ssize_t read(int fd, void *buf, size_t count);
n_read = read(c_fd,readbuf,sizeof(readbuf));
if(n_read == -1)
{
printf("read failed\n");
exit(-1);
}
else
{
printf("data:%s\n",readbuf);
}
write(c_fd,writebuf,sizeof(writebuf));
//关闭
close(s_fd);
close(c_fd);
return 0;
}
3.2 客户端实现
#include <stdio.h>
#include <error.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
//搜索技巧 grep “struct sockaddr_in" * -nir
//注意网络字节序转换 地址转网络 htons inet_aton
/*----------------------------------------客户端-------------------------------------*/
int main(void)
{
char readbuf[128] = {0}; //接收数据
char writebuf[128] = "message from client"; //发送数据
int c_fd = 0,ret = 0;
struct sockaddr_in client;
//创建套接字
// int socket(int domain, int type, int protocol);
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1)
{
printf("socket failed\n");
exit(-1);
}
//清空
memset(&client,0,sizeof(struct sockaddr_in));
//结构体初始化
client.sin_family = AF_INET;
client.sin_port = htons(8080);
// int inet_aton(const char *cp, struct in_addr *inp);
inet_aton("127.0.0.1",&client.sin_addr);
//连接 int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
if(connect(c_fd,(struct sockaddr *)&client,sizeof(struct sockaddr)) == -1)
{
perror("connect:");
exit(-1);
}
printf("connect success current ip:%s\n",inet_ntoa(client.sin_addr));
//读写操作
// ssize_t read(int fd, void *buf, size_t count);
write(c_fd,writebuf,sizeof(writebuf));
read(c_fd,readbuf,sizeof(readbuf));
printf("data:%s\n",readbuf);
//关闭
close(c_fd);
return 0;
}
3.3 实验结果
四、socket编程实现多方消息收发
上面实验中实现了简单的收发操作但时不灵活,ip和端口写的太固定并且还没有实现连续收发的效果,下面实验进行优化实现通过用户自己输入ip和端口 支持多发消息的收发。
4.1 服务端编写
#include <stdio.h>
#include <error.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
//搜索技巧 grep “struct sockaddr_in" * -nir
//注意网络字节序转换 地址转网络 htons inet_aton
/*-------------------------------------服务端-------------------------------------*/
int main(int argc,char *argv[])
{
char readbuf[128] = {0}; //接收数据
char writebuf[128] = {0}; //发送数据
int s_fd = 0,c_fd = 0;
int n_read = 0,size = 0;
struct sockaddr_in server,client;
if(argc != 3)
{
printf("param error\n");
exit(-1);
}
//创建套接字
// int socket(int domain, int type, int protocol);
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
printf("socket failed\n");
exit(-1);
}
size = sizeof(struct sockaddr);
//清空
memset(&server,0,sizeof(struct sockaddr_in));
memset(&client,0,sizeof(struct sockaddr_in));
//结构体初始化
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
// int inet_aton(const char *cp, struct in_addr *inp);
inet_aton(argv[1],&server.sin_addr);
//绑定
//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
if(bind(s_fd,(struct sockaddr *)&server,sizeof(struct sockaddr_in)) == -1)
{
perror("bind:");
exit(-1);
}
// int listen(int sockfd, int backlog);
//监听
if(listen(s_fd,10) == -1)
{
perror("listen:");
exit(-1);
}
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//接收
while(1)
{
c_fd = accept(s_fd,(struct sockaddr *)&client,&size);
if(c_fd == -1)
{
printf("accept failed\n");
exit(-1);
}
printf("connect scuuess client ip:%s\n",inet_ntoa(server.sin_addr));
if(fork() == 0)
{
if(fork() == 0)
{
while(1)
{
//输入数据
memset(writebuf,0,sizeof(writebuf));
printf("please input data:");
gets(writebuf);
write(c_fd,writebuf,sizeof(writebuf));
}
}
while(1)
{
//读取数据
memset(readbuf,0,sizeof(readbuf));
read(c_fd,readbuf,sizeof(readbuf));
printf("get data:%s\n",readbuf);
}
}
}
//关闭
close(s_fd);
close(c_fd);
return 0;
}
4.2 客户端编写
#include <stdio.h>
#include <error.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
//搜索技巧 grep “struct sockaddr_in" * -nir
//注意网络字节序转换 地址转网络 htons inet_aton
/*----------------------------------------客户端-------------------------------------*/
int main(int argc,char *argv[])
{
char readbuf[128] = {0}; //接收数据
char writebuf[128] = {0}; //发送数据
int c_fd = 0,ret = 0;
struct sockaddr_in client;
if(argc != 3)
{
printf("param error\n");;
exit(-1);
}
//创建套接字
// int socket(int domain, int type, int protocol);
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1)
{
printf("socket failed\n");
exit(-1);
}
//清空
memset(&client,0,sizeof(struct sockaddr_in));
//结构体初始化
client.sin_family = AF_INET;
client.sin_port = htons(atoi(argv[2]));
// int inet_aton(const char *cp, struct in_addr *inp);
inet_aton(argv[1],&client.sin_addr);
while(1)
{
//连接 int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
if(connect(c_fd,(struct sockaddr *)&client,sizeof(struct sockaddr)) == -1)
{
perror("connect:");
exit(-1);
}
printf("connect success current ip:%s\n",inet_ntoa(client.sin_addr));
if(fork() == 0)
{
//读写操作
while(1)
{
//输入数据
memset(writebuf,0,sizeof(writebuf));
printf("please input data:");
gets(writebuf);
write(c_fd,writebuf,sizeof(writebuf));
}
// ssize_t read(int fd, void *buf, size_t count);
}
while(1)
{
//读取数据
memset(readbuf,0,sizeof(readbuf));
read(c_fd,readbuf,sizeof(readbuf));
printf("get data:%s\n",readbuf);
}
}
//关闭
close(c_fd);
return 0;
}
4.3 实验结果
总结
上面对linux下网络编程进行了简单分享,对处理多方消息收发中并没有处理很好,如果多个客户接入时,这时候服务端发送时可能接收消息的不是指定的客户,因为每个客户端接入后他们之间的关系是竞争关系。后面可能要用到多并发处理,学了之后再进行优化改进吧。加油