Creating Your Own Server: The Socket API, Part 2
转:http://www.linuxforu.com/2011/09/creating-your-own-server-the-socket-api-part-2/
By Pankaj Tanwar on September 1, 2011 in Coding, Developers · 0 Comments
As mentioned, let’s dive straight into the code.
The IPv6 version of the server
Here’s the IPv6 version of the server we created in the previous article. There are no big changes, except the appearance of “6″ in the code. Let’s name it serverin6.c
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> int main() { int sfd, cfd; char ch; socklen_t len; struct sockaddr_in6 saddr, caddr; sfd= socket(AF_INET6, SOCK_STREAM, 0); saddr.sin6_family=AF_INET6; saddr.sin6_addr=in6addr_any; saddr.sin6_port=htons(1205); bind(sfd, ( struct sockaddr *)&saddr, sizeof (saddr)); listen(sfd, 5); while (1) { printf ( "Waiting...n" ); len= sizeof (cfd); cfd=accept(sfd, ( struct sockaddr *)&caddr, &len); if (read(cfd, &ch, 1)<0) perror ( "read" ); ch++; if (write(cfd, &ch, 1)<0) perror ( "write" ); close(cfd); } } |
Now let’s review the differences. The first is at Line 6 (sockaddr_in6
) and is easily understood; for IPv6 addresses, we need to store the address, port and address family in this address structure, as we have done in lines 13, 14 and 15. At Line 14, in6addr_any
is a wildcard for all IPv6 addresses, like INADDR_ANY
in IPv4. There’s also a change in accept()
, which can be easily understood. And here’s our server working with IPv6. You can set up your IPv6 using the following command as root:
ip -f inet6 addr add face:1f::ea54:a dev eth0 |
Here, -f
specifies the family (inet6
), and addr
is for the address — we added face:1f::ea54:a
(while writing IPv6 addresses, leading zeros can be dropped — the above address is actuallyface:001f::ea54:000a
). You can give any address; and to randomise it, you can use your MAC address. The dev
parameter specifies the device we are setting the address for — in this case,eth0
. You can check the results using the ifconfig
command.
Compile and run the server as follows:
cc serverin6.c -o serverin6 . /serverin6 |
Before writing the client, you can see that it even works with our IPv4 client, as the IPv4 address is also pointing to the same machine. For every server, we can use telnet to test if it’s working as expected — for example:
telnet localhost 1205 |
Type in the character you want to send to the server and press Enter, and the server will reply with the next ASCII character, and close the connection. Remember, for IPv4, localhost is 127.0.0.1, and for IPv6, it is ::1.
Client (IPv6 version)
Now let’s write the IPv6 version client, and name it clientin6.c
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> int main( int argc, char **argv) { int cfd; struct sockaddr_in6 addr; char ch; if (argc!=3) { printf ( "Usage: %s in6addr charactern" , argv[0]); return -1; } if ( ! inet_pton(AF_INET6, argv[1], &(addr.sin6_addr))) { /* returns 0 on error */ printf ( "Invalid Addressn" ); return -1; } ch=argv[2][0]; /* Set the character to second argument */ cfd=socket(AF_INET6, SOCK_STREAM, 0); addr.sin6_family=AF_INET6; addr.sin6_port=htons(1205); if (connect(cfd, ( struct sockaddr *)&addr, sizeof (addr))<0) { perror ( "connect error" ); return -1; } if (write(cfd, &ch, 1)<0) perror ( "write" ); if (read(cfd, &ch, 1)<0) perror ( "read" ); printf ( "Server sent: %cn" , ch); close(cfd); return 0; } |
Here, again, the changes are small and self-explanatory — just an added “6″. The client takes the IP address and characters as arguments, so to run it, just type the following:
cc clientin6.c -o clientin6 . /clientin6 ::1 d |
When d
is sent, the server replies with e
. The output will be the same as for the last examples, shown in the figures of the last article. To process the address, we have used the inet_pton()
function, which we’ll look at in more detail in the next section.
Diving deeper
First, let’s look at the function we used in our client to convert the address from string to numeric. In binary, the IP address 192.168.1.23
will be 11000000 10101000 00000001 00010111
(a 32-bit string of 0′s and 1′s). It will be a 128-bit string for IPv6 (64-bit address). The human-readable addresses 192.168.1.23
or face:1f::ea54:a
are the ‘presentation’ form (the p
of the function), and n
is for the numeric (binary) form. The function is prototyped in <arpa/inet.h>
as:
int inet_pton ( int family, const char *strptr, void *addrptr); |
This function can convert both IPv4 and IPv6 addresses from string form to binary as needed by the machine. The first argument is, of course, the family: AF_INET
or AF_INET6
. The *strptr
argument is the address in string form, and *addrptr
will be the destination where the numeric address will be stored; this must be a structure (sin_addr
for IPv4, and sin6_addr
for IPv6). The function returns 1 on success and 0 on error. The next function is:
const char *inet_ntop( int family, const void *addrptr, char *strptr, size_t len); |
This function does exactly the reverse of the previous case; it converts the numeric (addrptr
) and stores in the presentation (strptr
). The last argument is len
, the size/length of the destination variable, to avoid overflow. For ease, netinet/in.h
defines the size constants, as follows:
#define INET_ADDRSTRLEN 16 #define INET6_ADDRSTRLEN 46 |
There are other functions available for the same purpose — inet_aton()
and inet_ntoa()
— but only for IPv4; therefore, we aren’t using them.
Now let’s look at the functions to convert bytes between the network protocol stack and host to get machine-independent code. Machine architecture is either big-endian or little-endian, i.e., how the machine stores data. The high-order byte at a higher address and low-order byte at a lower address is little-endian. Conversely, high-order byte at a lower address, and low-order byte at a higher address will be big-endian.
For example, if we have a 2-byte integer storing 0x1234
at address 0×0200, it will occupy two bytes: 0x0200
and 0x0201
. If, at 0x0200
we have 0x12
, and at 0x0201
we have 0x32
, the machine is little-endian. If the values are stored the opposite way, the machine is big-endian. The book Unix Network Programming, by W Richard Stevens, gives a program to find out what machine you have. Let’s look at the functions now, defined in netinet/in.h
:
uint16_t htons(uint16_t host16bitvalue); uint32_t htonl(uint32_t host32bitvalue); |
These two functions return 16-bit and 32-bit values respectively; htons()
stands for the host to network short, htonl()
for long. They convert from host byte-ordering to network-stack byte-ordering. The functions ntohs()
and ntohl()
do the reverse (from network byte-ordering to host byte-ordering).
These functions are particularly useful when we ask the system to provide us the information of the host running a client or server. Now let’s try to extract some information from the client. Just add these lines to the server:
char address[INET6_ADDRSTRLEN]; /* at declaration section */ printf ( "Connected to client %s at port %dn" , inet_ntop(AF_INET6, &caddr.sin6_addr, buff, sizeof (buff)), ntohs(caddr.sin6_port)); /* after the call to accept() */ |
You can see the output after these code changes in Figure 1. The code itself is clear—we have used the functions we just understood, above.
Figure 1: Server running
A little exercise
Now put this code in the proper section of the server program, to make it do something more interesting than what we did earlier:
if (read(cfd, &ch, 1)<0) perror ( "read" ); while ( ch != EOF) { if ((ch>= 'a' && ch<= 'z' ) || (ch>= 'A' && ch<= 'Z' )) ch^=0x20; /* EXORing 6th bit will result in change in case */ if (write(cfd, &ch, 1)<0) perror ( "write" ); if (read(cfd, &ch, 1)<0) perror ( "read" ); } |
Yes, you’ve got it right: add it after the call to accept()
. Now, if you’re as sleepy as I am right now, then just telnet to the server — or go on and write a client; great, that’s the spirit.
Well, before closing, let’s look at the output of a telnet session. Run telnet ::1 1205
in your shell, and start typing. See Figure 2 for a sample output.
Figure 2: Telnetting
On hitting Ctrl+D, the server will close the connection, and wait for a new one. And for those lazy fellas who didn’t want to write their own client’s :P, you just needed to put this into your client, instead of only write() and read() calls, for the result shown in Figure 3:
while (1) { ch= getchar (); if (write(cfd, &ch, 1)<0) perror ( "write" ); if (read(cfd, &ch, 1)<0) perror ( "read" ); printf ( "%c" , ch); } |
Figure 3: Client running
Compile and run your client.
To close, use Ctrl+D to send EOF to the server, and the server will close the connection to the client, instead of shutting the server down using Ctrl-C. Or you can also close the connection from the client; we’ll handle this problem later, using signal handlers. Now, I think I should try to get some rest. Good night, and FOSS ROCKS!
Creating Your Own Server: The Socket API, Part 2的更多相关文章
- Creating Your Own Server: The Socket API, Part 1
转:http://www.linuxforu.com/2011/08/creating-your-own-server-the-socket-api-part-1/ By Pankaj Tanwar ...
- The Socket API, Part 5: SCTP
转:http://www.linuxforu.com/2011/12/socket-api-part-5-sctp/ By Pankaj Tanwar on December 29, 2011 in ...
- Mac mySql ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)的解决办法
我的环境:Mac 10.11.6 ,mysql 5.7.14 . mac mySql 报错ERROR 2002 (HY000): Can't connect to local MySQL serv ...
- .NET3.5中的高性能 Socket API
转载:http://www.cnblogs.com/TianFang/archive/2007/11/09/954730.html 在 .NET Framework 2.0 版本中,System.Ne ...
- UNIX网络编程——SOCKET API和TCP STATE的对应关系_三次握手_四次挥手及TCP延迟确认
在socket系统调用中,如何完成三次握手和四次挥手: SOCK_DGRAM即UDP中的connect操作知识在内核中注册对方机器的IP和PORT信息,并没有建立连接的过程,即没有发包,close也不 ...
- ERROR 2002 (HY000): Can't connect to local MySQL server through socket
在安装好了MySQL之后,使用了新的配置文件后.MySQL服务器能够成功启动,但在登陆的时候出现了ERROR 2002 (HY000): Can't connect to local MySQL se ...
- Mac - Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
在终端输入mysql,结果出现 macdeMacBook-Pro:~ mac$ alias mysql=/usr/local/mysql/bin/mysql macdeMacBook-Pro:~ ma ...
- JNI 和 socket api
1.JavaVM 和 JNIEnvJNIEnv是一个与线程相关的变量,不同线程的JNIEnv彼此独立.JavaVM是虚拟机在JNI层的代表,在一个虚拟机进程中只有一个JavaVM,因此该进程的所有线程 ...
- socket编程 ------ BSD socket API
伯克利套接字(Berkeley sockets),也称为BSD Socket.伯克利套接字的应用编程接口(API)是采用C语言的进程间通信的库,经常用在计算机网络间的通信. BSD Socket的应用 ...
随机推荐
- dom 关键字提示
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- 基于jquery的表格动态创建,自动绑定,自动获取值
最近刚加入GUT项目,学习了很多其他同事写的代码,感觉受益匪浅. 在GUT项目中,经常会碰到这样一个问题:动态生成表格,包括从数据库中读取数据,并绑定在表格中,以及从在页面上通过jQuery新增删除表 ...
- RESTful服务的版本管理经验 (转)
原文:RESTful服务的版本管理经验 最近,Howard Dierking将在设计NuGet API的下一个主要修订版(v3)时新学到的经验,与他在大约一年前的观念做了对比,并写道:使用服务器驱动的 ...
- <转>Linux环境进程间通信(二): 信号(上)
原文链接:http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html 原文如下: 一.信号及信号来源 信号本质 信号是在软件层 ...
- jQuery Callback 函数
@(编程) Callback 函数在当前动画 100% 完成之后执行. jQuery 动画的问题 许多 jQuery 函数涉及动画.这些函数也许会将 speed 或 duration 作为可选参数. ...
- GPIO 配置之ODR, BSRR, BRR 详解
STM32 GPIO 配置之ODR, BSRR, BRR 详解 用stm32 的配置GPIO 来控制LED 显示状态,可用ODR,BSRR,BRR 直接来控制引脚输出状态. ODR寄存器可读可写:既能 ...
- class bool
class bool(int): """ bool(x) -> bool Returns True when the argument x is true, Fal ...
- HDU2819Swap(二分图最大匹配)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2819 题目大意很明确,交换图的某些行或者是某些列(可以都换),使得这个N*N的图对角线上全部都是1. ...
- Remove Duplicates from Sorted List @LeetCode
/** * Remove Duplicates from Sorted List * * Given a sorted linked list, delete all duplicates such ...
- 解决window8 下连接PLSQL 报ora-12154错误
操作系统版本:window8 64位企业版 数据库:oracle10g2 安装PLSQL,登录PLSQL报ORA-12154错误. 首先:所以需要下载一个32位客户端,我同时也下载了64位客户端,具体 ...