从“hello”开始去分析linux下的socket编程
来源:互联网 发布:电信网络诈骗类型 编辑:程序博客网 时间:2024/06/05 10:29
如题,从一个简单示例去分析socket的编程。
首先给出源码
server.c
#include <stdlib.h>#include <sys/types.h>#include <stdio.h>#include <sys/socket.h>#include <netinet/in.h>#include <string.h>int main(){int sfp,nfp;struct sockaddr_in s_add,c_add;int sin_size;unsigned short portnum=0x8888;printf("Hello,welcome to my server !\r\n");sfp = socket(AF_INET, SOCK_STREAM, 0);if(-1 == sfp){ printf("socket fail ! \r\n"); return -1;}printf("socket ok !\r\n");bzero(&s_add,sizeof(struct sockaddr_in));s_add.sin_family=AF_INET;s_add.sin_addr.s_addr=htonl(INADDR_ANY);s_add.sin_port=htons(portnum);if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr))){ printf("bind fail !\r\n"); return -1;}printf("bind ok !\r\n");if(-1 == listen(sfp,5)){ printf("listen fail !\r\n"); return -1;}printf("listen ok\r\n");while(1){sin_size = sizeof(struct sockaddr_in);nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);if(-1 == nfp){ printf("accept fail !\r\n"); return -1;}printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n",ntohl(c_add.sin_addr.s_addr),ntohs(c_add.sin_port));if(-1 == write(nfp,"hello,welcome to my server \r\n",32)){ printf("write fail!\r\n"); return -1;}printf("write ok!\r\n");close(nfp);}close(sfp);return 0;}client.c
#include <stdlib.h>#include <sys/types.h>#include <stdio.h>#include <sys/socket.h>#include <netinet/in.h>#include <string.h>int main(){int cfd;int recbytes;int sin_size;char buffer[1024]={0};struct sockaddr_in s_add,c_add;unsigned short portnum=0x8888;printf("Hello,welcome to client !\r\n");cfd = socket(AF_INET, SOCK_STREAM, 0);if(-1 == cfd){ printf("socket fail ! \r\n"); return -1;}printf("socket ok !\r\n");bzero(&s_add,sizeof(struct sockaddr_in));s_add.sin_family=AF_INET;s_add.sin_addr.s_addr= inet_addr("192.168.3.124");s_add.sin_port=htons(portnum);printf("s_addr = %#x ,port : %#x\r\n",s_add.sin_addr.s_addr,s_add.sin_port);if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr))){ printf("connect fail !\r\n"); return -1;}printf("connect ok !\r\n");if(-1 == (recbytes = read(cfd,buffer,1024))){ printf("read data fail !\r\n"); return -1;}printf("read ok\r\nREC:\r\n");buffer[recbytes]='\0';printf("%s\r\n",buffer);getchar();close(cfd);return 0;}
gcc server.c -o server
gcc client.c -o client
下面对服务器端和客户端进行分析
server.c中
------------------------------------------------------------------------
第一个函数
sfp = socket(AF_INET, SOCK_STREAM, 0);
domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和 AF_INET 等).
AF_UNIX 只能够用于单一的 Unix 系统进程间通信,而AF_INET 是针对 Internet 的,因而可以允
许在远程 主机之间通信(当我们man socket 时发现 domain 可选项是 PF_*而不是AF_*,因
为 glibc 是 posix 的实现 所以用 PF 代替了 AF,不过我们都可以使用的).
type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM等)SOCK_STREAM
表明我们用的是 TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流.SOCK_DGRAM
表明我们用的是 UDP 协议,这样只会提供定长的,不可靠,无连接的通信.
protocol:由于我们指定了type,所以这个地方我们一般只要用0 来代替就可以了
socket 为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看errno 可知道出错
的详细情况.
extern void bzero(void *s, int n);将s为地址的n个字符置0;
一个小栗子
#include <string.h> main() { struct { int a; char s[5]; float f; } tt; char s[20]; bzero(&tt,sizeof(tt)); // struct initialization to zero bzero(s,20); return 0; }
-----------------------------------------------------------------------
第三个函数if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
int bind(int sock_fd,struct sockaddr_in *my_addr, int addrlen);
功能说明:
参数说明:
my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;
struct sockaddr_in结构类型是用来保存socket信息的:
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct sockaddr_in s_add,c_add;是sockaddr这个结构体,但是怎么会强制转换成struct sockaddr这个结构体
来分别看看这两个结构体
struct sockaddr //16bytes{unsigned short sa_family; //2byteschar sa_data[14]; //14bytes}struct sockaddr_in //16bytes{unsigned short sin_family; //2bytesunsigned short sin_port; //2bytesstruct in_addr sin_addr; //4bytesunsigned char sin_zero[8]; //8bytes}struct in_addr{unsigned int s_addr;}
可以看出sockaddr和sockaddr_in的字节数是一致的,在tcpip中我们所写ip端口呀都是合并起来用的,而为了输入方便,在_in结构体中分开,使用时再强制转换。
s_add.sin_family=AF_INET;s_add.sin_port=htons(portnum);s_add.sin_addr.s_addr=htonl(INADDR_ANY);上面为初始化这个结构体,看看htons这个函数
我们知道网络字节顺序是大端的,而主机字节顺序是小端的,所以有一组函数用于两者的转化
unsigned long int htonl(unsigned long int hostlong)unsigned short int htons(unisgned short int hostshort)unsigned long int ntohl(unsigned long int netlong)unsigned short int ntohs(unsigned short int netshort)在这四个转换函数中,h代表 host , n 代表 network . s 代表 short , l 代表 long 第一个函
数的意义是将本机器上的 long 数据转化为网络上的 long. 其他几个函数的意义也差不多.
表示可以和任何的主机通信
----------------------------------------------------
第四个函数if(-1 == listen(sfp,5))
sockfd:是bind 后的文件描述符.
backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时,使用这个表示
可以介绍的排队长度. listen函数将 bind 的文件描述符变为监听套接字.返回的情况和bind 一
样.
默认情况下,内核会认为socket函数的创建的描述符对应于主动套接字,调用listen函数告诉内核,描述符是被服务器而不是客户端使用的
------------------------------------------------
到这服务器的初始工作就完成了,等到客户端连接
先来看看client.c
--------------------------------------------------------
第一个函数
cfd = socket(AF_INET, SOCK_STREAM, 0);
一样去理解;
---------------------------------------------------------
第二个函数
bzero(&s_add,sizeof(struct sockaddr_in));
一样去理解
--------------------------------------------------------
s_add.sin_family=AF_INET;s_add.sin_addr.s_addr= inet_addr("192.168.3.124");s_add.sin_port=htons(portnum);这里有出现了inet_addr
IP地址转换
有三个函数将数字点形式表示的字符串IP地址与32位网络字节顺序的二进制形式的IP地址进行转换
(1) unsigned long int inet_addr(const char * cp):该函数把一个用数字和点表示的IP地址的字符串转换成一个无符号长整型,如:struct sockaddr_in ina
ina.sin_addr.s_addr=inet_addr("202.206.17.101")
该函数成功时:返回转换结果;失败时返回常量INADDR_NONE,该常量=-1,二进制的无符号整数-1相当于255.255.255.255,这是一个广播地址,所以在程序中调用iner_addr()时,一定要人为地对调用失败进行处理。由于该函数不能处理广播地址,所以在程序中应该使用函数inet_aton()。
(2)int inet_aton(const char * cp,struct in_addr * inp):此函数将字符串形式的IP地址转换成二进制形式的IP地址;成功时返回1,否则返回0,转换后的IP地址存储在参数inp中。
(3) char * inet_ntoa(struct in_addr in):将32位二进制形式的IP地址转换为数字点形式的IP地址,结果在函数返回值中返回,返回的是一个指向字符串的指针。
-----------------------------------------------------------------------
第三个函数
if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
sockfd:socket 返回的文件描述符.
serv_addr:储存了服务器端的连接信息.其中sin_add 是服务端的地址
addrlen:serv_addr 的长度
connect 函数是客户端用来同服务端连接的.成功时返回0,sockfd 是同服务端通讯的文件
描述符 失败时返回-1.
进行客户端程序设计无须调用 bind(),因为这种情况下只需知道目
的机器 的 IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,socket执行体
为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候到 打断口。
Connect 函数启动和远端主机的直接连接。 只有面向连接的客户程序使用 socket时才
需要将此 socket与远端主机相连。无连接协议从不建立直接连接。面向连接的服务器也从
不启动一个连接,它只是被动的在协议端口监听客户的请求
连接完成后 就可以通行了
-------------------------------------------------------------------------------
回到server
第五个函数
nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
accept()函数让服务器接收客户的连接请求。在建立好输入队列后,服务器就调用
accept 函数,然后睡眠并等待客户的连接请求。
int accept(int sockfd, void *addr, int *addrlen);
sockfd 是被监听的 socket 描述符,addr 通常是一个指向 sockaddr_in 变量的指针,
该变量用来存放提出连接请求服务的主机的信息(某 台主机从某个端口发出该请求);
addrten 通常为一个指向值为 sizeof(struct sockaddr_in)的整型指针变量。
write函数
syntax:
功能说明:
------------------------------------------
server写后,client需要去读
client中
if(-1 == (recbytes = read(cfd,buffer,1024)))
ssize_t read(int fd,void *buf,size_t nbyte)
函数说明:
通过connect fd已经和服务器取得联系
-----------------------------
函数
close(cfd);没什么问题
------------------------------------------------------------------------------------------------------------
上面是服务器发,客户端收,下面再看看两者相互通信的情况
server_2.c
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<errno.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#define MAXLINE 4096int main(int argc, char** argv){ int listenfd, connfd; char recvline[4096], sendline[4096]; struct sockaddr_in servaddr; char buff[4096]; int n; if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){ printf("create socket error: %s(errno: %d)/n",strerror(errno),errno); exit(0); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(6660); if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){ printf("bind socket error: %s(errno: %d)/n",strerror(errno),errno); exit(0); } if( listen(listenfd, 10) == -1){ printf("listen socket error: %s(errno: %d)/n",strerror(errno),errno); exit(0); } printf("======waiting for client's request======\n"); if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){ printf("accept socket error: %s(errno: %d)",strerror(errno),errno); exit(0); }while(1){ n = recv(connfd, buff, MAXLINE, 0); buff[n] = '\0'; printf("recv msg from client: %s\n", buff); printf("send msg to client: \n"); fgets(sendline, 4096, stdin); if( send(connfd, sendline, strlen(sendline), 0) < 0) { printf("send msg error: %s(errno: %d)\n", strerror(errno), errno); exit(0); }} close(listenfd);}
client_2.c
#include<stdio.h>#include<stdlib.h>#include<string.h>#include<errno.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#define MAXLINE 4096int main(int argc, char** argv){ int sockfd; char recvline[4096], sendline[4096]; struct sockaddr_in servaddr; char buff[4096]; int n; if( argc != 2){ printf("usage: ./client <ipaddress>/n"); exit(0); } if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ printf("create socket error: %s(errno: %d)/n", strerror(errno),errno); exit(0); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(6660); if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){ printf("inet_pton error for %s/n",argv[1]); exit(0); } if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){ printf("connect error: %s(errno: %d)/n",strerror(errno),errno); exit(0); }while(1){ printf("send msg to server: \n"); fgets(sendline, 4096, stdin); if( send(sockfd, sendline, strlen(sendline), 0) < 0) { printf("send msg error: %s(errno: %d)\n", strerror(errno), errno); exit(0); } n = recv(sockfd, buff, MAXLINE, 0); buff[n] = '\0'; printf("recv msg from server: %s\n", buff);} close(sockfd); exit(0);}
下面的使用recv和send两个函数发送的
- 从“hello”开始去分析linux下的socket编程
- Linux内核编程:从hello world 开始-(3)_run
- 带你从零学习linux下的socket编程
- Linux下的Socket编程
- linux下的socket编程
- Linux下的Socket编程
- linux下的socket编程
- Linux下的socket编程
- linux下的socket编程
- linux下的socket编程
- LINUX下的SOCKET编程
- Linux下的Socket编程
- Linux下的socket编程
- Linux下的Socket编程
- Linux下的socket编程
- Linux内核编程:从hello world 开始-(1)_C文件编写
- Linux内核编程:从hello world 开始-(2)_Makefile文件编写
- 从socket开始讲IOS网络编程
- POI操作Excel
- 第三节 Lookup 缓存 之 使用不高速缓存的查找或静态高速缓存
- Objective-C - 获取毫秒时间戳
- [BZOJ1010]HNOI2008玩具装箱|斜率优化DP
- 设计模式:2)策略设计模式
- 从“hello”开始去分析linux下的socket编程
- 深圳医院3D打印钛合金假体置换患者肩胛骨
- sqlite如何避免重复建表(获取已经存在的表)
- js中变量和jsp中java代码中变量互相访问解决方案
- 深入理解Python 装饰器(decorator)
- 排序
- 从小到大排序2
- zookeeper原理
- Mac OS X上使用Wireshark抓包