socket(建立一个socket通信)

相关函数

accept,bind,connect,listen

表头文件

#include<sys/types.h>

#include<sys/socket.h>

定义函数

int socket(int domain,int type,int protocol);

函数说明

socket()用来建立一个新的socket,也就是向系统注册,通知系统建立一通信端口。参数domain 指定使用何种的地址类型,完整的定义在/usr/include/bits/socket.h 内,底下是常见的协议:

PF_UNIX/PF_LOCAL/AF_UNIX/AF_LOCAL UNIX 进程通信协议

PF_INET?AF_INET Ipv4网络协议

PF_INET6/AF_INET6 Ipv6 网络协议

PF_IPX/AF_IPX IPX-Novell协议

PF_NETLINK/AF_NETLINK 核心用户接口装置

PF_X25/AF_X25 ITU-T X.25/ISO-8208 协议

PF_AX25/AF_AX25 业余无线AX.25协议

PF_ATMPVC/AF_ATMPVC 存取原始ATM PVCs

PF_APPLETALK/AF_APPLETALK appletalk(DDP)协议

PF_PACKET/AF_PACKET 初级封包接口

参数

type有下列几种数值:

SOCK_STREAM 提供双向连续且可信赖的数据流,即TCP。支持

OOB 机制,在所有数据传送前必须使用connect()来建立连线状态。

SOCK_DGRAM 使用不连续不可信赖的数据包连接

SOCK_SEQPACKET 提供连续可信赖的数据包连接

SOCK_RAW 提供原始网络协议存取

SOCK_RDM 提供可信赖的数据包连接

SOCK_PACKET 提供和网络驱动程序直接通信。

protocol用来指定socket所使用的传输协议编号,通常此参考不用管它,设为0即可。

返回值

成功则返回socket处理代码,失败返回-1。

错误代码

EPROTONOSUPPORT 参数domain指定的类型不支持参数type或protocol指定的协议

ENFILE 核心内存不足,无法建立新的socket结构

EMFILE 进程文件表溢出,无法再建立新的socket

EACCESS 权限不足,无法建立type或protocol指定的协议

ENOBUFS/ENOMEM 内存不足

EINVAL 参数domain/type/protocol不合法

 

 

 

bind(对socket定位) 

相关函数

socket,accept,connect,listen

表头文件

#include<sys/types.h>

#include<sys/socket.h>

定义函数

int bind(int sockfd,struct sockaddr * my_addr,int addrlen);

函数说明

bind()用来设置给参数sockfd的socket一个名称。此名称由参数my_addr指向一sockaddr结构,对于不同的socket domain定义了一个通用的数据结构

struct sockaddr

{

unsigned short int sa_family;

char sa_data[14];

};

sa_family 为调用socket()时的domain参数,即AF_xxxx值。

sa_data 最多使用14个字符长度。

此sockaddr结构会因使用不同的socket domain而有不同结构定义,例如使用AF_INET domain,其socketaddr结构定义便为

struct socketaddr_in

{

unsigned short int sin_family;

uint16_t sin_port;

struct in_addr sin_addr;

unsigned char sin_zero[8];

};

struct in_addr

{

uint32_t s_addr;

};

sin_family 即为sa_family

sin_port 为使用的port编号

sin_addr.s_addr 为IP 地址

sin_zero 未使用。

参数

addrlen为sockaddr的结构长度。

返回值

成功则返回0,失败返回-1,错误原因存于errno中。

错误代码

EBADF 参数sockfd 非合法socket处理代码。

EACCESS 权限不足

ENOTSOCK 参数sockfd为一文件描述词,非socket。

 

 

 

 

 

listen(等待连接)

 

相关函数

socket,bind,accept,connect

表头文件

#include<sys/socket.h>

定义函数

int listen(int s,int backlog);

函数说明

listen()用来等待参数s 的socket连线。参数backlog指定同时能处理的最大连接要求,如果连接数目达此上限则client端将收到ECONNREFUSED的错误。Listen()并未开始接收连线,只是设置socket为listen模式,真正接收client端连线的是accept()。通常listen()会在socket(),bind()之后调用,接着才调用accept()。

返回值

成功则返回0,失败返回-1,错误原因存于errno

附加说明

listen()只适用SOCK_STREAM或SOCK_SEQPACKET的socket类型。如果socket为AF_INET则参数backlog 最大值可设至128。

错误代码

EBADF 参数sockfd非合法socket处理代码

EACCESS 权限不足

EOPNOTSUPP 指定的socket并未支援listen模式。

 

 

 

 

accept(接受socket连线)

 

 

相关函数

socket,bind,listen,connect

表头文件

#include<sys/types.h>

#include<sys/socket.h>

定义函数

int accept(int s,struct sockaddr * addr,int * addrlen);

函数说明

accept()用来接受参数s的socket连线。参数s的socket必需先经bind()、listen()函数处理过,当有连线进来时accept()会返回一个新的socket处理代码,往后的数据传送与读取就是经由新的socket处理,而原来参数s的socket能继续使用accept()来接受新的连线要求。连线成功时,参数addr所指的结构会被系统填入远程主机的地址数据,参数addrlen为scokaddr的结构长度。关于结构sockaddr的定义请参考bind()。

返回值

成功则返回新的socket处理代码,失败返回-1,错误原因存于errno中。

错误代码

EBADF 参数s 非合法socket处理代码。

EFAULT 参数addr指针指向无法存取的内存空间。

ENOTSOCK 参数s为一文件描述词,非socket。

EOPNOTSUPP 指定的socket并非SOCK_STREAM。

EPERM 防火墙拒绝此连线。

ENOBUFS 系统的缓冲内存不足。

ENOMEM 核心内存不足。

 

 

 

 

connect(建立socket连线)

 

相关函数

socket,bind,listen

表头文件

#include<sys/types.h>

#include<sys/socket.h>

定义函数

int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);

函数说明

connect()用来将参数sockfd 的socket 连至参数serv_addr 指定的网络地址。结构sockaddr请参考bind()。参数addrlen为sockaddr的结构长度。

返回值

成功则返回0,失败返回-1,错误原因存于errno中。

错误代码

EBADF 参数sockfd 非合法socket处理代码

EFAULT 参数serv_addr指针指向无法存取的内存空间

ENOTSOCK 参数sockfd为一文件描述词,非socket。

EISCONN 参数sockfd的socket已是连线状态

ECONNREFUSED 连线要求被server端拒绝。

ETIMEDOUT 企图连线的操作超过限定时间仍未有响应。

ENETUNREACH 无法传送数据包至指定的主机。

EAFNOSUPPORT sockaddr结构的sa_family不正确。

EALREADY socket为不可阻断且先前的连线操作还未完成。

 

 

 

 

htons(将16位主机字符顺序转换成网络字符顺序)

 

相关函数

htonl,ntohl,ntohs

表头文件

#include<netinet/in.h>

定义函数

unsigned short int htons(unsigned short int hostshort);

函数说明

htons()用来将参数指定的16位hostshort转换成网络字符顺序。

返回值

返回对应的网络字符顺序。

 

 

 

 

 

 

htonl(将32位主机字符顺序转换成网络字符顺序)

 

相关函数

htons,ntohl,ntohs

表头文件

#include<netinet/in.h>

定义函数

unsigned long int htonl(unsigned long int hostlong);

函数说明

htonl()用来将参数指定的32位hostlong 转换成网络字符顺序。

返回值

返回对应的网络字符顺序。

 

 

 

 

select(I/O多工机制)

 

表头文件

#include<sys/time.h>

#include<sys/types.h>

#include<unistd.h>

定义函数

int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);

函数说明

select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数readfds、writefds 和exceptfds 称为描述词组,是用来回传该描述词的读,写或例外的状况。底下的宏提供了处理这三种描述词组的方式:

FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位

FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真

FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位

FD_ZERO(fd_set *set); 用来清除描述词组set的全部位

参数

timeout为结构timeval,用来设置select()的等待时间,其结构定义如下

struct timeval

{

time_t tv_sec;

time_t tv_usec;

};

返回值

如果参数timeout设为NULL则表示select()没有timeout。

错误代码

执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。

EBADF 文件描述词为无效的或该文件已关闭

EINTR 此调用被信号所中断

EINVAL 参数n 为负值。

ENOMEM 核心内存不足

范例

常见的程序片段:fs_set readset;

FD_ZERO(&readset);

FD_SET(fd,&readset);

select(fd+1,&readset,NULL,NULL,NULL);

if(FD_ISSET(fd,readset){……}

实例代码:

server端

 


C代码  


1. <span style="font-size: medium;">#include <sys/types.h>  
2. #include <sys/socket.h>  
3. #include <netinet/in.h>  
4. #include <arpa/inet.h>  
5. #include <unistd.h>  
6. #include <stdlib.h>  
7. #include <string.h>  
8. #include <stdio.h>  
9. #define PORT 1235  
10. #define MAXSOCKFD 10  
11.   
12. int Bind(int fd,const struct sockaddr *sa,socklen_t salen){  
13. return(bind(fd,sa,salen));  
14. }  
15.   
16. main()  
17. {  
18. int sockfd,newsockfd,is_connected[MAXSOCKFD],fd;  
19. struct sockaddr_in addr;  
20. // struct sockaddr *addrt;  
21. int addr_len = sizeof(struct sockaddr_in);  
22. fd_set readfds;  
23. char buffer[256];  
24. char msg[] ="Welcome to server!";  
25. if ((sockfd = socket(AF_INET,SOCK_STREAM,0))<0){  
26.   
27. "socket");  
28.     exit(1);  
29. }  
30. printf("%d\n", sockfd);  
31.   
32. bzero(&addr,sizeof(addr));  
33. // memset(&addr,0,sizeof(addr));  
34. addr.sin_family = AF_INET;  
35. addr.sin_port = htons(PORT);  
36. addr.sin_addr.s_addr = htonl(INADDR_ANY);  
37. // addrt = &addr;  
38. if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr))<0){  
39. "connect");  
40.     exit(1);  
41. }  
42. printf("%d\n", sockfd);  
43. if(listen(sockfd,3)<0){  
44. "listen");  
45.     exit(1);  
46. }  
47. printf("%d\n", sockfd);  
48. for(fd=0;fd<MAXSOCKFD;fd++)  
49.     is_connected[fd]=0;  
50. while(1){  
51.     FD_ZERO(&readfds);  
52.     FD_SET(sockfd,&readfds);  
53. "%d\n",sockfd );  
54. for(fd=0;fd<MAXSOCKFD;fd++)  
55. if(is_connected[fd]) FD_SET(fd,&readfds);  
56. if(!select(MAXSOCKFD,&readfds,NULL,NULL,NULL))continue;  
57. for(fd=0;fd<MAXSOCKFD;fd++)  
58. if(FD_ISSET(fd,&readfds)){  
59. if(sockfd ==fd){  
60. if((newsockfd = accept (sockfd,(struct sockaddr *)&addr,&addr_len))<0)  
61. "accept");  
62. sizeof(msg));  
63.                 is_connected[newsockfd] =1;  
64. "cnnect from %s\n",inet_ntoa(addr.sin_addr));  
65. else{  
66. sizeof(buffer));  
67. if(read(fd,buffer,sizeof(buffer))<=0){  
68. "connect closed.\n");  
69.                     is_connected[fd]=0;  
70.                     close(fd);  
71. else  
72. "%s",buffer);  
73.             }  
74.         }  
75. }  
76. }  
77. </span>


 

 client端

 


C代码  

1. <span style="font-size: medium;">#include <sys/stat.h>  
2. #include <fcntl.h>  
3. #include <unistd.h>  
4. #include <sys/types.h>  
5. #include <sys/socket.h>  
6. #include <netinet/in.h>  
7. #include <arpa/inet.h>  
8. #include <stdlib.h>  
9. #include <stdio.h>  
10. #include <string.h>  
11. #define PORT 1235  
12. #define SERVER_IP "127.0.0.1"  
13. main()  
14. {  
15. int s;  
16. struct sockaddr_in addr;  
17. char buffer[256];  
18. if((s = socket(AF_INET,SOCK_STREAM,0))<0){  
19. "socket");  
20.         exit(1);  
21.     }  
22. /* 填写sockaddr_in结构*/  
23. sizeof(addr));  
24.     addr.sin_family = AF_INET;  
25.     addr.sin_port=htons(PORT);  
26.     addr.sin_addr.s_addr = inet_addr(SERVER_IP);  
27. /* 尝试连线*/  
28. if(connect(s,(struct sockaddr *)&addr,sizeof(addr))<0){  
29. "connect");  
30.         exit(1);  
31.     }  
32. /* 接收由server端传来的信息*/  
33. sizeof(buffer),0);  
34. "%s\n",buffer);  
35. while(1){  
36. sizeof(buffer));  
37. /* 从标准输入设备取得字符串*/  
38. sizeof(buffer));  
39. /* 将字符串传给server端*/  
40. if(send(s,buffer,sizeof(buffer),0)<0){  
41. "send");  
42.             exit(1);  
43.         }  
44.     }  
45. }  
46. </span>
  1.  


 启动server

 

[root@localhost 桌面]# netstat -antup | grep 1236
tcp        0      0 0.0.0.0:1236                0.0.0.0:*                   LISTEN      15274/./tcpserver

处理LISTEN状态中

再开启client

 

[root@localhost 桌面]# netstat -antup | grep 1236
tcp        0      0 0.0.0.0:1236                0.0.0.0:*                   LISTEN      15274/./tcpserver   
tcp        0      0 127.0.0.1:54628             127.0.0.1:1236              ESTABLISHED 15314/./tcpclient   
tcp        0      0 127.0.0.1:1236              127.0.0.1:54628             ESTABLISHED 15274/./tcpserver

多了两个进程,状态为 ESTABLISHED 

 

将client^D

[root@localhost 桌面]# netstat -antup | grep 1236
tcp        0      0 0.0.0.0:1236                0.0.0.0:*                   LISTEN      15274/./tcpserver   
tcp        0      0 127.0.0.1:54628             127.0.0.1:1236              ESTABLISHED 15314/./tcpclient   
tcp        0      0 127.0.0.1:1236              127.0.0.1:54628             ESTABLISHED 15274/./tcpserver   
tcp        0      0 127.0.0.1:54629             127.0.0.1:1236              TIME_WAIT   -

状态变为TIME_WAIT