Linux C Socket入门
(ZZ:http://hi.baidu.com/atlas1005/item/4dcbebf53af082de6325d2a2)
socket的英文原义是“孔”或“插座”。作为4BDS UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。
具体解释详见http://baike.baidu.com/view/13870.htm
作为socket入门,首先建立最基本的局域网内两台计算机的连接和发送信息。
第一步:建立一个socket
socket编程的第一件事就是用socket()建立一个socket:
int socket(int af, int type, int protocol)
'int af'代表地址族或者称为socket所代表的域,通常有两个选项:
AF_UNIX - 只在单机上使用。
AF_INET - 可以在单机或其他使用DARPA协议(UDP/TCP/IP)的异种机通信。
'int type'代表你所使用的连接类型,通常也有两种情况:
SOCK_STREAM - 用来建立面向连接的sockets,可以进行可靠无误的的数据传输
SOCK_DGRAM - 用来建立没有连接的sockets,不能保证数据传输的可靠性。
在本文中,我们使用AF_INET地址族和SOCK_STREAM连接类型。
'int protocol'通常设定为0。这样的目的是使系统选择默认的由协议族和连接类型所确定的协议。
这个函数的返回值是一个文件描述句柄,如果在此期间发生错误则返回-1并且设定了相应的errno。
#include <sys/types.h>
#include <sys/socket.h>
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket 创建出错!");
exit(1);
}
第二步:名字绑定socket: bind()
int bind(int sockfd, struct sockaddr *name, int namelen)
在这个函数里,sockfd是从socket()调用得到的文件描述句柄。name是一个指向sockaddr类型结构的一个指针。namelen给出了文件名的具体长度。
如果地址族被设定为AF_UNIX,这个类型的定义如下所示:
struct sockaddr {
u_short sa_family;
char sa_data[14];
};
在这个结构种,name.sa_family应当被设定为AF_UNIX。name.sa_data应当包含最长为14个字节的文件名,这个文件名用来分配给socket。
#include <sys/types.h>
#include <sys.socket.h>
struct sockaddr name;
int sockfd;
name.sa_family = AF_UNIX;
strcpy(name.sa_data, "/tmp/whatever");
sockfd = socket(AF_UNIX, SOCK_STREAM, 0)
/* error checking code here */
bind(sockfd, &name, strlen(name.sa_data) + sizeof(name.sa_family)
/* error checking code here */
如果调用成功,则返回值为0,如果调用失败返回值为-1,并设定相应的错误代码errno。
在使用AF_INET地址族的时候,我们使用另外一种结构:
struct sockaddr_in {
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
在这个结构种,name.sin_family应当被设定为AF_INET
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
int sockfd, port = 23;
struct sockaddr_in my_addr;
if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("Socket Error, %d\n", errno);
exit(1);
}
my_addr.sin_family = AF_INET; /* host byte order */
my_addr.sin_port = htons(port); /* see man htons for more information */
my_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* get our address */
bzero(&(my_addr.sin_zero), 8); /* zero out the rest of the space */
if((bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
{
printf("Bind Error, %d\n", errno);
close(sockfd);
exit(1);
}
现在,如果没有问题的话,我们建立的socket就有一个名字了!相反,如果不成功,它会设定相应的错误代码,并使程序退出。这里需要说明的是,如果你的计算机不想和别人的计算机连接,那么完全没有必要使用bind()。对于端口的绑定,在服务器而言是不合适的,它只应该在客户机上实现。
第三步:远程连接: connect()
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
sockfd是我们建立的文件描述句柄,serv_addr是一个sockaddr结构,包含目的的地址和端口号,addrlen 被设定为sockaddr结构的大小。
第四步:监听: listen()
int listen(int sockfd, int backlog);
参数backlog是指一次可以监听多少个连接
它的调用返回结果和上述的几个函数是一样的,这里就不多说了。值得一提的是,在这里,我们需要建立一个绑定,用来接收特定端口的服务请求。
第五步:接受连接: accept()
当有人试图从我们打开的端口登陆进来时,我们应该响应他,这个时候就要用到accept()函数了。
int accept(int sockfd, void *addr, int *addrlen);
第六步:输入和输入的完成: send() and recv()
int send(int sockfd, const void *msg, int len, int flags);
int recv(int sockfd, void *buf, int len, unsigned int flags);
- ------
send():
sockfd - socket file descriptor
msg - message to send
len - size of message to send
flags - read 'man send' for more info, set it to 0 for now
recv():
sockfd - socket file descriptor
buf - data to receive
len - size of buf
flags - same as flags in send()
注意:如果使用的连接类型是SOCK_DGRAM,那么应该使用sendto()和recvfrom()来实现数据传输。
最后一步: close() and shutdown()
当传输结束时,应当关闭连接。
#include <stdio.h>
/* all you code */
close(sockfd);
更保险的方法是用shutdown()来关闭连接。
nt shutdown(int sockfd, int how)
参数how的选择:
0 - 不允许接收更多的数据
1 - 不允许发送更多的数据
2 - 不允许接收和发送更多的数据(和close()一样)
以上就完成了socket的基本通信。
一些额外的语句:
你是谁: getpeerbyname()
可能你还想知道是谁正在和你连接,那么看下面:
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
参数addr是一个指向'struct sockaddr'或者'struct sockaddr_in'的指针。
如果执行成功,我们就得到了对方的地址了,然后用inet_ntoa()用gethostbyad
dr()来得到对方更多的信息。
我是谁: gethostname()
#include <unistd.h>
int gethostname(char *hostname, size_t size);
hostname是一个存放主机名字的字符数组
返回的hostname可以作为gethostbyname()的参数,这样又可以得到自己的IP地址了。
最后附上一个最简单的实例:
服务器端:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netdb.h>
#include <arpa/inet.h>//for inet_ntoa
#include <unistd.h>//for fork()
#define SERVPORT 3333 //服务器监听端口号
#define BACKLOG 10 //最大同时连接请求数
int main()
{
int sockfd, client_fd;//sockfd:监听socket;client_fd:数据传输socket
struct sockaddr_in my_addr;//本机地址信息
struct sockaddr_in remote_addr;//client address
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket 创建出错!");
exit(1);
}
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(SERVPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero), 8);
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
{
perror("bind error");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen error");
exit(1);
}
while(1)
{
socklen_t sin_size = sizeof(struct sockaddr_in);//add socklen_t by zzz
if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size))== -1)
{
perror("accept error");
continue;
}
printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));
if (!fork())//子进程代码段
{
if (send (client_fd, "Hello, you are connected!\n", 26, 0) == -1)
perror("send error");
shutdown(client_fd, 2);
exit(0);
}
shutdown(sockfd,2);
}
return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define SERVPORT 3333
#define MAXDATASIZE 100 //maxim data transfer
int main(int argc, char *argv[])
{
int sockfd, recvbytes;
char buf[MAXDATASIZE];
struct hostent *host;
struct sockaddr_in serv_addr;
if(argc < 2)
{
fprintf(stderr, "Please enter the server's hostname!\n");
exit(1);
}
if ((host = gethostbyname(argv[1])) == NULL)
{
herror("gethostbyname error!");
exit(1);
}
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket establishment error!");
exit(1);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
if (connect (sockfd, (struct sockaddr *)&serv_addr, sizeof (struct sockaddr)) == -1)
{
perror("connect error!");
exit(1);
}
if ((recvbytes = recv (sockfd, buf, MAXDATASIZE, 0)) == -1)
{
perror("recv error!");
exit(1);
}
buf[recvbytes] = '\0';
printf("Received: %s", buf);
shutdown(sockfd,2);
return 0;
}

浙公网安备 33010602011771号