1.网络编程的概念
在学习了进程之间的通信(管道,消息队列,信号,共享内存,信号量)之后,发现他们都有一个特点,那就是依赖于内核,只能做到在同一台设备中的两个进程进行相互沟通。而网络编程通过某种协议,将想要发送或着接收的数据打包/解析,可以实现通过网络,处于不同终端的进程进行通信。网络编程必须的有两点,数据和地址,地址包括IP地址和端口号。
端口号的作用是用来区分不同服务的。
2. 字节序
字节序是指多字节数据在计算机内存中存储或者网络传输时个字节的存储顺序。
常见序:
- 小端字节序——将低序字节存储在起始地址
- 大端字节序——将高序字节存储在起始地址
网络字节序是大端字节序。
x86系列CPU都是小端字节序。
3. Socket套接字
3.1 TCP/UDP协议
TCP : 该协议面向连接,连接可靠,传输的数据相对比较小。
UDP : 该协议面向报文,连接不可靠,传输的数据量大。
两者对比:
3.2 相关API
开发步骤:
服务器socket开发步骤:
- 创建套接字socket()。
- 为套接字添加信息(IP地址和端口号)bind()。
- 监听网络变化listen()。
- 监听到网络变化,说明有客户端接入,就接受一个连接accept()。
- 数据交互read,write
- 关闭套接字 close()
客户端套接字开发步骤:
- 创建套接字
- 连接服务器connect()
- 读写数据
- 关闭套接字
原型:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
// 参数说明见下图
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);
// 若不关系客户端地址和长度,后两个传NULL
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// IP地址转换API
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// 将点分十进制转换为网络能识别的格式
int inet_aton(const char *cp, struct in_addr *inp);
// 将网络格式的IP地址转为字符串形式
char *inet_ntoa(struct in_addr in);
// 字节序转换API
#include <arpa/inet.h>
// h代表host本地主机,n表示网络,s表示short(两个字节),l表示long(四个字节)
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
收发第二套API:
sendto,recvform等函数多用于UDP协议
简单聊天室代码示例:
// server.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
//#include <linux/in.h>
#include <stdlib.h>
int main(int argc,char **argv) {
int socket_id;
int cnt=0;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
char readBuf[128] = {0};
char msg[128] = {0};
memset(&server_addr, 0, sizeof(struct sockaddr_in));
memset(&client_addr, 0, sizeof(struct sockaddr_in));
if(argc!=3){
printf("argument too lease\n");
exit(-1);
}
socket_id = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &server_addr.sin_addr);
bind(socket_id, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(socket_id, 5);
int clen = sizeof(struct sockaddr_in);
while (1) {
int client_id =
accept(socket_id, (struct sockaddr *)&client_addr, &clen);
if (client_id != -1) {
printf("accept client IP is %s\n", inet_ntoa(client_addr.sin_addr));
}
cnt++;
if (fork() == 0) {
if (fork() == 0) {
while (1) {
memset(msg, 0, sizeof(msg));
sprintf(msg,"NO.%d Client",cnt);
write(client_id, msg, sizeof(msg));
sleep(3);
}
}
while (1) {
memset(readBuf, 0, sizeof(readBuf));
int n_read=read(client_id, readBuf, sizeof(readBuf));
if(n_read>0){
printf("read form %s:%s\n", inet_ntoa(client_addr.sin_addr),
readBuf);
}
}
}
}
close(socket_id);
}
// client.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
//#include <linux/in.h>
#include <stdlib.h>
int main(int argc,char **argv) {
int socket_id;
struct sockaddr_in server_addr;
char readBuf[128] = {0};
char msg[128] = {0};
memset(&server_addr,0,sizeof(struct sockaddr_in));
if(argc!=3){
printf("argument too lease\n");
exit(-1);
}
socket_id = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&server_addr.sin_addr);
if(connect(socket_id,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in))==-1){
perror("connect");
exit(-1);
}
if(fork()==0){
while(1){
memset(msg,0,sizeof(msg));
printf("input:");
gets(msg);
write(socket_id,msg,strlen(msg));
}
}
while(1){
memset(readBuf,0,sizeof(readBuf));
int n_read = read(socket_id,readBuf,sizeof(readBuf));
if(n_read==-1){
perror("read");
exit(-1);
}else if(n_read > 0){
printf("rec mes form IP %s,%s\n",argv[1],readBuf);
}
}
close(socket_id);
}