目录
一、理论
二、编程实战
1. 网络编程
前面学习的管道,消息队列,共享内存,信号,信号量,都依耐于linux内核,无法实现多机通讯,所以要学习网络编程,实现服务器和客户端之间的通讯。
地址:IP地址
???????????端口号:ftp服务/http服务/socket服务
数据:协议(http、tcp、udp)
socket套接字:tcp:面向连接,A打电话B,连接可靠,传指令
? ? ? ? ? ? ? ? ? ? ? ? udp:面向报文,A发短信B,连接不可靠,视频,数据量大
tcp与udp对比:
tcp:面向连接,连接可靠,传指令,点到点,首部开销20个字节,全双工,可靠通信
udp:面向报文,连接不可靠,传视频,一对多、多对一、多对多,首付开销8个字节,不可靠通信
2. 字节序
字节序是指多个字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
小端字节序(little endian):将低序字节存储在起始地址
大端字节序(big endian):将高序字节存储在起始地址
3. socket是一种独立于协议的网络编程接口,即套接字,是一个抽象套,应用程序可以通过它发送或接收数据,可对其像文件一样打开,读写和关闭等操作,套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信,网络套接字是IP地址与端口的组合,为了满足不同的通信程序,对通信质量和性能的要求,一般的网络系统提供了三种不同类型的套接字,以供用户在设计网络应用程序时,根据不同的要求来选择,为流式套接字、数据套接字、原始套接字。
socket接口是tcp/ip网络的api
server.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main(int argc , char **argv)
{
//int socket(int domain, int type, int protocol);
int s_fd;
int c_fd;
int n_read;
char readBuf[128];
char *msg = "I get your connect";
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1. socket
//s_fd = socket(AF_LOCAL,SOCK_STREAM,0);
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1){
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
//inet_aton("127.0.0.1",&s_addr.sin_addr);
//2. bind
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3. listen
listen(s_fd,10);
//4. accept
int clen = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
if(c_fd == -1){
perror("accept");
}
//printf("contend");
printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
if(fork() == 0){
//5. read
n_read = read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}else{
printf("get message: %d,%s\n",n_read,readBuf);
}
//6. write
write(c_fd,msg,strlen(msg));
break;
}
}
return 0;
}
client.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char **argv)
{
//int socket(int domain, int type, int protocol);
int c_fd;
int n_read;
char readBuf[128];
char *msg = "msg from client";
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1. socket
// s_fd = socket(AF_LOCAL,SOCK_STREAM,0);
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_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);
//inet_aton("127.0.0.1",&s_addr.sin_addr);
//2. connect
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1 ){
//perror("connect have cuo wu\n");
perror("connect \n");
exit(-1);
}
//printf("contend");
printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));
//3. send
write(c_fd,msg,strlen(msg));
//4. read
n_read = read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}else{
printf("get message from server: %d,%s\n",n_read,readBuf);
}
return 0;
}
CLC@Embed_Learn:~/socket$ gcc server.c -o server
CLC@Embed_Learn:~/socket$ gcc client.c -o client ?
CLC@Embed_Learn:~/socket$ ./server 127.0.0.1 8888 get connect: 127.0.0.1 get message: 15,msg from client get connect: 127.0.0.1 get message: 15,msg from client
......没有退出 ?
CLC@Embed_Learn:~/socket$ ./client 127.0.0.1 8888 get connect: 127.0.0.1 get message from server: 18,I get your connect CLC@Embed_Learn:~/socket$ ./client 127.0.0.1 8888 get connect: 127.0.0.1 get message from server: 18,I get your connect CLC@Embed_Learn:~/socket$?? 退出
服务端,在bind()函数中设置好IP号和端口号,先使用监听listen()函数,在使用连接accept()函数,read()函数读客户端的数据,并显示出来,write()函数给客户端返回一个指令状态。服务端不会退出,有一个while()函数,还使用fork()函数,客户端一运行服务端就起来了。
客户端,先使用write()函数,后使用read()函数,与服务端的read和write刚好一一对应起来。我写你读,我读你写。
双方可以实现通信
server1.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main(int argc , char **argv)
{
//int socket(int domain, int type, int protocol);
int s_fd;
int c_fd;
int n_read;
char readBuf[128];
char msg[128] = {0};
// char *msg = "I get your connect";
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1. socket
// s_fd = socket(AF_LOCAL,SOCK_STREAM,0);
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1){
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
//inet_aton("127.0.0.1",&s_addr.sin_addr);
//2. bind
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3. listen
listen(s_fd,10);
//4. accept
int clen = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
if(c_fd == -1){
perror("accept");
}
//printf("contend");
printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));
//数据有到来时,创建一个上面的子进程去操作,子进程同时执行两个while(1),按照以前的经验,想要执行两个while(1),要么用线程,要么创建一个下面的子进程,
//两个while(1)同时跑,上面的子进程对接的是一个客户端,它既能不断的来检测,往套接字上面发送数据,也能读取套接字上面的数据。
if(fork() == 0){
if(fork() == 0){
while(1){
memset(msg,0,sizeof(msg));
printf("input: ");
gets(msg);
//6. write
write(c_fd,msg,strlen(msg));
}
}
while(1){
memset(readBuf,0,sizeof(readBuf));
//5. read
n_read = read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}else{
printf("get message: %d,%s\n",n_read,readBuf);
}
}
break;
}
}
return 0;
}
?client1.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char **argv)
{
//int socket(int domain, int type, int protocol);
int c_fd;
int n_read;
char readBuf[128];
// char *msg = "msg from client";
char msg[128] = {0};
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
//1. socket
// s_fd = socket(AF_LOCAL,SOCK_STREAM,0);
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_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);
//inet_aton("127.0.0.1",&s_addr.sin_addr);
//2. connect
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1 ){
//perror("connect have cuo wu\n");
perror("connect \n");
exit(-1);
}
while(1){//客户端:while(1)中要执行两个while(1),要么用线程,要么用创建一个子进程,这么创建了一个子进程来完成这个操作
if(fork() == 0){
while(1){
memset(msg,0,sizeof(msg));
printf("input: ");
gets(msg);
//write
write(c_fd,msg,strlen(msg));
}
}
while(1){
memset(readBuf,0,sizeof(readBuf));
//4. read
n_read = read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}else{
printf("get message from server: %d,%s\n",n_read,readBuf);
}
}
}
//printf("contend");
//printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));
//3. send
write(c_fd,msg,strlen(msg));
return 0;
}
CLC@Embed_Learn:~/socket$ gcc server1.c -o server1 CLC@Embed_Learn:~/socket$ gc client1.c -o client1 ?
CLC@Embed_Learn:~/socket$ ./server1 127.0.0.1 8888 get connect: 127.0.0.1 input: get message: 12,nihao client nihao server input: hello server input: get message: 12,hello client?
CLC@Embed_Learn:~/socket$ ./client1 127.0.0.1 8888 input: nihao,client input: get message from server: 12,nihao,server get message from server: 12,hello server hello client
可以实现双方互相聊天,那么可以实现多人聊天吗?请看server2.c
?server2.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main(int argc , char **argv)
{
//int socket(int domain, int type, int protocol);
int s_fd;
int c_fd;
int n_read;
char readBuf[128];
int mark = 0;
char msg[128] = {0};
// char *msg = "I get your connect";
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1. socket
// s_fd = socket(AF_LOCAL,SOCK_STREAM,0);
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1){
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
//inet_aton("127.0.0.1",&s_addr.sin_addr);
//2. bind
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3. listen
listen(s_fd,10);
//4. accept
int clen = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);
if(c_fd == -1){
perror("accept");
}
//printf("contend");
mark++;
printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));
if(fork() == 0){
if(fork() == 0){
while(1){
sprintf(msg,"welcom No.%d client",mark);
//6. write
write(c_fd,msg,strlen(msg));
sleep(3);
}
}
while(1){
memset(readBuf,0,sizeof(readBuf));
//5. read
n_read = read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}else{
printf("get message: %d,%s\n",n_read,readBuf);
}
}
break;
}
}
return 0;
}
client1.c
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char **argv)
{
//int socket(int domain, int type, int protocol);
int c_fd;
int n_read;
char readBuf[128];
// char *msg = "msg from client";
char msg[128] = {0};
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
//1. socket
// s_fd = socket(AF_LOCAL,SOCK_STREAM,0);
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_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);
//inet_aton("127.0.0.1",&s_addr.sin_addr);
//2. connect
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1 ){
//perror("connect have cuo wu\n");
perror("connect \n");
exit(-1);
}
while(1){//客户端:while(1)中要执行两个while(1),要么用线程,要么用创建一个子进程,这么创建了一个子进程来完成这个操作
if(fork() == 0){
while(1){
memset(msg,0,sizeof(msg));
printf("input: ");
gets(msg);
//write
write(c_fd,msg,strlen(msg));
}
}
while(1){
memset(readBuf,0,sizeof(readBuf));
//4. read
n_read = read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}else{
printf("get message from server: %d,%s\n",n_read,readBuf);
}
}
}
//printf("contend");
//printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));
//3. send
write(c_fd,msg,strlen(msg));
return 0;
}
CLC@Embed_Learn:~/socket$ gcc server2.c -o server2 CLC@Embed_Learn:~/socket$ gcc client1.c -o client1
CLC@Embed_Learn:~/socket$ ./server2 127.0.0.1 8877 get connect: 127.0.0.1 get message: 5,nihao? get connect: 127.0.0.1
CLC@Embed_Learn:~/socket$ ./client1 127.0.0.1 8877 input: get message from server: 18,welcom No.1 client get message from server: 18,welcom No.1 client get message from server: 18,welcom No.1 client nihao get message from server: 18,welcom No.1 client
input: get message from server: 18,welcom No.1 client get message from server: 18,welcom No.1 client get message from server: 18,welcom No.1 client
......
CLC@Embed_Learn:~/socket$ ./client1 127.0.0.1 8877 input: get message from server: 18,welcom No.2 client get message from server: 18,welcom No.2 client get message from server: 18,welcom No.2 client ......
1 ?可以多人聊天,但存在竞争关系
2 ?存在资源竞争,所以服务器发送的消息,不能同时给两个客户端发过去,现在让服务端自动给客户端回复消息。 ? ??如果我们不获取键盘的输入,每一个客户端发数据上来,自动的回复,客户端就知道连接有没有丢失。服务端都可以收到客户端的请求。
?
?
|