一.网络编程概述
网络中进程间的通信首先需要识别进程所在主机在网络中的唯一标识即网络层的IP地址主机上的进程可以通过传输层的协议与端口号识别。
与单机的进程间通信的区别:
进程间通信包括管道、消息队列、共享内存、信号以及信号量。特点都是依赖于linux内核。所以缺点就是无法进行多机通信。
网络编程注意点:
(1)地址:ip地址和端口号 ;(2)协议(http/tcp/udp…);
1.socket网络编程(套接字):
Socket是应用层与TCP/IP协议族通信的中间软件抽象层是一种编程接口。Socket屏蔽了不同网络协议的差异支持面向连接(Transmission Control Protocol - TCPIP)和无连接(User Datagram Protocol-UDP 和 Inter-Network Packet Exchange-IPX)的传输协议。
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则是不可靠信道。
2.端口号作用
一台拥有ip地址的主机可以提供许多服务,比如web服务、FTP服务、SMTP服务等;这些服务完全可以通过一个ip地址来实现。
那么主机是怎么区分不同的网络服务呢?
显然不能只靠ip地址,因为ip地址和网络服务的关系是一对多。
实际上通过“IP地址+端口号”来区分不同的服务。
端口提供一种访问通道,服务器一般都是通过知名端口号来识别的。例如:对于每个TCP/IP实现来说,FTP的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TPTP(简单文件传送协议)服务器的UDP端口号都是69.
二.字节序
1.概述
字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
2.常见字节序
(1)Little endian 小端字节序
将低序字节存储在起始地址。(x86系列CPU都是little-endian的字节序)
(2)Big endian 大端字节序
将高序字节存储在起始地址
网络字节序 = 大端字节序
三.Socket 编程逻辑
Socket编程的一般流程如下:
四.socket常用api
1、socket
int socket(int domain, int type, int protocol);
创建一个Socket
2、bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
把一个地址族中的特定地址赋给socket.
3、listen
int listen(int sockfd, int backlog);
设置sockfd套接字为监听套接字.
4、accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
接收客户端的请求建立连接套接字.
5、connect
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
6、数据传输操作
ssize_t read(int fd, void *buf, size_t count);
read函数是负责从连接套接字fd中读取内容。
还有wirte函数与文件操作一样。也可以用send和recv函数实现数据收发。
7、close
int close(int fd);
关闭断开连接套接字
fd参数表示要断开的连接套接字
8.地址转换API
int inet_aton(const char *cp, struct in_addr *inp);
把字符串形式的“192.168.1.123”转为网络能识别的形式。
char *inet_ntoa(stuct in_addr in);
把网络格式的ip地址转为字符串形式。
9.字节序转API
五.代码示例
实现服务端与客户端通信
服务端代码:
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void listen_con(int c_fd, int id)
{
int n_read;
char readbuf[128] = {0};
char msg[128] = {0};
//如果服务端需要发送信息,此处添加发送信息
if(fork() == 0)
{
while(1)
{
memset(msg,0,sizeof(msg));
scanf("%s",msg);
write(c_fd,msg,strlen(msg));
}
}
//一直处于监听客户端信息状态,直到客户端断开或客户端发出退出信息
while(1)
{
memset(readbuf,0,sizeof(readbuf));//
n_read = read(c_fd,readbuf,128);
if(n_read == -1)
{
printf("client %d close!\n",id);
}
else
{
if(strcmp(readbuf,"exit\n") == 0)
{
printf("id %d exited.\n",id);
break;
}
printf("ID_%d:%s\n",id,readbuf); //输出第N 个用户,输出>的信息
}
}
close(c_fd); //关闭此客户端的连接的socket
}
int main(int argc,char **argv)
{
int s_fd,c_fd;
int c_size,mark=0;
struct sockaddr_in my_addr; //本地连接socked
struct sockaddr_in c_addr; //客户端连接socked
//1.socket
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
//记录本地信息
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&my_addr.sin_addr);
//绑定socket, 用 bind
if(bind(s_fd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr)) == -1)
{
perror("bind");
exit(-1);
}
//开启监听,可连接客户端最大为10个
listen(s_fd,10);
//服务端一直运行,等待客户端连接
while(1)
{
//accept
c_size = sizeof(struct sockaddr_in);
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_size);
if(c_fd == -1)
{
perror("accept");
exit(-1);
}
printf("get connect:%d\n",++mark);
//连接成功后,客户端数目加1
// 开辟子进程 大于0,为父;等于0,为子;小于0,报错
// 子进程运行客户端
if(fork() == 0)
{
close(s_fd); //子进程中不再需要s_fd去监听,这里释放它,>只需要new_fd即可
//交流
listen_con(c_fd,mark);
exit(0); //正常退出
}
//父进程继续执行while,在accept()等待客户端。父进程的socked此>时还在运行,没有关闭
}
//关闭所有客户端
close(s_fd);
printf("server-------closed.\n");
return 0;
}
客户端代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char **argv)
{
int s_fd;
int n_read;
char msg[128] = {0};
char readbuf[128] = {0};
struct sockaddr_in c_addr;
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
//记录信息
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
// 使用connect连接服务器,c_addr获取服务器信息
if(connect(s_fd,(struct sockaddr*)&c_addr,sizeof(struct sockaddr)) == -1)
{
perror("connect error!\n");
exit(1);
}
while(1)
{
if(fork() == 0)
{
while(1)
{
memset(msg,0,sizeof(msg));
printf("client-----:");
scanf("%s",msg);
write(s_fd,msg,strlen(msg));
}
}
while(1)
{
memset(readbuf,0,sizeof(readbuf));
n_read = read(s_fd,readbuf,128);
if(n_read == -1)
{
printf("client close\n");
}
else
{
printf("s: %s\n",readbuf);
}
}
}
close(s_fd);
return 0;
运行结果: