信息来源:EST
In this tutorial we will introduce the
very basics of socket coding in c. You
will learn the connect statement, how to
send and receieve data.
It's highly recommended that you know atleast
some c coding, tcp/ip and networking before
attempting this, on the other hand if you don't,
and you still wish to try go for it.
There are several ways to operate sockets with
c-code, this is just one of them. We tryed making
it as easy as possible for you to understand.
This is all client part code, we felt that
the tutorial would grow too big for a basic
one if we were to include server samples aswell,
so we will release a second part which explains
about socket server code at a later time.
Both windows and *nix examples are included
with this tutorial.
All examples are console based, meaning
you will run it all in console mode, be it
linux/unix or the cmd prompt in win.
Windows Sockets
~~~~~~~~~~~~~~~
You need to add wsock32.lib to your projects
in windows to make them work. You can usually
do that inside the compiler, in Visual C++
it's located under the menu,
Choose projects>Settings and click on the Link
tab, add wsock32.lib to the Object/Library
modules list.
If you are using another compiler you need to
find the same option and add wsock32.lib to it.
Your first socket application.
First we will explain each code segment, and
later we will put together all the segments to
create a complete socket application.
CODE:
SOCKADDR_IN remotehost;
SOCKET war;
remotehost will hold hostname/port, and so on.
Our socket is called 'war'
CODE:
int main(int argc, char **argv)
This is the main function in the program.
argc determines how many parameters the program
takes, argv is for host/port for example:
program.exe <hostname> <port>
Where program.exe is 0, hostname 1, port 2.
Which makes '3' in total.
The next segment is where we check the parameters given
so that it has the data it needs. For example
to be able to connect, we need ip/port.
CODE:
if(argc < 3){
printf("Usage: hostname, port\n");
exit(1);
}
If we don't have 3 parameters like (program, host, port)
Then give an error message, and exit the program.
Otherwise we continue.
After this we get to the part where windows needs something
that unix/linux does not. WSAData.
This is the WSAdata init, and it's needed to be able to
operate the socket.
Init WSAData>
CODE:
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
printf("WSA-init failed...\n");
exit(1);
}
Now we need to get the hostname so that we know where to connect
with our program. First we check if parameter 1 was really a
valid hostname, otherwise host would be equal to NULL, which means
we print an error message and terminate the program, because
the connect would not be possible without a valid or working
hostname.
argv[1] Is the same thing as (program.exe >argv[1]<)
Meaning it's the first parameter after program.exe, so
by starting the program with (program.exe warindustries.com)
it will use (warindustries.com) as hostname.
CODE:
struct hostent *host;
//get the hostname from argv[1]
if ((host=gethostbyname(argv[1])) == NULL) {
printf("Could not find host\n");
exit(1);
}
The last step to make this work is to setup the hostname/port
properly, so that we can use it with our sockety sock :)
The next part is the family, we use AF_INET.
Finally we grab the port number from argv2.
program.exe (argv1) (argv2)
program.exe (host ) (port )
CODE:
remotehost.sin_addr = *((struct in_addr *)host->h_addr);
remotehost.sin_family = AF_INET;
remotehost.sin_port = htons(atoi(argv[2]));
Now we create our socket, it's called war.
SOCK_STREAM is what we will be using, there
are other types aswell such as, sock_dgram, sock_raw.
CODE:
war = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
We now have everything setup as it should be, and we are
go for connect.
The connect segment, with error checking ofcourse. If an error
occured the program will terminate with the printf error message.
Otherwise we are connected, and can move on.
CODE:
if(connect(war, (struct sockaddr *)&remotehost, sizeof(struct sockaddr))==SOCKET_ERROR){
printf("Can't establish connection\n");
exit(1);
}
CODE:
printf("connected!\n");
(Everything went okay, end of program).
All segments above are in the code below,
which makes a full console application
that connects to the host you specify, on
the given port.
Example> C:>csocket.exe
www.warindustries.com 80
:)
--------------------win_socket.c--------------------
#include <winsock.h>
#include <windows.h>
#include <stdio.h>
SOCKADDR_IN remotehost;
SOCKET war;
int main(int argc, char **argv)
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
printf("WSA-init failed...\n");
exit(1);
}
if(argc < 3){
printf("Usage: hostname, port\n");
exit(1);
}
struct hostent *host;
if ((host=gethostbyname(argv[1])) == NULL) { // host info
printf("Could not find host, or lookup failed\n");
exit(1);
}
remotehost.sin_addr = *((struct in_addr *)host->h_addr);
remotehost.sin_family = AF_INET;
remotehost.sin_port = htons(atoi(argv[2]));
war = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(connect(war, (struct sockaddr *)&remotehost, sizeof(struct sockaddr))==SOCKET_ERROR){
printf("Can't establish connection\n");
exit(1);
}
printf("connected!\n");
return 0;
}
--------------------EOF win_socket.c--------------------
Linux Sockets
~~~~~~~~~~~~~
As always, linux is better :)
Actually sockets in unix/linux is alot easier compared to
the confusing windows part. Unix/Linux does not require wsa
data, and makes a lot more sense, logically than windows.
As you can see, looking at the code there isn't a significant
difference, only some small things.
For one, we make a variable for the socket,
int war;
The other part beeing:
struct sockaddr_in remotehost;
if(connect(war, (struct sockaddr *)&remotehost,sizeof(struct sockaddr)) == -1) {
Whereas in windows we are using:
SOCKADDR_IN remotehost;
if(connect(war, (struct sockaddr *)&remotehost, sizeof(struct sockaddr))==SOCKET_ERROR){
The rest of the stuff pretty much speaks for itself
we covered the remaining code in the first part of
this document.
--------------------linuxsocket.c--------------------
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>
int main(int argc, char **argv)
{
int war;
war = socket(AF_INET, SOCK_STREAM, 0);
if(argc != 3) {
printf("Usage: hostname, port\n");
exit(0);
}
struct sockaddr_in remotehost;
struct hostent *host;
if ((host=gethostbyname(argv[1])) == NULL) {
printf("Hostname lookup failure\n");
exit(1);
}
remotehost.sin_addr = *(struct in_addr*)host->h_addr;
remotehost.sin_family = AF_INET;
remotehost.sin_port = htons(atoi(argv[2]));
if(connect(war, (struct sockaddr *)&remotehost,sizeof(struct sockaddr)) == -1) {
printf("Could not connect\n");
exit(1);
}
printf("Connected!\n");
return 0;
}
-------------------- EOF linuxsocket.c--------------------
Sending data
~~~~~~~~~~~~
Right we are connected and all, but we want to send data aswell
right? Ofcourse! :-) , let's see what we can do.
CODE:
send(war, rdata, strlen(rdata), 0);
Okay, but wait a second, we need something to send. In fact "test" needs
to be a variable containing something.
CODE:
char *rdata="this is the data";
send(war, rdata, strlen(rdata), 0);
That makes more sense.
We could also keep track of the number of bytes we are sending:
CODE:
int numbytes;
char *rdata="this is the data";
numbytes=send(war, rdata, strlen(rdata), 0);
printf("Data Sent\n");
printf("Length: %d\n", numbytes);
Receiving data
~~~~~~~~~~~~~~
When receiving data we need somewhere to put it, we need a way to display
the data or atleast analyze what we have received.
CODE:
int incdata;
char socketdata[SIZE];
if ((incdata=recv(war, socketdata, SIZE-1, 0)) == -1) {
printf("[-] Error while receiving data");
exit(1);
}
socketdata[incdata] = '\0';
printf("%s\n", socketdata);
Thats alot in one chunk, ok lets look at what the above code is.
We have the char socketdata, where we store the incoming data.
When the socket is receiving data, we put it in our variable
socketdata, we correct the data and then print it so that we
can read it. Should there be an error, we have the error check
that will report any error.
'SIZE' is defined previously in the code, or so we assume.
CODE:
#define SIZE 1024
All this should go into a function really, that you call
when you want to receieve data. Defines are usually put
below the includes at the top of the .c source file(s).
CODE:
int fetch(int war)
{
int incdata;
char socketdata[SIZE];
if ((incdata=recv(war, socketdata, SIZE-1, 0)) == -1) {
printf("[-] Error while receiving data");
exit(1);
}
socketdata[incdata] = '\0';
printf("%s\n", socketdata);
}
Now we can easily call this function when we want to receive data,
which will most likely be after we have (sent) data.
CODE:
fetch(war);
Basically calls the function telling it to fetch incoming data
on our socket called war.
So now we put together this code so that we can send and receive a
response aswell. A http request will be used for testing.
Linux version first.
--------------------linux_test.c--------------------
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>
#define SIZE 1024
int fetch(int war)
{
int incdata;
char socketdata[SIZE];
if ((incdata=recv(war, socketdata, SIZE-1, 0)) == -1) {
printf("[-] Error while receiving data");
exit(1);
}
socketdata[incdata] = '\0';
printf("%s\n", socketdata);
}
int main(int argc, char **argv)
{
int war;
war = socket(AF_INET, SOCK_STREAM, 0);
if(argc != 3) {
printf("Usage: hostname, port\n");
exit(0);
}
struct sockaddr_in remotehost;
struct hostent *host;
if ((host=gethostbyname(argv[1])) == NULL) {
printf("Hostname lookup failure\n");
exit(1);
}
remotehost.sin_addr = *(struct in_addr*)host->h_addr;
remotehost.sin_family = AF_INET;
remotehost.sin_port = htons(atoi(argv[2]));
if(connect(war, (struct sockaddr *)&remotehost,sizeof(struct sockaddr)) == -1) {
printf("Could not connect\n");
exit(1);
}
printf("Connected!\n");
char *rdata="GET /html/nopic.html\r\n";
send(war, rdata, strlen(rdata), 0);
printf("Data Sent\n");
fetch(war);
return 0;
}
--------------------EOF linux_test.c---------------------
This below is the windows version of the above code.
Which includes sending data, and receiving. The example
contains a http get request, it asks a webserver for a file
and prints the returned data.
Example:
C:>csocket warindustries.com 80
--------------------win_test.c---------------------------
#include <winsock.h>
#include <windows.h>
#include <stdio.h>
#define SIZE 1024
SOCKADDR_IN remotehost;
SOCKET war;
int fetch(int war)
{
int incdata;
char socketdata[SIZE];
if ((incdata=recv(war, socketdata, SIZE-1, 0)) == -1) {
printf("[-] Error while receiving data");
exit(1);
}
socketdata[incdata] = '\0';
printf("%s\n", socketdata);
return 0;
}
int main(int argc, char **argv)
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
printf("WSA-init failed...\n");
exit(1);
}
if(argc < 3){
printf("Usage: hostname, port\n");
exit(1);
}
struct hostent *host;
if ((host=gethostbyname(argv[1])) == NULL) { // host info
printf("Could not find host, or lookup failed\n");
exit(1);
}
remotehost.sin_addr = *((struct in_addr *)host->h_addr);
remotehost.sin_family = AF_INET;
remotehost.sin_port = htons(atoi(argv[2]));
war = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(connect(war, (struct sockaddr *)&remotehost, sizeof(struct sockaddr))==SOCKET_ERROR){
printf("Can't establish connection\n");
exit(1);
}
printf("connected!\n");
char *rdata="GET /html/nopage.html \r\n";
send(war, rdata, strlen(rdata), 0);
printf("Data sent...\n");
fetch(war);
return 0;
}
--------------------EOF win_test.c------------------------
Final words
~~~~~~~~~~~
Well, that's it. Go play with your new knowledge. We will
release a tutorial on server side socket coding in C aswell,
in the future or whenever we can find the time, asap :)
As always flames goes to
/dev/arse
Questions/Feedback:
http://warindustries.com/phpBB2/
Enjoy.
Team WarIndustries